1、简介
现需在一个html页面中显示一张图片,这张图片的地址是通过GET方式从地址栏中传入的。但是如果图片损坏或传参错误,在部分浏览器下会出现破裂图,所以当遇到这种情况则将图片隐藏掉。
地址示例:http://example.com/test.html?s=1.jpg
据此要求编写的初始代码如下(代码中的两个console用于后续的测试):
HTML
<img src="" id="image" onerror="this.onerror='';console.log('none');this.style.display='none'">
JS
var imgSrc=GetUrlParams().s||""; // GetUrlParams是用来获取url中的参数的,参数名为s
var img=document.getElementById("image");
if (imgSrc) {
img.src=imgSrc+"?t="+Math.random();
console.log('block');
img.style.display='block';
}
2、问题
当用上述代码在各浏览器中进行测试时,发现在IE下图片无法显示,但从开发者工具中可以看到图片是正确请求了。其它浏览器(Chrome、Opera、FireFox、Safari)表现符合预期。
之后我对IE做了一些测试,测试内容如下:
1)当不传参数时,img的error事件会被触发,控制台输出none,这一点跟其它浏览器一致;
2)当传参时,其它浏览器均不触发error事件,只有IE会触发;
按照浏览器的加载原则,图片会在其它所有代码之后加载,所以页面先获取到了图片地址参数并更新到了img标签上,然后才去加载图片。也就是说,当加载图片时,图片的地址已经是最终的那个传参了,理论上是不应该触发error事件的。
IE这个行为比较奇怪。经过一番琢磨后,我猜想IE是不是在自上而下读到img标签处时,虽然没立即加载图片,但是却把图片的地址添加到了后续的计划任务中,就好像打印机那样,然后在其它代码加载完之后陆续请求了这些地址(就像是请求了一个地址数组[“”,”1.jpg”])。为了验证这一猜想,我做了如下的一番测试:
① 编写了一个新的页面,功能是在初始化时,通过循环多次修改img的src属性,然后观察IE是否会对所有赋值都发起请求。
HTML
<img id="image" onload="this.style.display='block'">
JS
var img=document.getElementById("image");
var imgArr=["1.jpg","favicon.png","loading.gif"];
for (var i = 0; i < imgArr.length; i++) {img.src=imgArr[i]+"?t="+Math.random();}
测试后发现,IE的确请求了数组中所有的地址,但在变更img的src的过程中,被废弃的那些图片地址都显示挂起,应该是被中断掉了。
② 继续观察该测试页面在其它浏览器下的反应,发现Chrome、FireFox、Opera均只请求了最后那张图片的地址。
但是Safari的表现有点意外了,它像IE一样请求了所有的地址,不过在开发者工具界面上的显示与IE略有不同。
3)根据上面的测试,我将img标签的src属性从html中去除掉,代码成了:
<img id="image" onerror="this.onerror='';console.log('none');this.style.display='none'">
然后再进行测试,发现IE不再触发img的error事件,可以解决图片不显示的问题。
之后,我又测试了将src赋值为“javascript:;”,发现也不会在IE下触发img的error事件,其它部分浏览器(如Safari)虽然控制台可能会报错,但并不影响页面功能。
4)虽然本次实验是因为onerror而起,但就功能而言,其实用onload事件会更加容易实现和理解(各个浏览器表现一致)。只是最初入门前端时见到的是onerror这样的错误处理方法,往后就优先想到它,而没再往其它方向考虑。
使用onload的代码为:
// 初始将图片隐藏
img{display:none;}
// 此处有无src均可
<img id="image" onload="this.style.display='block'">
附:最早见过的印象深刻的onerror用法
<img src=”main.png” onerror=” this.onerror=''; this.src=’default.png’”>
3、结论
解决问题的办法有两个:
1)将img标签的src属性不写或者写成正确但无意义的值(如src="javascript:;")。
<img id="image" onerror="this.onerror=''; this.style.display='none'">
<img src="javascript:;" id="image" onerror="this.onerror=''; this.style.display='none'">
2)使用onload代替onerror事件实现上文所述的功能。
<img id="image" onload="this.style.display='block'">