jQuery/JavaScript 替换损坏的图像

jQuery/JavaScript to replace broken images

提问人:Dan Lord 提问时间:9/18/2008 最后编辑:Peter MortensenDan Lord 更新时间:10/1/2021 访问量:325838

问:

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

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


--我以为使用 jQuery 会更容易做到这一点,但事实证明,只使用纯 JavaScript 解决方案要容易得多,即 Prestaul 提供的解决方案。

javascript jquery html 损坏的图像

评论


答:

819赞 Prestaul 9/18/2008 #1

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

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

评论

142赞 pcorcoran 9/19/2008
您可能希望在设置 src 之前清除 onerror。否则,如果 noimage.gif 也丢失,则最终可能会出现“堆栈溢出”。
49赞 redsquare 9/2/2009
我以为并希望我们正在摆脱 javascript 事件的内联属性......
25赞 Nicole 2/19/2010
红方...当您明智地控制它们时,它们是完全可以理解的,甚至当您希望标记反映实际将要发生的事情时,它们也很有用。
2赞 SSH This 3/28/2012
好点 NickC,关于显示正在发生的事情的标记,当我看到它时,我只是畏缩:P
39赞 Prestaul 9/5/2012
@redsquare,我完全同意,但这是一个独特的案例。内联是唯一可以附加侦听器的地方,可以绝对确定在附加侦听器之前不会触发事件。如果在文档末尾执行此操作或等待 DOM 加载,则可能会错过某些图像上的错误事件。在事件触发后附加错误处理程序对你没有好处。
39赞 Nick Craver 9/18/2008 #2

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

下面是演示中的示例代码,您指定了正在加载和未找到的图像,然后就完成了所有设置:

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

评论

17赞 Nick Craver 9/19/2008
是的。。。这在第一行的答案中链接。
3赞 Chii 9/18/2008 #3

我不确定是否有更好的方法,但我可以想出一个技巧来获取它 - 您可以将 Ajax 发布到 img URL,并解析响应以查看图像是否真的回来了。如果它以 404 或其他形式返回,则换掉 img。虽然我预计这会很慢。

65赞 Devon 9/18/2008 #4

下面是一个独立的解决方案:

$(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赞 Sugendran 2/19/2009
几乎正确...IE 中的正确图像仍将返回 (typeof(this.naturalWidth) == “undefined”) == true;- 我将 if 语句更改为 (!this.complete ||(!$.browser.msie && (typeof this.naturalWidth == “undefined” || this.naturalWidth == 0)))
214赞 travis 10/4/2008 #5

我使用内置的处理程序:error

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

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

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

评论

118赞 Prestaul 4/2/2011
如果使用此技术,则可以使用 “one” 方法来避免需要解绑事件: $('img').one('error', function() { this.src = 'broken.gif'; });
31赞 Mohamad 3/26/2010 #6
$(window).bind('load', function() {
  $('img').each(function() {
    if( (typeof this.naturalWidth != "undefined" && this.naturalWidth == 0) 
    ||  this.readyState == 'uninitialized'                                  ) {
        $(this).attr('src', 'missing.jpg');
    }
  });
});

来源:http://www.developria.com/2009/03/jquery-quickie---broken-images.html

9赞 Shade 12/20/2011 #7

我找不到适合我需求的脚本,所以我做了一个递归函数来检查损坏的图像,并尝试每四秒重新加载一次,直到它们被修复。

我将其限制为 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);
});
4赞 Shade 12/24/2011 #8

更好的通话方式

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

因为 using 不一定意味着加载图像,只有 HTML。因此,无需延迟呼叫。document.ready

136赞 mouad 6/15/2012 #9

如果像我这样的人试图将事件附加到动态 HTML 标签上,我想指出的是,有一个问题:errorimg

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

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

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

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

评论

1赞 vsync 6/15/2014
没用。我动态加载一个死图像并将其附加到 dom 中,并且没有触发“错误”事件。
4赞 yckart 6/28/2013 #10
(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/

4赞 Kevin 7/19/2013 #11

CoffeeScript 变体:

我这样做是为了解决 Turbolinks 的一个问题,该问题有时会导致 .error() 方法在 Firefox 中引发,即使图像确实存在。

$("img").error ->
  e = $(@).get 0
  $(@).hide() if !$.browser.msie && (typeof this.naturalWidth == "undefined" || this.naturalWidth == 0)
3赞 Luis Gustavo Beligante 10/21/2013 #12

我用这两个简单的函数解决了我的问题:

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');
        }
    });
}
11赞 Ashfaque Ali Solangi 2/18/2014 #13

