記錄一次IE8的填坑之旅

StupidIE

業(yè)務(wù)需求分析

簡單來說隧魄,需在前端實現(xiàn)搜索圖片的交互藻糖。流程來看,無非先本地選中圖片叮喳,瀏覽器端用戶可以對圖片進(jìn)行相應(yīng)的裁剪被芳,將裁剪后的參數(shù)和圖片本身異步上傳到服務(wù)器,后臺分析圖片信息嘲更,裁剪圖片上傳筐钟,確認(rèn)上傳完畢后,再讓前端以GET的形式跳轉(zhuǎn)到搜圖結(jié)果頁面赋朦,完成搜索篓冲。本文只討論前端實現(xiàn)。

我的做法 (環(huán)境是jQuery)

用戶點擊搜圖按鈕的時候宠哄,加載一個全新的Form表單壹将,并且將其隱藏

    <form id="my_form" style="display:none"
     enctype="multipart/form-data">
      <input type="file" name="inputname" class="inputname" id="inputname">
    </form>

在同一個回調(diào)方法體內(nèi),觸發(fā)input file 的點擊

  $('#inputname').click();

此時用戶瀏覽器將彈出一個資源窗口毛嫉,選中具體的圖片诽俯,點擊確定后將觸發(fā)綁定在$('#inputname')的change事件,事件回調(diào)中異步提交表單

  $('#my_form').ajaxSubmit();//偽代碼承粤,jQuery原生不支持

關(guān)于異步提交帶文件的二進(jìn)制表單暴区,用jQuery原生并不能很好的實現(xiàn),因此借助了FormData對象

        $.ajax({
            url: 'http://yourdomain/upload',
            data: new FormData($('#my_form')[0]),
            method: 'post',
            dataType: 'json',
            processData: false,
            contentType: false
        }).done(function(){
            alert('OK');
        }).fail(function () {
            alert('FAILED');
        });

剩下回調(diào)后的處理我就不多說辛臊,也并非本文的重點仙粱。

遇到的問題

既然是IE8填坑之旅,還是應(yīng)該著重說說坑彻舰。理論上來講伐割,支持ES5規(guī)范的瀏覽器,在上述流程中都不會出現(xiàn)問題刃唤。然而IE8依舊有龐大的市場份額...

IE8的安全機(jī)制

問題描述

由于表單是通過change事件回調(diào)函數(shù)里提交的隔心,而觸發(fā)change事件本身并非直接點擊input file按鈕并在資源窗口選擇文件后觸發(fā)的,而是間接通過點擊其它DOM結(jié)構(gòu)的回調(diào)中觸發(fā)了input file 的點擊事件尚胞。那么問題來了硬霍,IE8可能會把這個當(dāng)做是非用戶觸發(fā)的事件,不允許進(jìn)行任何表單提交操作辐真。題外話须尚,這個與window.open(url,'_blank')的限制機(jī)制異曲同工崖堤。

解決思路

因為之前沒有任何處理IE8的經(jīng)驗,解決過程比較坎坷耐床。
最簡單的思路是密幔,既然IE8對此有安全限制,那么是否有辦法讓用戶直接點擊input file撩轰,直接觸發(fā)change事件回調(diào)胯甩,提交表單?
流程走下來固然可行堪嫂,可是也有問題偎箫,我們都知道要想把input file控件的樣式改成自己想要的樣式,而且無法借助css3的前提下皆串,是非常麻煩的淹办,那么索性,同時也是借鑒淘寶的做法恶复,把整個input file控件做成透明的怜森,把自己預(yù)先定義好的按鈕,或是字體谤牡,或是圖片副硅,統(tǒng)統(tǒng)放到透明控件下面,這樣用戶看到的是你定義的按鈕或是icon翅萤,實際上點擊的則是input file控件本身恐疲,沒有半點違和感。
當(dāng)然這么處理仍然不完美套么,我們都知道input file在IE8默認(rèn)樣式分兩個部分培己,一部分是輸入框,一部分是帶有"瀏覽"字樣的按鈕胚泌,點擊瀏覽按鈕可以彈出資源窗口漱凝,而輸入框則需要雙擊才能彈出資源窗口,那么如果你自定義的按鈕或者icon比input file中"瀏覽"按鈕大诸迟,如何能確保"瀏覽"按鈕尺寸足夠大不出錯呢?這里有一個比較巧妙的辦法愕乎,通過font-size屬性可以把按鈕撐大阵苇,然后通過css定位一下確保完全能夠覆蓋自己定義的范圍就可以了。下面給出Demo感论。

  .inputname{
      width: 600px;
      height: 600px;
      position: absolute;
      top:0;
      right: 0px;
      z-index: 9999;
      display: block;
      cursor: pointer;
      font-size: 100px;
      -ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0);
      filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0);
  }

FormData不支持IE10以下的瀏覽器

formdata.png

解決思路

這個解決方式就比較簡單了绅项,很多overflowstack網(wǎng)友都推薦了一個輪子jquery.form.js,參考了jquery.form.js的源碼以及其他聲稱自己能夠完美兼容IE8的異步提交帶文件的二進(jìn)制表單的插件比肄,我發(fā)現(xiàn)如果要在不支持FormData屬性的瀏覽器環(huán)境中做到異步提交快耿,大家的做法都很統(tǒng)一囊陡。
思路大概是表單提交的時候,預(yù)先在用戶看不到的地方生成一個空白iframe掀亥,并將form的target屬性設(shè)置為該空白iframe的name撞反。
這樣一來,表單提交后本頁就不會跳轉(zhuǎn)或刷新搪花,返回的XML遏片、JSON、PLAIN TEXT或是HTML的內(nèi)容都會在空白iframe里加載出來撮竿,只需要開發(fā)者預(yù)先監(jiān)聽該iframe的load事件吮便,并把iframe中加載出來的內(nèi)容轉(zhuǎn)換成所需要的數(shù)據(jù)格式進(jìn)行判斷就可以完成異步提交了。

    //不是jQuery.form.js源碼
    iframeObj.bind("load", function () {
        var contents = $(this).contents().get(0);
        var data = $(contents).find('body').text();
        if ('json' == options.dataType) {
            data = window.eval('(' + data + ')');
        }
        options.onSuccess(data);
        iframeObj.remove();
        form.remove();
        iframeObj = null;
    });

    form.submit();

