未分类

    利用onerror/onload写的jsonp在IE下不兼容的问题

    某一天,我用 js 写了一段 jsonp 的代码。以前在学校也这么写,因为在学校没有测试IE所有的版本兼容性,一直觉得这段代码木有问题。

    代码是这样子的:

    简单来说就是将一个script标签添加进入dom,这样就可以伪造一次请求,因为同源策略可以用script/image之类标签回避掉。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    Util.prototype.loadScript = function (url, params, cb) {
    var path = url + '?' + this.serializeParam(params);
    var head = document.getElementsByTagName('head')[0];
    var script = document.createElement('script');
    var format = params.format.substr(6), result;
    window[format] = function (_res) {
    result = _res;
    };
    var handler = function () {
    if(!script) return;
    head.removeChild(script);
    script = null;
    window[format] = undefined;
    typeof cb == 'function' && cb(result);
    };
    script.src = path;
    script.async = true;
    script.charset = 'utf-8';
    script.type = 'text/javascript';
    script.onload = handler;
    script.onerror = handler;
    head.insertBefore(script, head.firstChild);
    };

    直到测试同事那天在测试的时候,发现IE7,IE6的时候,请求可以发送,但是 callback 函数没有执行。导致了一个按钮好像像点不动一样。

    这种 bug 算得上是严重的 bug 了,经过排查,发现确实是代码不完善,没有考虑到:

    在多数浏览器(包括Firefox和Chrome)下会触发onload和onerror, 但是在IE下只会触发 onreadystatechange,也就是说在IE8及IE8以下,onerror和onload都不能够使用。

    所以要调用回调函数一定需要再写一个针对 onreadystatechange 时的操作。错误中成长。

    1
    2
    3
    4
    5
    6
    script.onreadystatechange = function() {
    var r = script.readyState;
    if (r === 'loaded' || r === 'complete') {
    handler();
    }
    };

    淘宝率先不支持IE8及以下,真的是一件美好的事情

    完整的代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    Util.prototype.loadScript = function (url, params, cb) {
    var path = url + '?' + this.serializeParam(params);
    var head = document.getElementsByTagName('head')[0];
    var script = document.createElement('script');
    var format = params.format.substr(6), result;
    window[format] = function (_res) {
    result = _res;
    };
    var handler = function () {
    if(!script) return;
    head.removeChild(script);
    script = null;
    window[format] = undefined;
    typeof cb == 'function' && cb(result);
    };
    script.src = path;
    script.async = true;
    script.charset = 'utf-8';
    script.type = 'text/javascript';
    script.onreadystatechange = function() {
    var r = script.readyState;
    if (r === 'loaded' || r === 'complete') {
    handler();
    }
    };
    script.onload = handler;
    script.onerror = handler;
    head.insertBefore(script, head.firstChild);
    };