JQuery / JavaScript, чтобы заменить сломанные изображения


547

У меня есть веб-страница, которая включает в себя кучу изображений. Иногда изображение недоступно, поэтому поврежденное изображение отображается в браузере клиента.

Как мне использовать jQuery, чтобы получить набор изображений, отфильтровать его до поврежденных изображений, а затем заменить src?


- Я думал, что с jQuery было бы легче сделать это, но оказалось гораздо проще просто использовать чистое решение JavaScript, то есть то, которое предоставляет Prestaul.

Ответы:


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
Вы, вероятно, хотите очистить onerror перед установкой src. В противном случае, если noimage.gif также отсутствует, вы можете получить «переполнение стека».
pcorcoran

45
Я думал и надеялся, что мы отошли от встроенных атрибутов для событий javascript ...
redsquare

24
redsquare ... они вполне понятны, если вы осмотрительны в отношении их контроля, и даже могут быть полезны, когда вы хотите, чтобы ваша разметка отражала то, что на самом деле произойдет.
Николь

2
Хороший вопрос, NickC, по поводу разметки, показывающей, что происходит, я просто съеживаюсь, когда смотрю на это: P
SSH This

38
@redsquare, я полностью согласен, но это уникальный случай. Inline - это единственное место, где вы можете присоединить слушателя с абсолютной уверенностью, что событие не сработает, пока ваш слушатель не будет подключен. Если вы делаете это в конце документа или ждете загрузки DOM, вы можете пропустить событие ошибки на некоторых изображениях. Не стоит добавлять обработчик ошибок после того, как событие сработало.
Престол

201

Я использую встроенный errorобработчик:

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

Edit:error() метод является устаревшим в JQuery 1.8 и выше. Вместо этого вы должны использовать .on("error")вместо:

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

112
Если вы используете эту технику, вы можете использовать метод "one", чтобы избежать необходимости отмены привязки события: $ ('img'). One ('error', function () {this.src = 'broken.gif';}) ;
Престол

7
+1 А разве вы должны это развязать? Он прекрасно работает с динамическими изображениями (например, те, которые могут меняться при наведении), если вы не отсоединяете его.
Аксимили

15
Я думаю, что если вы не отсоедините его, а ссылка на broken.gif src по какой-либо причине не загрузится, то в браузере могут произойти плохие вещи.
Трэвис

4
@travis, в какой момент вы бы сделали этот звонок? Есть ли способ использовать этот метод и быть уверенным, что ваш обработчик присоединяется ПЕРЕД возникновением события ошибки?
Престол

5
Вот пример случая, когда ошибка возникает до того, как прикреплен обработчик события: jsfiddle.net/zmpGz/1 Мне действительно любопытно, есть ли надежный способ присоединить этот обработчик ...
Prestaul

120

Если кто-то, вроде меня, попытается прикрепить errorсобытие к динамическому imgтегу HTML , я хотел бы отметить, что здесь есть одна загвоздка:

Очевидно, что imgошибки ошибок не всплывают в большинстве браузеров, в отличие от того, что говорится в стандарте .

Итак, что-то вроде следующего не будет работать :

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

Надеюсь, это будет полезно для кого-то еще. Я хотел бы видеть это здесь, в этой теме. Но я не сделал. Итак, я добавляю это


не работал Я загружаю мертвое изображение динамически и добавляю его в dom, и событие 'error' не запускается.
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
это правила, потому что он может обнаружить поврежденное изображение после загрузки страницы. мне это нужно
Североамериканский

@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


19

В то время как ОП искал замену 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.forEach и стрелки .


1
Я нашел это как наиболее надежное для меня решение очень похожей проблемы. Я просто предпочитаю не показывать изображение, а не показывать альтернативу с разбитым изображением. Спасибо.
pc_

Это, очевидно, не работает, когда политика безопасности содержимого запрещает встроенные сценарии.
Ишервуд

@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);
});

Хорошая идея с попыткой перезагрузить изображения. Это полезно для изображений, которые загружены / автоматически созданы, и сервер, возможно, не был получен / завершен, поскольку страница загружена. Это оригинальное форматирование. Слишком непоследователен и не в моем вкусе, однако ...
Иоаким

8

Это дрянная техника, но она в значительной степени гарантирована:

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

6

Вы можете использовать собственный выбор GitHub для этого:

Внешний интерфейс: https://github.com/github/fetch
или для Backend, версия 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:


upvote дляwhile (i--)
s3c

4

Лучше звонить, используя

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

Поскольку использование document.readyне обязательно означает, что изображения загружаются, только HTML. Таким образом, нет необходимости в задержке вызова.


4

CoffeeScriptВариант :

Я сделал это, чтобы исправить проблему с Turbolinks, из-за которой метод .error () иногда вызывался в Firefox, даже если изображение действительно там.

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

4

Используя ответ Престола , я добавил несколько проверок и предпочитаю использовать способ 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

Если вы вставили imgс помощью 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

Я нашел этот пост, глядя на этот другой пост . Ниже копия ответа, который я дал там.

Я знаю, что это старая ветка, но React стал популярным, и, возможно, кто-то, использующий React, придет сюда в поисках ответа на ту же проблему.

Итак, если вы используете React, вы можете сделать что-то вроде ниже, который был оригинальным ответом, предоставленным Беном Альпертом из команды React здесь

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

Вот пример использования объекта HTML5 Image, обернутого JQuery. Вызовите функцию загрузки для основного URL-адреса изображения и, если эта загрузка вызовет ошибку, замените атрибут 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

Чистый 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();
                    }

                }
            }
        }

    };

Таким образом, он вставит правильный 'required.png' в мой src или 'no-image.png' из текущего каталога.


Я использовал на том же сайте. Это просто идея. Мой реальный код был улучшен ...
Дмитрий Дорогонов

3

Я не уверен, что есть лучший способ, но я могу придумать, как его получить - вы можете Ajax опубликовать URL-адрес img и проанализировать ответ, чтобы увидеть, действительно ли изображение вернулось. Если это вернулось как 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

Я думаю , что у меня есть более элегантный способ с делегацией событий и событий захвата на window«S , errorдаже если образ резервной копии не нагрузки.

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 error-> backup.png-> error-> backup.png-> error-> ,,,,,


Это, безусловно, лучший ответ. Единственное, что я хотел бы предложить, это использовать, 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не загружается на передний план, на imgbg виден заполнитель . Это работает , если ваши изображения имеют размер по умолчанию (например height, min-height, widthи / или 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.naturalWidthиimg.naturalHeight 0, кажется, делает свое дело.

Например, вот фрагмент для перезагрузки всех неработающих изображений из консоли:

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

0

Я нашел, что это работает лучше всего, если какое-либо изображение не загружается в первый раз, оно полностью удаляется из DOM. Выполнение console.clear()сохраняет консольное окно чистым, поскольку ошибки 404 нельзя пропустить с помощью блоков try / catch.

$('img').one('error', function(err) {
    // console.log(JSON.stringify(err, null, 4))
    $(this).remove()
    console.clear()
})
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.