jQuery / JavaScript pour remplacer les images cassées

547

J'ai une page Web qui comprend un tas d'images. Parfois, l'image n'est pas disponible, donc une image cassée est affichée dans le navigateur du client.

Comment utiliser jQuery pour obtenir l'ensemble d'images, le filtrer sur des images cassées puis remplacer le src?


- Je pensais qu'il serait plus facile de le faire avec jQuery, mais il s'est avéré beaucoup plus facile d'utiliser simplement une solution JavaScript pure, c'est-à-dire celle fournie par Prestaul.

Dan Lord
la source

Réponses:

760

Gérez l' onErrorévénement pour que l'image réaffecte sa source à l'aide de JavaScript:

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

Ou sans fonction JavaScript:

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

Le tableau de compatibilité suivant répertorie les navigateurs prenant en charge la fonction d'erreur:

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

Prestaul
la source
132
Vous voudrez probablement effacer l'erreur avant de définir src. Sinon, si noimage.gif est également manquant, vous pourriez vous retrouver avec un "débordement de pile".
pcorcoran le
45
Je pensais et espérais que nous nous
éloignions
24
redsquare ... ils sont parfaitement compréhensibles lorsque vous êtes judicieux de les garder sous contrôle et peuvent même être utiles lorsque vous voulez que votre balisage reflète ce qui va réellement se passer.
Nicole
2
Bon point NickC, à propos du balisage montrant ce qui se passe, je grimace juste quand je le regarde: P
SSH Ce
38
@redsquare, je suis entièrement d'accord, mais c'est un cas unique. Inline est le seul endroit où vous pouvez attacher l'auditeur avec une certitude absolue que l'événement ne se déclenchera pas avant que votre auditeur ne soit attaché. Si vous le faites à la fin du document ou attendez que DOM soit chargé, vous risquez de manquer l'événement d'erreur sur certaines images. Il ne sert à rien d'attacher un gestionnaire d'erreurs après le déclenchement de l'événement.
Prestaul
201

J'utilise le errorgestionnaire intégré :

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

Edit: La error()méthode est déconseillée dans jquery 1.8 et supérieur. Au lieu de cela, vous devez utiliser à la .on("error")place:

$("img").on("error", function () {
    $(this).attr("src", "broken.gif");
});
travis
la source
112
Si vous utilisez cette technique, vous pouvez utiliser la méthode "one" pour éviter d'avoir à dissocier l'événement: $ ('img'). One ('error', function () {this.src = 'broken.gif';}) ;
Prestaul
7
+1 Mais devez-vous le dissocier? Cela fonctionne à merveille sur les images dynamiques (par exemple, celles qui peuvent changer en survol) si vous ne les dissociez pas.
Aximili
15
Je pense que si vous ne le dissociez pas et que la référence cassée.gr src ne parvient jamais à se charger pour une raison quelconque, alors de mauvaises choses pourraient se produire dans le navigateur.
travis
4
@travis, à quel moment feriez-vous cet appel? Existe-t-il un moyen d'utiliser cette méthode et d'être certain que votre gestionnaire est attaché AVANT que l'événement d'erreur ne se déclenche?
Prestaul
5
Voici un exemple de cas où l'erreur se déclenche avant que le gestionnaire d'événements ne soit attaché: jsfiddle.net/zmpGz/1 Je suis vraiment curieux de savoir s'il existe un moyen de sécurité pour attacher ce gestionnaire ...
Prestaul
120

Dans le cas où quelqu'un comme moi, essaie d'attacher l' errorévénement à un HTML dynamiqueimg balise , je voudrais souligner qu'il y a un hic:

Apparemment, imgles événements d'erreur ne se propagent pas dans la plupart des navigateurs, contrairement à ce que la norme dit .

Donc, quelque chose comme ce qui suit ne fonctionnera pas :

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

