2013-11-23 42 views
0

下面的代码将注册一个的onerror功能用于在图像元件的JavaScript img.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更改为占位符图像。

您能否告诉我为什么会发生这种情况,并提出一种解决方法?

+0

您正在使用'i',但它不是一个闭包变量,因此它将包含循环中的最后一个值。 – Barmar

+0

我已经改变它传递对象'imgElements [i]',它仍然导致相同的功能。 –

+0

其实,这不相关。你没有在回调函数中使用'i'。我不明白你为什么把所有这些IIFE放在首位,他们只是让代码更加混乱。 – Barmar

回答

2

您无法控制事情的时机,以这种方式可靠地按照您尝试这样做的方式进行工作。这里的秩序的事情发生了:

  1. 浏览器获取的HTML页面
  2. 浏览器开始解析HTML
  3. 它找到一个<img>标签,它触发关闭加载src URL的请求。
  4. 浏览器发现src URL无法加载,从而触发onerror处理程序。
  5. 您的JavaScript运行并安装onerror处理程序。

现在,步骤4和步骤5的顺序完全与时间有关并且不受您的控制。有些情况可能会导致它按照这种顺序发生,而4和5的某些情况则会发生逆转。并且请记住,一旦您的页面被加载一次,其中的一部分可能会被缓存,并且后续加载中的时序可能会有所不同。某些浏览器甚至可能缓存URL当前无法访问的事实,并且如果它立即再次请求(从而生成立即的错误响应),则可能会避免再次尝试它。

此外,当.src属性设置为新值时,某些浏览器的行为完全不正确。几年前,我知道某些浏览器在更改.src属性并加载新映像时并未可靠地触发onload事件。对于onerror处理程序也是如此。我的经验是,他们可以可靠地为第一次图像加载工作,但我不确定他们在设置不同的.src属性时可靠地工作。

要设置一个不会错过的错误处理程序,必须在.src属性被设置或解析之前使用它。这有两种方法可以做到这一点。

  1. 将使用onerror处理程序的HTML本身,以便它是<img>标签的一部分。
  2. 在您的HTML中没有完全形成<img>标签(例如,src或不在HTML中),然后用你的javascript,你可以添加onerror处理程序,然后创建图像或适当地设置.src

关键是onerror处理程序必须在以任何方式设置.src属性之前安装。

解决方案#1是这样的:实现的解决方案#2

<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'; 
} 

一种方法是这样的:

<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; 
     } 
    } 
})(); 

你也可以没有<img>标签在HTML不惜一切创造所有这些动态通过JavaScript动态,确保在设置.src属性之前安装了onerror处理程序,但这往往会使设计过程更复杂一些。对于选项#2,上述解决方案通常优先于完全以代码形式创建图像对象。

+0

修改后的代码更加健壮。 – jfriend00