记录一次IE8的填坑之旅

StupidIE

业务需求分析

简单来说,需在前端实现搜索图片的交互。流程来看,无非先本地选中图片,浏览器端用户可以对图片进行相应的裁剪,将裁剪后的参数和图片本身异步上传到服务器,后台分析图片信息,裁剪图片上传,确认上传完毕后,再让前端以GET的形式跳转到搜图结果页面,完成搜索。本文只讨论前端实现。

我的做法 (环境是jQuery)

用户点击搜图按钮的时候,加载一个全新的Form表单,并且将其隐藏

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

在同一个回调方法体内,触发input file 的点击

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

此时用户浏览器将弹出一个资源窗口,选中具体的图片,点击确定后将触发绑定在$('#inputname')的change事件,事件回调中异步提交表单

  $('#my_form').ajaxSubmit();//伪代码,jQuery原生不支持

关于异步提交带文件的二进制表单,用jQuery原生并不能很好的实现,因此借助了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');
        });

剩下回调后的处理我就不多说,也并非本文的重点。

遇到的问题

既然是IE8填坑之旅,还是应该着重说说坑。理论上来讲,支持ES5规范的浏览器,在上述流程中都不会出现问题。然而IE8依旧有庞大的市场份额...

IE8的安全机制

问题描述

由于表单是通过change事件回调函数里提交的,而触发change事件本身并非直接点击input file按钮并在资源窗口选择文件后触发的,而是间接通过点击其它DOM结构的回调中触发了input file 的点击事件。那么问题来了,IE8可能会把这个当做是非用户触发的事件,不允许进行任何表单提交操作。题外话,这个与window.open(url,'_blank')的限制机制异曲同工。

解决思路

因为之前没有任何处理IE8的经验,解决过程比较坎坷。
最简单的思路是,既然IE8对此有安全限制,那么是否有办法让用户直接点击input file,直接触发change事件回调,提交表单?
流程走下来固然可行,可是也有问题,我们都知道要想把input file控件的样式改成自己想要的样式,而且无法借助css3的前提下,是非常麻烦的,那么索性,同时也是借鉴淘宝的做法,把整个input file控件做成透明的,把自己预先定义好的按钮,或是字体,或是图片,统统放到透明控件下面,这样用户看到的是你定义的按钮或是icon,实际上点击的则是input file控件本身,没有半点违和感。
当然这么处理仍然不完美,我们都知道input file在IE8默认样式分两个部分,一部分是输入框,一部分是带有"浏览"字样的按钮,点击浏览按钮可以弹出资源窗口,而输入框则需要双击才能弹出资源窗口,那么如果你自定义的按钮或者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网友都推荐了一个轮子jquery.form.js,参考了jquery.form.js的源码以及其他声称自己能够完美兼容IE8的异步提交带文件的二进制表单的插件,我发现如果要在不支持FormData属性的浏览器环境中做到异步提交,大家的做法都很统一。
思路大概是表单提交的时候,预先在用户看不到的地方生成一个空白iframe,并将form的target属性设置为该空白iframe的name
这样一来,表单提交后本页就不会跳转或刷新,返回的XML、JSON、PLAIN TEXT或是HTML的内容都会在空白iframe里加载出来,只需要开发者预先监听该iframe的load事件,并把iframe中加载出来的内容转换成所需要的数据格式进行判断就可以完成异步提交了。

    //不是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规范不一致,因此就决定了每个项目或多或少都要写一些必要的PolyFills,同时也是为了获取更方便的开发体验。
项目在用的PolyFills(Object.bind函数和Array.filter函数)

/**
 * 针对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;
            };
        }
    }
}

小结

如果没有耐心,兼容问题根本处理不来。上世纪末到现在,我们经历了浏览器阵营的各种混战,实际上随着ES标准不断更迭,和各厂商的推进努力,浏览器兼容的问题相比以前容易处理很多。加之有许多优秀的CSS resets以及浏览器特性检测框架(如Modernizr),更抚慰了无数前端的小心脏。

参考资料

FormData API
jQuery.form.js官网
Github上不错的PolyFills
modernizr官网

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,723评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,485评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,998评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,323评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,355评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,079评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,389评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,019评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,519评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,971评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,100评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,738评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,293评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,289评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,517评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,547评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,834评论 2 345

推荐阅读更多精彩内容