这是一种快速而肮脏的方法来替换所有损坏的图像,并且无需更改HTML代码;)

CodePen 示例

    $("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"),
            });
        }
  });
11赞 Phil LaNasa 4/11/2014 #14

这是一种蹩脚的技术,但几乎可以保证:

<img onerror="this.parentNode.removeChild(this);">
6赞 Dylan Valade 4/25/2014 #15

多年来,这一直让我感到沮丧。我的 CSS 修复在 .当动态图像未加载到前台时,在 的 bg 上可以看到一个占位符。如果您的图像具有默认大小(例如 、 和/或 )。imgsrcimgheightmin-heightwidthmin-width

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

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

添加一些动画,以便在没有背景闪烁的情况下有时间加载。Chrome 在后台会平滑淡出,但桌面 Safari 不会。src

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

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

.dynamicContainer img {
  background: url('https://picsum.photos/id/237/200');
  background-size: contain;
  animation: fadein 1s;
}

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

img {
  /* must define dimensions */
  width: 200px;
  height: 200px;
  min-width: 200px;
  min-height: 200px;
  /* hides broken text */
  color: transparent;
  /* optional css below here */
  display: block;
  border: .2em solid black;
  border-radius: 1em;
  margin: 1em;
}
<div class="dynamicContainer">
  <img src="https://picsum.photos/200" alt="Found image" />
  <img src="https://picsumx.photos/200" alt="Not found image" />
</div>

4赞 trante 6/29/2014 #16

通过使用 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;
}
6赞 Axel 8/19/2014 #17

这是 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] );
}

代码笔:

5赞 horro 11/12/2014 #18

如果您插入了 ,例如: ,如果操作失败,您可以加载另一个图像,例如:imginnerHTML$("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)">

为什么需要?因为通过脚本标签注入到 DOM 中的脚本在注入时不会运行,所以你必须明确。javascript: _innerHTML

6赞 i i 5/3/2016 #19

为此,您可以使用 GitHub 自己的 fetch:

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

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

编辑:此方法将取代XHR,并且据说已经在Chrome中。对于将来阅读本文的任何人,您可能不需要包含上述库。

4赞 Jordan Bonitatis 7/14/2016 #20

我在看另一个 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赞 Rohith K P 9/1/2016 #21

我创建了一个小提琴来使用“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))
    })
27赞 Nathan Arthur 10/26/2016 #22

虽然 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' />

请参阅浏览器对 NodeList.forEach箭头函数的支持。

4赞 Kurt Hartmann 11/5/2016 #23

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

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赞 Dima Dorogonov 4/7/2017 #24

纯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 中插入正确的“需要.png”或从当前目录中插入“no-image.png”。

3赞 Nabi K.A.Z. 9/2/2017 #25

j查询 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" );

参考

1赞 Dale Nguyen 11/17/2017 #26

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

// Replace broken images by a default img
$('img').each(function(){
    if($(this).attr('src') === ''){
      this.src = '/default_feature_image.png';
    }
});
3赞 Lea Verou 1/14/2018 #27

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

例如,下面是一个代码片段,用于从控制台重新加载所有损坏的图像:

$$("img").forEach(img => {
  if (!img.naturalWidth && !img.naturalHeight) {
    img.src = img.src;
  }
}
2赞 Casper 2/20/2018 #28

如果无法加载图像(例如,因为它不存在在提供的 URL 中),则图像 URL 将更改为默认值,

有关 .error() 的更多信息

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

3赞 xianshenglu 5/23/2018 #29

我认为我有一种更优雅的方式,即使在备份映像无法加载时,也可以进行事件委派和事件捕获。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. 在给 error 元素一个属性后,给它们一个属性,以避免消失和随后的无限循环,如下所示:imgbackup.pngbackup.png

img 错误->备份.png->错误->备份.png->错误->,,,,,

评论

0赞 Sabo 11/29/2019
这是迄今为止最好的答案。我唯一建议的是使用,因为它简化了一点。let isImgErrorHandled = target.src === 'backup.png';
0赞 xianshenglu 11/29/2019
@Sabo,有一个小问题,因为 src 路径可能不等于我分配的路径。例如,,但下次可能不是target.src='backup.png'console.log(target.src)backup.png
4赞 Jawla 10/1/2021 #30

对于 React 开发人员

<img 
    src={"https://urlto/yourimage.png"} // <--- If this image src fail to load, onError function will be called, where you can add placeholder image or any image you want to load
    width={200} 
    alt={"Image"} 
    onError={(event) => {
       event.target.onerror = "";
       event.target.src = "anyplaceholderimageUrlorPath"
       return true;
    }}
 />