【问题标题】:Detect when an image fails to load in JavaScript检测图像何时无法在 JavaScript 中加载
【发布时间】:2012-04-06 15:06:34
【问题描述】:

有没有办法确定图片路径是否指向实际图片,即检测图片何时无法在 JavaScript 中加载。

对于一个 Web 应用程序,我正在解析一个 xml 文件并从图像路径列表中动态创建 HTML 图像。服务器上可能不再存在某些图像路径,因此我想通过检测哪些图像无法加载并删除该 HTML img 元素来优雅地失败。

注意 jQuery 解决方案将无法使用(老板不想使用 jQuery,是的,我知道不要让我开始)。我知道 jQuery 中有一种方法可以检测图像何时加载,但不知道它是否失败。

我的代码用于创建 img 元素,但我如何检测 img 路径是否导致图片加载失败?

var imgObj = new Image();  // document.createElement("img");
imgObj.src = src;

【问题讨论】:

标签: javascript image


【解决方案1】:

这是@emil.c 答案的变体,但根据图像加载与否以真/假解决(而不是在失败时抛出错误):

function testImage(url) {
  if (!url || typeof url !== 'string') return Promise.resolve(false);
  return new Promise((resolve) => {
    const imgElement = new Image();
    imgElement.addEventListener('load', () => resolve(true));
    imgElement.addEventListener('error', () => resolve(false));
    imgElement.src = url;
  });
}

