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

Rya*_*yan 1 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';
                    }


        })();
    }

})();
Run Code Online (Sandbox Code Playgroud)

该代码仅有时有效。(我使用的是铬);如果我按住 F5 或非常快地刷新页面,似乎 onerror 函数不会被执行。

例如:如果我加载页面,然后等待几秒钟,然后再次刷新,src 将发生变化,但并非总是如此。

我相信这是浏览器的某种缓存问题?

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

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

您能否让我深入了解为什么会发生这种情况,并提出一种规避这种情况的方法?

jfr*_*d00 5

你无法控制事情的时间安排,让这项工作以你想要的方式可靠地进行。这是事情发生的顺序:

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

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

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

为了拥有一个不会被错过的 onerror 处理程序,您必须在.src设置或解析属性之前将其放置到位。这里有两种方法可以做到这一点。

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

关键是必须在以任何方式设置属性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';
}
Run Code Online (Sandbox Code Playgroud)

实现解决方案 #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;
        }
    }
})();
Run Code Online (Sandbox Code Playgroud)

您也可以在 HTML 中完全没有<img>标签,并通过 javascript 动态创建所有标签,确保在设置属性之前安装 onerror 处理程序.src,但这往往会使设计过程变得更加复杂。上述选项#2 的解决方案通常优于完全用代码创建图像对象。