jQuery / JavaScript替换损坏的图像


546

我有一个包含一堆图像的网页。有时,该图像不可用,因此在客户端的浏览器中会显示损坏的图像。

如何使用jQuery获取图像集,将其过滤为损坏的图像然后替换src?


-我以为使用jQuery会更容易,但是事实证明,仅使用纯JavaScript解决方案(即Prestaul提供的解决方案)要容易得多。

Answers:


760

处理onError事件以使图像使用JavaScript重新分配其源:

function imgError(image) {
    image.onerror = "";
    image.src = "/images/noimage.gif";
    return true;
}
<img src="image.png" onerror="imgError(this);"/>

或没有JavaScript函数:

<img src="image.png" onError="this.onerror=null;this.src='/images/noimage.gif';" />

以下兼容性表列出了支持错误工具的浏览器:

http://www.quirksmode.org/dom/events/error.html


132
您可能想设置src 之前清除onerror 。否则,如果也缺少noimage.gif,则可能会导致“堆栈溢出”。
pcorcoran

45
我以为并希望我们摆脱JavaScript事件的内联属性...
Redsquare

24
redsquare ...当您谨慎地控制它们时,它们是完全可以理解的,当您希望标记反映实际发生的情况时,它们甚至很有用。
妮可

2
关于标记显示正在发生的事情,尼克C的要点是,当我看着它时,我只是畏缩:P
SSH This

38
@redsquare,我完全同意,但这是一个特例。内联是唯一可以绝对确定附加事件的事件的源,它可以绝对确定事件不会在附加事件之前触发。如果在文档末尾执行此操作或等待DOM加载,则可能会丢失某些图像上的错误事件。事件触发后,附加错误处理程序对您没有好处。
Prestaul 2012年

201

我使用内置error处理程序:

$("img").error(function () {
    $(this).unbind("error").attr("src", "broken.gif");
});

编辑:error()方法在jquery 1.8及更高版本中已弃用。相反,您应该使用.on("error")改用:

$("img").on("error", function () {
    $(this).attr("src", "broken.gif");
});

112
如果使用此技术,则可以使用“一个”方法来避免需要取消绑定事件:$('img')。one('error',function(){this.src ='broken.gif';}) ;
Prestaul

7
+1但是您必须取消绑定吗?如果不取消绑定,它可以在动态图像(例如,可以在悬停时改变的图像)上很好地工作。
2011年

15
我认为,如果您不解除绑定,并且broken.gif src引用由于任何原因而无法加载,则浏览器中可能会发生不良情况。
travis

4
@travis,您将在何时拨打此电话?有什么方法可以使用此方法,并确定在错误事件触发之前已附加了处理程序?
Prestaul 2012年

5
这是一个在附加事件处理程序之前引发错误的情况的示例:jsfiddle.net/zmpGz/1我真的很好奇是否有附加故障安全的方法来附加该处理程序...
Prestaul

120

如果像我这样的人尝试将error事件附加到动态HTMLimg标签上,我想指出的是,有一个陷阱:

显然,img错误事件不会在大多数浏览器中冒泡,这与标准相反所说的。

因此,类似以下内容将不起作用

$(document).on('error', 'img', function () { ... })

希望这对其他人有帮助。我希望我已经在此线程中看到了这一点。但是,我没有。所以,我添加了它


没用。我动态加载死图像并将其添加到dom,并且不会触发“错误”事件。
vsync

59

这是一个独立的解决方案:

$(window).load(function() {
  $('img').each(function() {
    if (!this.complete || typeof this.naturalWidth == "undefined" || this.naturalWidth == 0) {
      // image was broken, replace with your new image
      this.src = 'http://www.tranism.com/weblog/images/broken_ipod.gif';
    }
  });
});

5
几乎正确... IE中的正确图像仍将返回(typeof(this.naturalWidth)==“ undefined”)== true; -我将if语句更改为(!this.complete ||(!$。browser.msie &&(typeof this.naturalWidth ==“ undefined” || this.naturalWidth == 0)))
Sugendran

3
此规则是因为它可以在页面加载后检测到损坏的图像。我需要这个。
北美,2013年

