【问题标题】:JavaScript img.onerror changing source - race conditionJavaScript img.onerror 更改源 - 竞争条件
【发布时间】:2013-11-23 05:46:04
【问题描述】:

以下代码将为图像元素注册一个 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 更改为我的占位符图像。

您能否告诉我为什么会发生这种情况,并建议一种方法来规避这种情况?

【问题讨论】:

  • 您使用的是i,但它不是闭包变量,因此它将包含循环中的最后一个值。
  • 我已将其更改为传入对象imgElements[i],它仍然会产生相同的功能。
  • 其实这无关紧要。您没有在回调函数中使用i。我不明白为什么你首先拥有所有这些 IIFE,它们只是让代码更加混乱。
  • 我删除了第二个 IIFE
  • 您唯一需要的 IIFE 就是外部的 IIFE,这只是为了防止 imgElements 污染全局命名空间。

标签: javascript image onerror


【解决方案1】:

您无法控制事情的时间安排以使这项工作以您尝试的方式可靠地工作。以下是事情发生的顺序:

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

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

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

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

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

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

解决方案 #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 中根本没有 &lt;img&gt; 标记并通过 javascript 动态创建所有标记,确保在设置 .src 属性之前安装 onerror 处理程序,但这往往会使设计过程稍微复杂一些。选项#2 的上述解决方案通常优于完全在代码中创建图像对象。

【讨论】:

    猜你喜欢
    • 2010-11-27
    • 2010-09-25
    • 1970-01-01
    • 2023-03-23
    • 1970-01-01
    • 2020-07-12
    • 2012-01-26
    • 2022-01-23
    • 2018-10-08
    相关资源
    最近更新 更多