J'espère que cela sera utile à quelqu'un d'autre. J'aimerais avoir vu ça ici dans ce fil. Mais je ne l'ai pas fait. Donc, je l'ajoute

mouad
la source
n'a pas fonctionné. Je charge dynamiquement une image morte et je l’ajoute au dom et aucun événement «erreur» n’est déclenché.
vsync
59

Voici une solution autonome:

$(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';
    }
  });
});
Devon
la source
5
Presque correct ... une image correcte dans IE retournera toujours (typeof (this.naturalWidth) == "undefined") == true; - J'ai changé l'instruction if en (! This.complete || (! $. Browser.msie && (typeof this.naturalWidth == "undefined" || this.naturalWidth == 0)))
Sugendran
3
cette règle car elle peut détecter une image cassée après un chargement de page. J'en avais besoin.
nord-américain
@Sugendran $.browsera été déprécié et supprimé maintenant, utilisez plutôt la détection des fonctionnalités (devient plus compliqué dans ce cas).
lee penkman
3
Autonome mais nécessite jQuery?
Charlie
Cela fonctionne très bien, mais lorsque le src de l'image renvoie un, 204 No Contentce sera une image cassée.
Carson Reinke
35

Je crois que c'est ce que vous recherchez: jQuery.Preload

Voici l'exemple de code de la démo, vous spécifiez le chargement et les images non trouvées et vous êtes prêt:

$('#images img').preload({
    placeholder:'placeholder.jpg',
    notFound:'notfound.jpg'
});
Nick Craver
la source
1
jQuery.Preload est un plugin qui est requis pour utiliser l'exemple de code.
Dan Lord
17
Oui ... ceci est lié dans la réponse de la première ligne.
Nick Craver
Merci Nick, cela semble être une bonne solution pour le problème de Firefox de plus de 8 ans ne montrant pas de remplacement d'image pour les images cassées. De plus, vous pouvez marquer la petite chose ... merci encore.
klewis
25
$(window).bind('load', function() {
$('img').each(function() {
    if((typeof this.naturalWidth != "undefined" &&
        this.naturalWidth == 0 ) 
        || this.readyState == 'uninitialized' ) {
        $(this).attr('src', 'missing.jpg');
    }
}); })

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

Mohamad
la source
1
Correction d'un problème d'erreur ne se déclenchant pas sur Firefox :) Merci.
ram
19

Alors que le PO cherchait à remplacer le SRC, je suis sûr que de nombreuses personnes qui ont posé cette question ne souhaitent que masquer l'image cassée, auquel cas cette solution simple a très bien fonctionné pour moi.

Utilisation de JavaScript en ligne:

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

Utilisation de JavaScript externe:

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

Utilisation de JavaScript externe moderne:

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

Voir la prise en charge du navigateur pour les fonctions NoteList.forEach et arrow .

Nathan Arthur
la source
1
J'ai trouvé cela comme une solution la plus fiable pour moi à un problème très similaire. Je préfère simplement ne montrer aucune image plutôt que de montrer une alternative d'image cassée. Merci.
pc_
Cela ne fonctionne évidemment pas lorsque la stratégie de sécurité du contenu interdit les scripts en ligne.
isherwood
@isherwood Bon point. J'ai ajouté une solution qui fonctionne via un fichier JS externe.
Nathan Arthur
11

Voici un moyen rapide et sale de remplacer toutes les images cassées, et il n'est pas nécessaire de changer le code HTML;)

exemple de 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"),
            });
        }
  });
Ashfaq Ahmed
la source
9

Je n'ai pas pu trouver de script adapté à mes besoins, j'ai donc créé une fonction récursive pour vérifier les images cassées et tenter de les recharger toutes les quatre secondes jusqu'à ce qu'elles soient corrigées.