@Sugendran $.browser现在已被弃用并删除,请改用功能检测(在这种情况下会变得更加复杂)。
李·彭克曼

3
独立但需要jQuery吗?
查理

这很好用,但是当图像的src返回a时204 No Content,它将是损坏的图像。
卡森·莱因克

35

我相信这就是您所追求的:jQuery.Preload

这是演示中的示例代码,您指定了加载和未找到的图像,并且一切就绪:

$('#images img').preload({
    placeholder:'placeholder.jpg',
    notFound:'notfound.jpg'
});

1
jQuery.Preload是使用示例代码所必需的插件。
丹·洛德

17
是的...这链接在第一行的答案中。
尼克·克拉弗

谢谢尼克,这似乎是一个很好的解决方案,可以解决8年以上的Firefox未显示损坏图像的图像替换问题。另外,您可以为小事打上烙印...再次感谢。
klewis 2014年


19

在OP寻求替换SRC的同时,我敢肯定很多遇到此问题的人可能只希望隐藏破碎的图像,在这种情况下,这种简单的解决方案对我很有用。

使用内联JavaScript:

<img src="img.jpg" onerror="this.style.display='none';" />

使用外部JavaScript:

var images = document.querySelectorAll('img');

for (var i = 0; i < images.length; i++) {
  images[i].onerror = function() {
    this.style.display='none';
  }
}
<img src='img.jpg' />

使用现代外部JavaScript:

document.querySelectorAll('img').forEach((img) => {
  img.onerror = function() {
    this.style.display = 'none';
  }
});
<img src='img.jpg' />

请参阅浏览器对NoteList.forEacharrow函数的支持。


1
对于我来说,这是解决类似问题的最可靠解决方案。我宁愿不显示任何图像,也不愿显示残破的图像。谢谢。
pc_

在内容安全策略不允许内联脚本的情况下,这显然不起作用。
isherwood

@isherwood好点。我添加了通过外部JS文件工作的解决方案。
内森·亚瑟

11

这是一种替换所有损坏图像的快捷方法,不需要更改HTML代码;)

代码笔示例

    $("img").each(function(){
        var img = $(this);
        var image = new Image();
        image.src = $(img).attr("src");
        var no_image = "https://dummyimage.com/100x100/7080b5/000000&text=No+image";
        if (image.naturalWidth == 0 || image.readyState == 'uninitialized'){
            $(img).unbind("error").attr("src", no_image).css({
                height: $(img).css("height"),
                width: $(img).css("width"),
            });
        }
  });

9

我找不到适合自己需要的脚本,因此我创建了一个递归函数来检查损坏的图像,并尝试每四秒重新加载它们,直到它们被修复为止。

我将其限制为10次尝试,就好像它没有被加载一样,则映像可能不存在于服务器上,并且该函数将进入无限循环。我仍然在测试。随意调整:)

var retries = 0;
$.imgReload = function() {
    var loaded = 1;

    $("img").each(function() {
        if (!this.complete || typeof this.naturalWidth == "undefined" || this.naturalWidth == 0) {

            var src = $(this).attr("src");
            var date = new Date();
            $(this).attr("src", src + "?v=" + date.getTime()); //slightly change url to prevent loading from cache
            loaded =0;
        }
    });

    retries +=1;
    if (retries < 10) { // If after 10 retries error images are not fixed maybe because they
                        // are not present on server, the recursion will break the loop
        if (loaded == 0) {
            setTimeout('$.imgReload()',4000); // I think 4 seconds is enough to load a small image (<50k) from a slow server
        }
        // All images have been loaded
        else {
            // alert("images loaded");
        }
    }
    // If error images cannot be loaded  after 10 retries
    else {
        // alert("recursion exceeded");
    }
}

jQuery(document).ready(function() {
    setTimeout('$.imgReload()',5000);
});

尝试重新加载图像的好主意。这对于上载/自动生成的图像很有用,并且在加载页面时服务器可能尚未获得/完成。那是一些原始格式。有点太不一致了,不符合我的口味……
Joakim 2014年

8

这是一项cr脚的技术,但是可以保证:

<img ...  onerror="this.parentNode.removeChild(this);">

6

您可以为此使用GitHub自己的获取:

前端:https//github.com/github/fetch
或对于后端,Node.js版本:https//github.com/bitinn/node-fetch

fetch(url)
  .then(function(res) {
    if (res.status == '200') {
      return image;
    } else {
      return placeholder;
    }
  }

编辑:此方法将替换XHR,并且据说已经在Chrome中。对于以后阅读此书的任何人,您可能不需要包含上述库。


6

这是JavaScript,应该与跨浏览器兼容,并且交付时不会带有难看的标记onerror=""

var sPathToDefaultImg = 'http://cdn.sstatic.net/stackexchange/img/logos/so/so-icon.png',
    validateImage = function( domImg ) {
        oImg = new Image();
        oImg.onerror = function() {
            domImg.src = sPathToDefaultImg;
        };
        oImg.src = domImg.src;
    },
    aImg = document.getElementsByTagName( 'IMG' ),
    i = aImg.length;

while ( i-- ) {
    validateImage( aImg[i] );
}

CODEPEN:


投票while (i--)
s3c

4

更好地使用

jQuery(window).load(function(){
    $.imgReload();
});

因为使用document.ready不一定意味着图像已加载,所以仅HTML。因此,不需要延迟的呼叫。


4

CoffeeScript变体:

我解决了Turbolinks的一个问题,该问题有时会导致Firefox中出现.error()方法,即使该图像确实存在。

$("img").error ->
  e = $(@).get 0
  $(@).hide() if !$.browser.msie && (typeof this.naturalWidth == "undefined" || this.naturalWidth == 0)

4

通过使用Prestaul的答案,我添加了一些检查,并且我更喜欢使用jQuery方式。

<img src="image1.png" onerror="imgError(this,1);"/>
<img src="image2.png" onerror="imgError(this,2);"/>

function imgError(image, type) {
    if (typeof jQuery !== 'undefined') {
       var imgWidth=$(image).attr("width");
       var imgHeight=$(image).attr("height");

        // Type 1 puts a placeholder image
        // Type 2 hides img tag
        if (type == 1) {
            if (typeof imgWidth !== 'undefined' && typeof imgHeight !== 'undefined') {
                $(image).attr("src", "http://lorempixel.com/" + imgWidth + "/" + imgHeight + "/");
            } else {
               $(image).attr("src", "http://lorempixel.com/200/200/");
            }
        } else if (type == 2) {
            $(image).hide();
        }
    }
    return true;
}

4

如果您插入了imgwith innerHTML,例如:$("div").innerHTML = <img src="wrong-uri">,则如果加载失败,则可以加载另一个图像,例如:

<script>
    function imgError(img) {
        img.error="";
        img.src="valid-uri";
    }
</script>

<img src="wrong-uri" onerror="javascript:imgError(this)">

为什么javascript: _需要?由于通过脚本标记插入到DOM中的脚本在innerHTML注入时未运行,因此必须明确。


4

我在查看其他SO帖子时发现了此帖子。以下是我在那给出的答案的副本。

我知道这是一个旧线程,但是React已经流行起来,也许,使用React的人会来这里寻找相同问题的答案。

因此,如果您使用的是React,则可以执行以下操作,这是React团队的Ben Alpert在此处提供的原始答案。

getInitialState: function(event) {
    return {image: "http://example.com/primary_image.jpg"};
},
handleError: function(event) {
    this.setState({image: "http://example.com/failover_image.jpg"});
},
render: function() {
    return (
        <img onError={this.handleError} src={src} />;
    );
}

4

我创建了一个小提琴,以使用“ onerror”事件替换损坏的图像。这可能对您有帮助。

    //the placeholder image url
    var defaultUrl = "url('https://sadasd/image02.png')";

    $('div').each(function(index, item) {
      var currentUrl = $(item).css("background-image").replace(/^url\(['"](.+)['"]\)/, '$1');
      $('<img>', {
        src: currentUrl
      }).on("error", function(e) {
        $this = $(this);
        $this.css({
          "background-image": defaultUrl
        })
        e.target.remove()
      }.bind(this))
    })

4

这是一个使用JQuery包装的HTML5 Image对象的示例。为主图像URL调用load函数,如果该加载导致错误,则用备用URL替换图像的src属性。

function loadImageUseBackupUrlOnError(imgId, primaryUrl, backupUrl) {
    var $img = $('#' + imgId);
    $(new Image()).load().error(function() {
        $img.attr('src', backupUrl);
    }).attr('src', primaryUrl)
}

<img id="myImage" src="primary-image-url"/>
<script>
    loadImageUseBackupUrlOnError('myImage','primary-image-url','backup-image-url');
</script>

4

纯JS。我的任务是:如果图像'bl-once.png'为空->从数组列表(在当前目录中)插入第一个图像(没有404状态):

<img src="http://localhost:63342/GetImage/bl-once.png" width="200" onerror="replaceEmptyImage.insertImg(this)">

也许需要改进,但是:

var srcToInsertArr = ['empty1.png', 'empty2.png', 'needed.png', 'notActual.png']; // try to insert one by one img from this array
    var path;
    var imgNotFounded = true; // to mark when success

    var replaceEmptyImage = {
        insertImg: function (elem) {

            if (srcToInsertArr.length == 0) { // if there are no more src to try return
                return "no-image.png";
            }
            if(!/undefined/.test(elem.src)) { // remember path
                path = elem.src.split("/").slice(0, -1).join("/"); // "http://localhost:63342/GetImage"
            }
            var url = path + "/" + srcToInsertArr[0];

            srcToInsertArr.splice(0, 1); // tried 1 src

            
                if(imgNotFounded){ // while not success
                    replaceEmptyImage.getImg(url, path, elem); // CALL GET IMAGE
                }
            

        },
        getImg: function (src, path, elem) { // GET IMAGE

            if (src && path && elem) { // src = "http://localhost:63342/GetImage/needed.png"
                
                var pathArr = src.split("/"); // ["http:", "", "localhost:63342", "GetImage", "needed.png"]
                var name = pathArr[pathArr.length - 1]; // "needed.png"

                xhr = new XMLHttpRequest();
                xhr.open('GET', src, true);
                xhr.send();

                xhr.onreadystatechange = function () {

                    if (xhr.status == 200) {
                        elem.src = src; // insert correct src
                        imgNotFounded = false; // mark success
                    }
                    else {
                        console.log(name + " doesn't exist!");
                        elem.onerror();
                    }

                }
            }
        }

    };

因此,它将从当前目录向我的src或“ no-image.png”插入正确的“ needed.png”。


我在同一网站上使用过。这只是个主意。我真正的代码进行改进......
ДмитрийДорогонов

3

我不确定是否有更好的方法,但是我可以考虑采用这种方法-您可以将Ajax发布到img URL,然后解析响应以查看图像是否真的回来了。如果它以404或类似的形式返回,则换掉img。虽然我希望这会很慢。


3

我通过以下两个简单功能解决了我的问题:

function imgExists(imgPath) {
    var http = jQuery.ajax({
                   type:"HEAD",
                   url: imgPath,
                   async: false
               });
    return http.status != 404;
}

function handleImageError() {
    var imgPath;

    $('img').each(function() {
        imgPath = $(this).attr('src');
        if (!imgExists(imgPath)) {
            $(this).attr('src', 'images/noimage.jpg');
        }
    });
}

3

jQuery 1.8

// If missing.png is missing, it is replaced by replacement.png
$( "img" )
  .error(function() {
    $( this ).attr( "src", "replacement.png" );
  })
  .attr( "src", "missing.png" );

jQuery 3

// If missing.png is missing, it is replaced by replacement.png
$( "img" )
  .on("error", function() {
    $( this ).attr( "src", "replacement.png" );
  })
  .attr( "src", "missing.png" );

参考


3

我觉得我有事件委托和事件捕捉一个更优雅的方式windowerror,即使备份映像加载失败。

img {
  width: 100px;
  height: 100px;
}
<script>
  window.addEventListener('error', windowErrorCb, {
    capture: true
  }, true)

  function windowErrorCb(event) {
    let target = event.target
    let isImg = target.tagName.toLowerCase() === 'img'
    if (isImg) {
      imgErrorCb()
      return
    }

    function imgErrorCb() {
      let isImgErrorHandled = target.hasAttribute('data-src-error')
      if (!isImgErrorHandled) {
        target.setAttribute('data-src-error', 'handled')
        target.src = 'backup.png'
      } else {
        //anything you want to do
        console.log(target.alt, 'both origin and backup image fail to load!');
      }
    }
  }
</script>
<img id="img" src="error1.png" alt="error1">
<img id="img" src="error2.png" alt="error2">
<img id="img" src="https://i.stack.imgur.com/ZXCE2.jpg" alt="avatar">

重点是 :

  1. 将代码放入中,head并作为第一个内联脚本执行。因此,它将侦听脚本之后发生的错误。

  2. 使用事件捕获来捕获错误,尤其是对于那些不会冒泡的事件。

  3. 使用事件委托可以避免在每个图像上绑定事件。

  4. 为错误img元素赋予一个属性后,为它们提供一个属性,backup.png以避免消失,backup.png随后出现无限循环,如下所示:

img错误-> backup.png->错误-> backup.png->错误-> 、、、、


到目前为止,这是最好的答案。我只建议使用,let isImgErrorHandled = target.src === 'backup.png';因为它简化了一点。
萨博

@Sabo,有一个小问题,因为src路径可能不等于我分配的路径。例如,,target.src='backup.png'但下次console.log(target.src)可能不是backup.png
xianshenglu

2
;(window.jQuery || window.Zepto).fn.fallback = function (fallback) {
    return this.one('error', function () {
        var self = this;
        this.src = (fallback || 'http://lorempixel.com/$width/$height')
        .replace(/\$(\w+)/g, function (m, t) { return self[t] || ''; });
    });
};

您可以通过以下方式传递占位符路径,并从失败的图像对象访问所有属性$*

$('img').fallback('http://dummyimage.com/$widthx$height&text=$src');

http://jsfiddle.net/ARTsinn/Cu4Zn/


2

多年来一直让我感到沮丧。我的CSS修复程序在上设置了背景图片img。当动态图片src未加载到前景时,占位符在img的bg 上可见。这个工作,如果你的形象有一个默认的大小(例如heightmin-heightwidth和/或min-width)。

您会看到损坏的图像图标,但这是一个改进。已成功测试到IE9。iOS Safari和Chrome甚至没有显示损坏的图标。

.dynamicContainer img {
  background: url('/images/placeholder.png');
  background-size: contain;
}

添加一些动画,以便有src时间加载而不会闪烁背景。Chrome在背景中平滑变暗,但桌面Safari不会变暗。

.dynamicContainer img {
  background: url('/images/placeholder.png');
  background-size: contain;
  -webkit-animation: fadein 1s;
  animation: fadein 1s;                     
}

@-webkit-keyframes fadein {
  0%   { opacity: 0.0; }
  50%  { opacity: 0.5; }
  100% { opacity: 1.0; }
}

@keyframes fadein {
  0%   { opacity: 0.0; }
  50%  { opacity: 0.5; }
  100% { opacity: 1.0; }
}

2

如果无法加载图片(例如,由于所提供的URL中不存在图片),则图片URL将更改为默认值,

有关.error()的更多信息

$('img').on('error', function (e) {
  $(this).attr('src', 'broken.png');
});


1

我遇到了同样的问题。此代码在我的情况下效果很好。

// Replace broken images by a default img
$('img').each(function(){
    if($(this).attr('src') === ''){
      this.src = '/default_feature_image.png';
    }
});

1

有时,使用error事件是不可行的,例如,因为您试图在已加载的页面上执行某项操作,例如,通过控制台,书签或异步加载的脚本运行代码时。在这种情况下,请检查img.naturalWidthimg.naturalHeight为0似乎可以解决问题。

例如,以下代码片段可从控制台重新加载所有损坏的图像:

$$("img").forEach(img => {
  if (!img.naturalWidth && !img.naturalHeight) {
    img.src = img.src;
  }
}

0

我发现这是最好的方法,如果任何图像第一次加载失败,都会将其从DOM中完全删除。执行console.clear()可以使控制台窗口保持干净,因为使用try / catch块不能忽略404错误。

$('img').one('error', function(err) {
    // console.log(JSON.stringify(err, null, 4))
    $(this).remove()
    console.clear()
})
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.