IE 中图片大小的误判

IE8 以下的浏览器都会出现这个问题,不致命,但非常影响页面观感。幸运的是,你不一定能遇上。

情景是,图片需要动态加载,根据给出的缩放比例调整显示大小。图片是普通 PNG,只是存放点是多样化的,同样的图片,一个以文件形式存在,另一个是保存于缓存(内存)中,前面都是用 Apache,一个读文件返回,一个做转发,用 IE 访问它们,获取其大小信息,结果竟然是不一样的。

装载图片的代码片段:

function loadImg(url, callback) {
    var img = new Image();
    img.src = url;
    
    if (img.complete) {
        console.log("complete");
        return callback.call(img, url);
    }
    img.onload = function() {
        img.onload = null;
        callback.call(img, url);
    }
    return img;
}

要完成缩放,需要获取图片的原始大小。当然也可以用百分比数值,方案比较不是本文重点。下面写一个函数可以打印出图像对象上所有的数值属性,当然包括宽和高。

function getParam() {
    var arr = [];
    for(var i in this) {
        try {
            if (typeof this[i] !== 'number') continue;
            arr.push(i + ' : ' + this[i] + '\n');
        } catch(e) {} // this[i] 在 IE 下有可能会报错,不影响观察,暂且忽略
    }
    alert(arr.join('')); // 使用 alert 方便观看结果
};

然后都执行下面的,其中 testPngURL 分别为两者的 URL,这也是唯一的区别了。

preload(testPngURL, getParam);

两次执行的结果显示的 width 和 height 是不一样的,而且不成比例,而且结果是可以稳定重复的。我最纳闷的是 IE 凭什么得出另外那个错误结果的。我初时觉得可能是两次请求的 header 不一样,主要差异是存放于内存的,其响应的 Content-Type 是 application/octet-stream,文件形式的则是 image/png,内容长度是完全一样的。于是我使用 Fiddler 对内存图像的请求进行拦截,并修改其 Content-Type,结果是无效的。甚至将两者的响应头弄成完全一致,也得不到一致的结果。

暂时还没想到有什么方法可以绕过。而且最奇怪的是,有少数几个 png 的大小是正确的。难道问题出在 png 的数据上,有些隐藏的信息,但触发点到底是什么,可是敲爆头也不知道了。

虽然生产不会这么使用,但为了开发调试方便,我们会使用缓存来存放各种资源,其返回头都是非常简单而且统一的,无任何多余 header,浏览器不会自作主张做缓存。更新也方便,往同一个地址 PUT 内容即可。现在由于 IE 存在这样一个问题,其显示大小是不正确的,非常影响观感,不得不需要做些调整了。

IE 中对 toString 的双重标准

使用 for .. in 遍历对象,似乎没什么好说的,不过还是有例外。在各个浏览器里面跑下面的代码:

var obj = {
    toString: function() {
        return "custom.toString()";
    }
}
for(var i in obj) {
    alert(i);
}

它们都会 alert 一个 toString,除了 IE。一开始还以为是加载慢,后来才认识到,其实 IE 并不把它当成是一个自定义的对象方法。同样的情况也发生在 valueOf() 方法上,可以猜想,IE 可能把它当成是 native 的了。
Quick Fix – Internet Explorer and toString problems 里面,介绍了使用条件注释(conditional comment)的方法来解决。 Continue reading