Je l'ai limité à 10 tentatives comme s'il n'était pas chargé d'ici là, l'image pourrait ne pas être présente sur le serveur et la fonction entrerait dans une boucle infinie. Je teste toujours. N'hésitez pas à le modifier :)

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);
});
Ombre
la source
Belle idée d'essayer de recharger les images. Ceci est utile pour les images téléchargées / générées automatiquement et le serveur peut ne pas avoir été obtenu / terminé lors du chargement de la page. C'est une mise en forme originale. Un peu trop incohérent et pas à mon goût cependant ...
Joakim
8

C'est une technique merdique, mais elle est à peu près garantie:

<img ...  onerror="this.parentNode.removeChild(this);">
Phil LaNasa
la source
6

Vous pouvez utiliser la propre extraction de GitHub pour cela:

Frontend: https://github.com/github/fetch
ou pour Backend, une version Node.js: https://github.com/bitinn/node-fetch

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

Edit: Cette méthode va remplacer XHR et aurait déjà été dans Chrome. Si vous lisez ceci à l'avenir, vous n'aurez peut-être pas besoin de la bibliothèque susmentionnée.

BarryMode
la source
6

Ceci est JavaScript, devrait être compatible avec tous les navigateurs et fournit sans le balisage laid 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:

Axel
la source
vote positif pourwhile (i--)
s3c
4

Mieux appeler en utilisant

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

Parce que l'utilisation document.readyn'implique pas nécessairement que les images sont chargées, seul le HTML. Ainsi, aucun appel différé n'est nécessaire.

Ombre
la source
4

Variante CoffeeScript :

Je l'ai fait pour résoudre un problème avec Turbolinks qui provoque parfois l'apparition de la méthode .error () dans Firefox même si l'image est vraiment là.

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

En utilisant la réponse de Prestaul , j'ai ajouté quelques vérifications et je préfère utiliser la méthode 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;
}
trante
la source
4

Si vous avez inséré votre imgavec innerHTML, comme:, $("div").innerHTML = <img src="wrong-uri">vous pouvez charger une autre image si elle échoue, par exemple ceci:

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

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

Pourquoi est-il javascript: _nécessaire? Parce que les scripts injectés dans le DOM via des balises de script dans innerHTMLne sont pas exécutés au moment où ils sont injectés, vous devez donc être explicite.

horro
la source
4

J'ai trouvé ce post en regardant cet autre post SO . Vous trouverez ci-dessous une copie de la réponse que j'ai donnée là-bas.

Je sais que c'est un vieux fil, mais React est devenu populaire et, peut-être, quelqu'un utilisant React viendra ici à la recherche d'une réponse au même problème.

Donc, si vous utilisez React, vous pouvez faire quelque chose comme ci-dessous, qui était une réponse originale fournie par Ben Alpert de l'équipe React ici

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} />;
    );
}
Jordan Bonitatis
la source
4

J'ai créé un violon pour remplacer l'image cassée en utilisant l'événement "onerror". Cela peut vous aider.

    //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))
    })
Ninjaneer
la source
4

Voici un exemple utilisant l'objet Image HTML5 encapsulé par JQuery. Appelez la fonction de chargement pour l'URL de l'image principale et si cette charge provoque une erreur, remplacez l'attribut src de l'image par une URL de sauvegarde.

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>
Kurt Hartmann
la source
4

Pure JS. Ma tâche était: si l'image 'bl-once.png' est vide -> insérer la première image (qui n'a pas le statut 404) de la liste des tableaux (dans le répertoire courant):

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

Il faut peut-être l'améliorer, mais:

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

                }
            }
        }

    };

Ainsi, il insérera le 'required.png' correct dans mon src ou 'no-image.png' du répertoire actuel.

Дмитрий Дорогонов
la source
J'ai utilisé sur le même site. C'est juste une idée. Mon vrai code a été amélioré ...
Дмитрий Дорогонов
3