【讨论】:

    【解决方案2】:

    用于 img 的 jQuery + CSS

    使用 jQuery 这对我有用:

    $('img').error(function() {
        $(this).attr('src', '/no-img.png').addClass('no-img');
    });
    

    我可以在我的网站上随处使用这张图片,无论它的大小如何,都具有以下 CSS3 属性:

    img.no-img {
        object-fit: cover;
        object-position: 50% 50%;
    }
    

    提示 1:使用至少 800 x 800 像素的方形图片

    提示 2:用于人物肖像,请使用object-position: 20% 50%;

    CSS 仅适用于 background-img

    对于缺少的背景图片,我还在每个background-image 声明中添加了以下内容:

    background-image: url('path-to-image.png'), url('no-img.png');
    

    注意:不适用于透明图像。

    Apache 服务器端

    另一种解决方案是在发送到浏览器之前使用 Apache 检测丢失的图像并将其替换为默认的 no-img.png 内容。

    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_URI} /images/.*\.(gif|jpg|jpeg|png)$
    RewriteRule .* /images/no-img.png [L,R=307]
    

    【讨论】:

      【解决方案3】:

      这个:

      <img onerror="this.src='/images/image.png'" src="...">
      

      【讨论】:

      • 虽然这是一个有用的属性,可以用作图像故障转移技术,但更详细的响应将有助于读者理解它。
      • 这么短,这么有用!为了隐藏它:&lt;img src="your/path/that/might/fail.png" onerror="$(this).hide();"/&gt;
      • @J0ANMM 另一种隐藏图像的方法是添加一个空的 alt 属性:
      • 太棒了!我没有故障转移到图像,而是打印此文本 onerror="this.alt='Not Valid Image, Use Link ^'"
      • 注意:如果这个“image.png”不退出,会导致无限错误循环。你可以这样做来避免它:&lt;img onerror="this.onerror=null;this.src='/images/image.png'" src="..."&gt;
      【解决方案4】:
      /**
       * Tests image load.
       * @param {String} url
       * @returns {Promise}
       */
      function testImageUrl(url) {
        return new Promise(function(resolve, reject) {
          var image = new Image();
          image.addEventListener('load', resolve);
          image.addEventListener('error', reject);
          image.src = url;
        });
      }
      
      return testImageUrl(imageUrl).then(function imageLoaded(e) {
        return imageUrl;
      })
      .catch(function imageFailed(e) {
        return defaultImageUrl;
      });
      

      【讨论】:

        【解决方案5】:

        答案很好,但它引入了一个问题。每当您直接分配onloadonerror 时,它可能会替换之前分配的回调。这就是为什么有一个很好的方法“在调用它的 EventTarget 上注册指定的侦听器”,正如他们在MDN 上所说的那样。您可以在同一事件上注册任意数量的侦听器。

        让我稍微重写一下答案。

        function testImage(url) {
            var tester = new Image();
            tester.addEventListener('load', imageFound);
            tester.addEventListener('error', imageNotFound);
            tester.src = url;
        }
        
        function imageFound() {
            alert('That image is found and loaded');
        }
        
        function imageNotFound() {
            alert('That image was not found.');
        }
        
        testImage("http://foo.com/bar.jpg");
        

        由于外部资源加载过程是异步的,因此使用带有 promise 的现代 JavaScript 会更好,如下所示。

        function testImage(url) {
        
            // Define the promise
            const imgPromise = new Promise(function imgPromise(resolve, reject) {
        
                // Create the image
                const imgElement = new Image();
        
                // When image is loaded, resolve the promise
                imgElement.addEventListener('load', function imgOnLoad() {
                    resolve(this);
                });
        
                // When there's an error during load, reject the promise
                imgElement.addEventListener('error', function imgOnError() {
                    reject();
                })
        
                // Assign URL
                imgElement.src = url;
        
            });
        
            return imgPromise;
        }
        
        testImage("http://foo.com/bar.jpg").then(
        
            function fulfilled(img) {
                console.log('That image is found and loaded', img);
            },
        
            function rejected() {
                console.log('That image was not found');
            }
        
        );
        

        【讨论】:

        • 也许我遗漏了一些东西,但是如果刚刚创建了“测试器”,那么分配 (tester.onload=..) 如何替换回调?即使代码的某些晦涩部分为创建的每个图像添加了一个事件,我们也不是很明显要保留这些事件以检测图像是否存在。
        • 你是绝对正确的。在这种特殊情况下,它可能不是问题,因为它很可能不会被替换。但是,一般来说,添加事件监听器比直接分配方法更好。
        • @JohnWeisz 箭头函数是匿名的,因此更难调试,谨慎使用。
        • @GifCo 我不愿意继续与您进行此类讨论。我被你的语言冒犯了。如果您认为我的回答不正确或不完整,请提出更改建议并解释您的理由。如果您认为某件事是不好的做法,请用您自己的答案提出一个好的做法。
        • 这里为什么要讨论箭头函数?这是题外话。答案很好,特别是因为旧浏览器不支持箭头功能(例如 Internet Explorer)。
        【解决方案6】:

        您可以尝试以下代码。不过我不能保证浏览器的兼容性,所以你必须测试一下。

        function testImage(URL) {
            var tester=new Image();
            tester.onload=imageFound;
            tester.onerror=imageNotFound;
            tester.src=URL;
        }
        
        function imageFound() {
            alert('That image is found and loaded');
        }
        
        function imageNotFound() {
            alert('That image was not found.');
        }
        
        testImage("http://foo.com/bar.jpg");
        

        还有我对抗 jQuery 的老板表示同情!

        【讨论】:

        • 知道是否有办法判断它是否遇到重定向?
        • 这在 webKit(Epiphany, Safari, ...) 上不起作用,具体取决于您从中获取图像的服务器。例如,有时 imgur 不发送存在的图像或 404 状态,并且在这种情况下不会触发错误事件。
        • 问题在于您的消息“未找到该图像”。响应代码可以是 500 或 403 或其他。您不知道它是 404。事实上,它不太可能是 404,因为您可能不会尝试加载不存在的图像。
        • 应该添加事件处理程序而不是直接赋值
        【解决方案7】:

        如下:

        var img = new Image(); 
        img.src = imgUrl; 
        
        if (!img.complete) {
        
        //has picture
        }
        else //not{ 
        
        }
        

        【讨论】:

        • 不。这可能会提示您在某些浏览器中是否缓存了图像 - 但如果没有加载回调,您甚至不需要等待浏览器有机会为该图像发起 HTTP 请求。
        • 这只是说说而已!
        • 图片加载是异步的。
        • @emil.c 出于某种原因,并非总是如此。 ChaseMoskal(SO 不会让我通知...)说得对——这只有在图像在 JS 执行继续之前完成加载时才有效,这似乎在缓存时发生。
        【解决方案8】:

        这是我为另一个答案编写的函数:Javascript Image Url Verify。我不知道它是否正是您所需要的,但它使用了您将使用的各种技术,包括onloadonerroronabort 的处理程序和一般超时。

        由于图像加载是异步的,因此您使用图像调用此函数,然后稍后它会调用您的回调并返回结果。

        【讨论】:

          猜你喜欢
          • 2014-03-25
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-02-21
          • 1970-01-01
          • 1970-01-01
          • 2011-02-27
          • 1970-01-01
          相关资源
          最近更新 更多