jQuery / JavaScript для заміни зламаних зображень


546

У мене є веб-сторінка, яка містить купу зображень. Іноді зображення недоступне, тому зламане зображення відображається у браузері клієнта.

Як я використовую 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 Це

38
@redsquare я повністю згоден, але це унікальний випадок. Inline - це єдине місце, де ви можете прив’язати слухача з абсолютною впевненістю, що подія не запуститься до того, як ваш слухач буде приєднаний. Якщо ви робите це в кінці документа або чекаєте завантаження DOM, ви можете пропустити подію помилки на деяких зображеннях. Немає користі приєднувати оброблювач помилок після закінчення події.
Prestaul

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 ("помилка", функція () {this.src = 'зла.gif';}) ;
Prestaul

7
+1 Але чи потрібно це скасувати? Це прекрасно працює на динамічних зображеннях (наприклад, на тих, які можуть змінюватися на наведення курсора), якщо ви не відв'язуєте їх.
Аксімілі

15
Я думаю, що якщо ви не скасуєте це, і посилання невідомої sr.gif не буде завантажено з будь-якої причини, то в браузері можуть трапитися погані речі.
travis

4
@travis, в який момент ти би зателефонував? Чи є спосіб скористатися цим методом і бути впевненим, що ваш обробник приєднується до початку пожеж?
Prestaul

5
Ось приклад випадку, коли помилка видаляється до того, як додається обробник подій: jsfiddle.net/zmpGz/1 Мені справді цікаво, чи існує безпечний спосіб приєднати цей обробник ...
Prestaul

120

Якщо хтось, як я, спробує приєднати errorподію до динамічного imgтегу HTML , я хотів би зазначити:

Мабуть, imgподії помилок не виникають у більшості браузерів, всупереч тому, що говорить стандарт .

Отже, щось подібне не вийде :

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

Сподіваюся, що це буде корисно комусь іншому. Мені б хотілося, щоб я це бачив у цій темі. Але я цього не зробив. Отже, я додаю це


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

3
це правило, оскільки воно може виявити зламане зображення після завантаження сторінки. мені це було потрібно.
північний американський

@Sugendran $.browserтепер застарілий і видалений, замість цього використовуйте виявлення функцій (у цьому випадку стає складніше).
lee penkman

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

Хоча ОП шукала замінити КРК, я впевнений, що багато людей, які потрапляють у це питання, можуть лише захотіти приховати порушене зображення, і в цьому випадку це просте рішення спрацювало для мене чудово.

Використання вбудованого 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

@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:

Frontend: 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 forwhile (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

Користуючись відповіддю 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

Якщо ви вставили 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

Я знайшов цю публікацію, переглядаючи цю іншу публікацію SO . Нижче - копія відповіді, яку я там дав.

Я знаю, що це стара тема, але 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();
                    }

                }
            }
        }

    };

Таким чином, він буде вставити правильний 'Need.png' в мій src або 'no-image.png' з поточного dir.


Я використовував на тому самому сайті. Це просто ідея. Мій реальний код був покращений ...
Дмитрий Дорогонов

3

Я не впевнений, чи є кращий спосіб, але я можу придумати хак, щоб його отримати - ви могли б Ajax опублікувати URL-адресу img та проаналізувати відповідь, щоб побачити, чи дійсно зображення повернулося. Якщо він повернувся як 404 або щось подібне, тоді поміняйте імг. Хоча я очікую, що це буде досить повільним.


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не завантажується на передній план, на Bg видно заповнювач заповнення img. Це працює , якщо ваші зображення мають розмір за замовчуванням (наприклад height, min-height, widthі / або min-width).

Ви побачите значок зламаного зображення, але це покращення. Тестовано до IE9. iOS Safari та Chrome навіть не показують зламану піктограму.

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

Додайте трохи анімації, щоб дати srcчас завантажуватися без мерехтіння фону. Хром плавно вицвітає на задньому плані, але 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.naturalHeight0 є.

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

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

0

Я вважаю, що це працює найкраще, якщо якесь зображення не вдається завантажити перший раз, воно повністю видаляється з DOM. Виконання забезпечує console.clear()збереження вікна консолі чистим, оскільки помилки 404 не можна опустити за допомогою блоків спробувати.

$('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.