JavaScript img.onerror更改源 - 竞争条件

时间:2013-11-23 05:46:05

标签: javascript image onerror

以下代码将为图像元素注册onerror函数

(function() {

    var imgElements = document.getElementsByTagName('img');
    for(i = 0; i < imgElements.length; i++) {   
        (function() {

                    imgElements[i].onerror = function() {
                        this.src = base_url() + 'assets/images/placeholder.jpg';
                    }


        })();
    }

})();

此代码有时仅适用。 (我正在使用铬);如果我按住F5或快速刷新页面,似乎onerror函数没有被执行。

例如:如果我加载页面,然后等待几秒钟,然后再次刷新src将会改变,但不是所有时间。

我认为这是浏览器的某种缓存问题?

更具体地说,如果我按下chrome上的刷新图标,即使突然刷新,一切都会正常工作。

但是,如果我突出显示URL并按回车键,代码最终不会将src更改为我的占位符图像。

你能否告诉我为何会发生这种情况,并提出一种规避这种方法的方法?

1 个答案:

答案 0 :(得分:3)

你无法控制事情的时间安排,使你以这种方式可靠地按照你尝试的方式工作。事情的顺序是这样的:

  1. 浏览器提取页面的HTML
  2. 浏览器开始解析HTML
  3. 当它找到<img>标记时,会触发加载src网址的请求。
  4. 浏览器发现无法加载src网址,从而触发onerror处理程序。
  5. 您的javascript运行并安装onerror处理程序。
  6. 现在,步骤4和5的顺序纯粹与时间相关,并且不在您的控制之下。某些条件可能会导致它按此顺序发生,而某些条件则反转4和5。并且,请记住,一旦您的页面加载一次,其中的一部分可能会被缓存,并且后续加载中的时序可能会有所不同。有些浏览器甚至可以缓存URL当前无法访问的事实,如果它立即再次请求(因此会立即生成响应),可能会避免再次尝试它。

    此外,当.src属性设置为新值时,某些浏览器的行为并不完全正常。从几年前开始,我知道有些浏览器在更改onload属性并加载新图像时无法可靠地触发.src事件。 onerror处理程序也是如此。我的经验是,它们可以在首次加载图像时可靠地工作,但我不确定它们在设置不同的.src属性时是否可靠。

    要拥有一个不会错过的onerror处理程序,必须在设置或解析.src属性之前将其置于适当的位置。这有两种方法可以做到。

    1. 将onerror处理程序放在HTML本身中,使其成为<img>标记的一部分。
    2. HTML中没有完全形成的<img>标签(例如,根本没有.src或HTML中没有),然后使用您的javascript,您可以添加onerror处理程序,然后创建图像或设置适当地.src
    3. 关键是必须在以任何方式设置onerror属性之前安装.src处理程序。

      解决方案#1看起来像这样:

      <img src="xxx.jpg" onerror="setErrorImage(this)">
      
      // this function must be in the global scope
      function setErrorImage(img) {
          // clear error handler so no chance of looping
          img.onerror = function() {};
          img.src = base_url() + 'assets/images/placeholder.jpg';
      }
      

      实施解决方案#2的一种方法如下:

      <img data-src="xxx.jpg">
      
      (function() {
          function setErrorImage() {
              this.removeEventListener("error", setErrorImage);
              this.src = base_url() + 'assets/images/placeholder.jpg';
          }
      
          var imgElements = document.getElementsByTagName('img');
          var img, url;
          for (var i = 0; i < imgElements.length; i++) {   
              img = imgElements[i];
              // now that onerror handler is in place, set the .src property
              url = img.getAttribute("data-src")
              if (!img.src && url) {
                  img.addEventListener("error", setErrorImage);
                  img.src = url;
              }
          }
      })();
      

      你可能根本没有HTML中的<img>标签,并通过javascript动态创建所有标签,确保在.src属性设置之前安装了onerror处理程序,但是使设计过程更复杂一些。选项#2的上述解决方案通常优于完全在代码中创建图像对象。