Je ne sais pas s'il y a une meilleure façon, mais je peux penser à un hack pour l'obtenir - vous pouvez Ajax poster sur l'URL img et analyser la réponse pour voir si l'image est effectivement revenue. Si elle est revenue en tant que 404 ou quelque chose, échangez l'img. Bien que je m'attende à ce que ce soit assez lent.

Chii
la source
3

J'ai résolu mon problème avec ces deux fonctions simples:

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');
        }
    });
}
Luis Gustavo Beligante
la source
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" );

référence

Nabi KAZ
la source
3

Je pense que j'ai une manière plus élégante avec la délégation d'événements et la capture d'événements window, errormême lorsque l'image de sauvegarde ne parvient pas à se charger.

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">

Le point est:

  1. Mettez le code dans headet exécuté comme premier script en ligne. Ainsi, il écoutera les erreurs qui se produisent après le script.

  2. Utilisez la capture d'événements pour détecter les erreurs, en particulier pour les événements qui ne bouillonnent pas.

  3. Utilisez la délégation d'événements qui évite de lier des événements sur chaque image.

  4. Donnez à l' imgélément error un attribut après leur avoir donné un backup.pngpour éviter la disparition de la backup.pngboucle infinie et des suivantes comme ci-dessous:

img error-> backup.png-> error-> backup.png-> error -> ,,,,,

xianshenglu
la source
C'est de loin la meilleure réponse. La seule chose que je suggérerais est d'utiliser let isImgErrorHandled = target.src === 'backup.png';car cela simplifie un peu.
Sabo
@Sabo, il y a un petit problème car le chemin src peut ne pas correspondre au chemin que j'ai attribué. Par exemple, target.src='backup.png'mais la prochaine fois console.log(target.src)pourrait ne pas êtrebackup.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] || ''; });
    });
};

Vous pouvez passer un chemin réservé et y accéder à toutes les propriétés de l'objet image défaillant via $*:

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

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

yckart
la source
2

Cela me frustre depuis des années. Ma correction CSS définit une image d'arrière-plan sur le img. Lorsqu'une image dynamique srcne se charge pas au premier plan, un espace réservé est visible sur le imgbg du. Cela fonctionne si vos images ont une taille par défaut (par exemple height, min-height, widthet / ou min-width).

Vous verrez l'icône d'image cassée mais c'est une amélioration. Testé avec succès vers IE9. iOS Safari et Chrome ne montrent même pas d'icône cassée.

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

Ajoutez une petite animation pour donner le srctemps de charger sans scintillement d'arrière-plan. Chrome s'estompe en arrière-plan en douceur, mais pas Safari de bureau.

.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; }
}
Dylan Valade
la source
2

Si l'image ne peut pas être chargée (par exemple, car elle n'est pas présente à l'URL fournie), l'URL de l'image sera remplacée par défaut,

Pour en savoir plus sur .error ()

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

Casper
la source
1

J'ai le même problème. Ce code fonctionne bien sur mon cas.

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

Parfois, l'utilisation de l' errorévénement n'est pas possible, par exemple parce que vous essayez de faire quelque chose sur une page qui est déjà chargée, comme lorsque vous exécutez du code via la console, un bookmarklet ou un script chargé de manière asynchrone. Dans ce cas, vérifier cela img.naturalWidthetimg.naturalHeight sont 0 semble faire l'affaire.

Par exemple, voici un extrait pour recharger toutes les images cassées à partir de la console:

$$("img").forEach(img => {
  if (!img.naturalWidth && !img.naturalHeight) {
    img.src = img.src;
  }
}
Léa Verou
la source
0

J'ai trouvé que cela fonctionnait mieux, si une image ne se charge pas la première fois, elle est complètement supprimée du DOM. L'exécution console.clear()maintient la fenêtre de la console propre, car les erreurs 404 ne peuvent pas être omises avec les blocs try / catch.

$('img').one('error', function(err) {
    // console.log(JSON.stringify(err, null, 4))
    $(this).remove()
    console.clear()
})
decoder7283
la source