IE8各種PolyFills

由于各種瀏覽器ES規(guī)范不一致幢踏,因此就決定了每個項目或多或少都要寫一些必要的PolyFills髓需,同時也是為了獲取更方便的開發(fā)體驗。
項目在用的PolyFills(Object.bind函數(shù)和Array.filter函數(shù))

/**
 * 針對ES兼容性的寫法
 */
module.exports = {
   makePolyFill : function(){
        if (!Function.prototype.bind) {
            Function.prototype.bind = function (oThis) {
                if (typeof this !== 'function') {
                    // closest thing possible to the ECMAScript 5
                    // internal IsCallable function
                    throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
                }

                var aArgs = Array.prototype.slice.call(arguments, 1),
                    fToBind = this,
                    fNOP = function () {
                    },
                    fBound = function () {
                        return fToBind.apply(this instanceof fNOP && oThis
                                ? this
                                : oThis,
                            aArgs.concat(Array.prototype.slice.call(arguments)));
                    };

                fNOP.prototype = this.prototype;
                fBound.prototype = new fNOP();

                return fBound;
            };
        }
        if (!Array.prototype.filter) {
            Array.prototype.filter = function (fun /*, thisp */) {
                "use strict";

                if (this === void 0 || this === null)
                    throw new TypeError();

                var t = Object(this);
                var len = t.length >>> 0;
                if (typeof fun !== "function")
                    throw new TypeError();

                var res = [];
                var thisp = arguments[1];
                for (var i = 0; i < len; i++) {
                    if (i in t) {
                        var val = t[i]; // in case fun mutates this
                        if (fun.call(thisp, val, i, t))
                            res.push(val);
                    }
                }

                return res;
            };
        }
    }
}

小結(jié)

如果沒有耐心房蝉,兼容問題根本處理不來僚匆。上世紀(jì)末到現(xiàn)在,我們經(jīng)歷了瀏覽器陣營的各種混戰(zhàn)惨驶,實際上隨著ES標(biāo)準(zhǔn)不斷更迭白热,和各廠商的推進(jìn)努力,瀏覽器兼容的問題相比以前容易處理很多粗卜。加之有許多優(yōu)秀的CSS resets以及瀏覽器特性檢測框架(如Modernizr)屋确,更撫慰了無數(shù)前端的小心臟。

參考資料

FormData API续扔;
jQuery.form.js官網(wǎng)攻臀;
Github上不錯的PolyFills
modernizr官網(wǎng)纱昧;

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末刨啸,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子识脆,更是在濱河造成了極大的恐慌设联,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,080評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件灼捂,死亡現(xiàn)場離奇詭異离例,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)悉稠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,422評論 3 385
  • 文/潘曉璐 我一進(jìn)店門宫蛆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人的猛,你說我怎么就攤上這事耀盗∠牖ⅲ” “怎么了?”我有些...
    開封第一講書人閱讀 157,630評論 0 348
  • 文/不壞的土叔 我叫張陵叛拷,是天一觀的道長舌厨。 經(jīng)常有香客問我,道長胡诗,這世上最難降的妖魔是什么邓线? 我笑而不...
    開封第一講書人閱讀 56,554評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮煌恢,結(jié)果婚禮上骇陈,老公的妹妹穿的比我還像新娘。我一直安慰自己瑰抵,他們只是感情好你雌,可當(dāng)我...
    茶點故事閱讀 65,662評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著二汛,像睡著了一般婿崭。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上肴颊,一...
    開封第一講書人閱讀 49,856評論 1 290
  • 那天氓栈,我揣著相機(jī)與錄音,去河邊找鬼婿着。 笑死授瘦,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的竟宋。 我是一名探鬼主播提完,決...
    沈念sama閱讀 39,014評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼丘侠!你這毒婦竟也來了徒欣?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,752評論 0 268
  • 序言:老撾萬榮一對情侶失蹤蜗字,失蹤者是張志新(化名)和其女友劉穎打肝,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體挪捕,經(jīng)...
    沈念sama閱讀 44,212評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡闯睹,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,541評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了担神。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,687評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡始花,死狀恐怖妄讯,靈堂內(nèi)的尸體忽然破棺而出孩锡,到底是詐尸還是另有隱情,我是刑警寧澤亥贸,帶...
    沈念sama閱讀 34,347評論 4 331
  • 正文 年R本政府宣布躬窜,位于F島的核電站,受9級特大地震影響炕置,放射性物質(zhì)發(fā)生泄漏荣挨。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,973評論 3 315
  • 文/蒙蒙 一朴摊、第九天 我趴在偏房一處隱蔽的房頂上張望默垄。 院中可真熱鬧,春花似錦甚纲、人聲如沸口锭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,777評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽鹃操。三九已至,卻和暖如春春哨,著一層夾襖步出監(jiān)牢的瞬間荆隘,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,006評論 1 266
  • 我被黑心中介騙來泰國打工赴背, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留椰拒,地道東北人。 一個月前我還...
    沈念sama閱讀 46,406評論 2 360
  • 正文 我出身青樓癞尚,卻偏偏與公主長得像耸三,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子浇揩,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,576評論 2 349

推薦閱讀更多精彩內(nèi)容