jQuery / JavaScript per sostituire le immagini rotte


547

Ho una pagina web che include un mucchio di immagini. A volte l'immagine non è disponibile, quindi un'immagine rotta viene visualizzata nel browser del client.

Come posso usare jQuery per ottenere il set di immagini, filtrarlo in immagini rotte e quindi sostituire l'src?


- Ho pensato che sarebbe stato più facile farlo con jQuery, ma è risultato molto più semplice utilizzare solo una soluzione JavaScript pura, ovvero quella fornita da Prestaul.

Risposte:


760

Gestisci l' onErrorevento affinché l'immagine riassegni la sua fonte usando JavaScript:

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

O senza una funzione JavaScript:

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

La seguente tabella di compatibilità elenca i browser che supportano la funzione di errore:

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


132
Probabilmente vuoi cancellare onerror prima di impostare src. Altrimenti se manca anche noimage.gif potresti finire con uno "overflow dello stack".
pcorcoran,

45
Ho pensato e sperato che ci stessimo allontanando dagli attributi inline per gli eventi javascript ...
redsquare

24
redsquare ... sono perfettamente comprensibili quando si è prudenti nel tenerli sotto controllo e possono anche essere utili quando si desidera che il markup rifletta ciò che sta realmente accadendo.
Nicole,

2
Buon punto NickC, riguardo al markup che mostra cosa sta succedendo, mi limito a guardarlo: P
SSH Questo

38
@redsquare, sono completamente d'accordo, ma questo è un caso unico. Inline è l'unico posto in cui è possibile collegare l'ascoltatore con assoluta certezza che l'evento non si attiverà prima che l'ascoltatore sia collegato. Se lo fai alla fine del documento o attendi il caricamento del DOM, potresti perdere l'evento di errore su alcune immagini. Non è utile allegare un gestore errori dopo che l'evento è stato generato.
Prestaul,

201

Uso il errorgestore integrato :

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

Modifica: il error()metodo è obsoleto in jquery 1.8 e versioni successive. Invece, dovresti usare .on("error")invece:

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

112
Se usi questa tecnica puoi usare il metodo "one" per evitare di dover separare l'evento: $ ('img'). One ('error', function () {this.src = 'broken.gif';}) ;
Prestaul

7
+1 Ma devi scioglierlo? Funziona magnificamente su immagini dinamiche (ad es. Quelle che possono cambiare al passaggio del mouse) se non le si scioglie.
Aximili,

15
Penso che se non lo si scioglie e il riferimento spezzato.gif src non riesce mai a caricarsi per qualsiasi motivo, nel browser potrebbero accadere cose brutte.
travis,

4
@travis, a che punto effettueresti questa chiamata? Esiste un modo per utilizzare questo metodo e accertarsi che il gestore venga collegato PRIMA che l'evento di errore venga generato?
Prestaul,

5
Ecco un esempio di un caso in cui l'errore viene generato prima che il gestore eventi sia collegato: jsfiddle.net/zmpGz/1 Sono sinceramente curioso se esiste un modo sicuro per collegare questo gestore ...
Prestaul

120

Nel caso qualcuno come me, provi ad associare l' errorevento a un imgtag HTML dinamico , vorrei sottolineare che c'è un problema:

Apparentemente gli imgeventi di errore non si diffondono nella maggior parte dei browser, contrariamente a quanto afferma lo standard .

Quindi, qualcosa di simile al seguente non funzionerà :

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

Spero che questo possa essere utile a qualcun altro. Vorrei averlo visto qui in questa discussione. Ma non l'ho fatto. Quindi lo sto aggiungendo


non ha funzionato. Carico dinamicamente un'immagine morta e la aggiungo al dom e non viene generato alcun evento di "errore".
vsync,

59

Ecco una soluzione autonoma:

$(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
Quasi corretto ... verrà comunque restituita un'immagine corretta in IE (typeof (this.naturalWidth) == "undefined") == true; - Ho cambiato l'istruzione if in (! This.complete || (! $. Browser.msie && (typeof this.naturalWidth == "undefined" || this.naturalWidth == 0)))
Sugendran,

3
questa regola perché può rilevare un'immagine spezzata dopo il caricamento di una pagina. ne avevo bisogno.
nordamericano,

@Sugendran $.browserè stato deprecato e rimosso ora, utilizza invece il rilevamento delle funzionalità (diventa più complicato in questo caso).
Lee Penkman,

3
Autonomo ma richiede jQuery?
Charlie

Funziona benissimo, ma quando ritorna l'immagine sr 204 No Contentsarebbe un'immagine rotta.
Carson Reinke,

35

Credo che questo sia ciò che stai cercando : jQuery.Preload

Ecco il codice di esempio dalla demo, specifichi il caricamento e le immagini non trovate e sei pronto:

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

1
jQuery.Preload è un plug-in necessario per utilizzare il codice di esempio.
Dan Lord,

17
Sì ... questo è collegato nella risposta sulla prima riga.
Nick Craver

Grazie Nick, questa sembra essere una buona soluzione per il problema di 8+ anni di Firefox che non mostra una sostituzione di immagini per immagini rotte. Inoltre puoi marchiare la piccola cosa ... grazie ancora.
Klewis,


19

Mentre l'OP stava cercando di sostituire l'SRC, sono sicuro che molte persone che rispondono a questa domanda potrebbero solo voler nascondere l'immagine spezzata, nel qual caso questa semplice soluzione ha funzionato perfettamente per me.

Utilizzando JavaScript incorporato:

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

Utilizzando JavaScript esterno:

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

Utilizzando JavaScript esterno moderno:

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

Vedere il supporto del browser per NoteList.forOgni e le funzioni freccia .


1
Ho trovato questa soluzione più affidabile per me per un problema molto simile. Preferisco semplicemente non mostrare alcuna immagine piuttosto che mostrare un'alternativa di immagine rotta. Grazie.
pc_4

Ciò ovviamente non funziona laddove la politica di sicurezza dei contenuti non consente gli script incorporati.
Isherwood,

@isherwood Un buon punto. Ho aggiunto una soluzione che funziona tramite un file JS esterno.
Nathan Arthur,

11

Ecco un modo rapido e sporco per sostituire tutte le immagini rotte e non è necessario modificare il codice HTML;)

esempio 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"),
            });
        }
  });

9

Non sono riuscito a trovare uno script adatto alle mie esigenze, quindi ho creato una funzione ricorsiva per verificare la presenza di immagini rotte e tentare di ricaricarle ogni quattro secondi fino a quando non vengono risolte.

L'ho limitato a 10 tentativi come se non fosse caricato da allora l'immagine potrebbe non essere presente sul server e la funzione entrerebbe in un ciclo infinito. Sto ancora testando però. Sentiti libero di modificarlo :)

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

Bella idea con il tentativo di ricaricare le immagini. Ciò è utile per le immagini caricate / generate automaticamente e il server potrebbe non essere ottenuto / completato durante il caricamento della pagina. Questa è una formattazione originale. Un po 'troppo incoerente e non nei miei gusti comunque ...
Joakim,

8

Questa è una tecnica schifosa, ma è praticamente garantita:

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

6

Puoi usare il recupero di GitHub per questo:

Frontend: https://github.com/github/fetch
o per Backend, una versione Node.js: https://github.com/bitinn/node-fetch

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

Modifica: questo metodo sostituirà XHR e presumibilmente è già stato in Chrome. Per chiunque leggerà questo in futuro, potrebbe non essere necessario includere la libreria di cui sopra.


6

Questo è JavaScript, dovrebbe essere compatibile con più browser e offre senza il brutto markup 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:


votato perwhile (i--)
s3c il

4

Meglio chiamare usando

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

Poiché l'utilizzo document.readynon implica necessariamente che le immagini siano caricate, solo l'HTML. Pertanto, non è necessario effettuare una chiamata ritardata.


4

Variante CoffeeScript :

Ho risolto un problema con Turbolink che causa il metodo .error () per essere sollevato in Firefox a volte anche se l'immagine è davvero lì.

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

4

Usando la risposta di Prestaul , ho aggiunto alcuni controlli e preferisco usare il modo 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

Se hai inserito il tuo imgcon innerHTML, come: $("div").innerHTML = <img src="wrong-uri">è possibile caricare un'altra immagine se non riesce a fare, ad esempio, questo:

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

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

Perché è javascript: _necessario? Poiché gli script immessi nel DOM tramite i tag di script innerHTMLnon vengono eseguiti nel momento in cui vengono iniettati, è necessario essere espliciti.


4

Ho trovato questo post mentre guardavo questo altro post SO . Di seguito è una copia della risposta che ho dato lì.

So che questo è un vecchio thread, ma React è diventato popolare e, forse, qualcuno che usa React verrà qui alla ricerca di una risposta allo stesso problema.

Quindi, se stai usando React, puoi fare qualcosa come il seguente, che era una risposta originale fornita da Ben Alpert del team React qui

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

Ho creato un violino per sostituire l'immagine rotta usando l'evento "onerror". Questo può aiutarti.

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

Ecco un esempio usando l'oggetto Image HTML5 racchiuso da JQuery. Chiamare la funzione di caricamento per l'URL dell'immagine principale e se quel caricamento provoca un errore, sostituire l'attributo src dell'immagine con un URL di backup.

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 puro. Il mio compito era: se l'immagine 'bl-once.png' è vuota -> inserisci la prima immagine (che non ha lo stato 404) dall'elenco di array (nella directory corrente):

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

Forse deve essere migliorato, ma:

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

                }
            }
        }

    };

Quindi, inserirà 'necessario.png' corretto nel mio src o 'no-image.png' dalla directory corrente.


Ho usato sullo stesso sito. Questa è solo un'idea. Il mio vero codice è stato migliorato ...
Дмитрий Дорогонов

3

Non sono sicuro che ci sia un modo migliore, ma posso pensare a un hack per ottenerlo: potresti Ajax pubblicare nell'URL img e analizzare la risposta per vedere se l'immagine è effettivamente tornata. Se è tornato come un 404 o qualcosa del genere, scambia l'img. Anche se mi aspetto che questo sia abbastanza lento.


3

Ho risolto il mio problema con queste due semplici funzioni:

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

riferimento


3

Penso di avere un modo più elegante con la delegazione evento e l'evento catturare il window's erroranche quando l'immagine di backup non riescono a carico.

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

Il punto è :

  1. Inserisci il codice in headed eseguito come primo script incorporato. Quindi, ascolterà gli errori che si verificano dopo lo script.

  2. Usa la cattura degli eventi per rilevare gli errori, specialmente per quegli eventi che non fanno bolle.

  3. Utilizzare la delega di eventi che evita di associare eventi su ciascuna immagine.

  4. Assegna imgall'elemento errore un attributo dopo avergli dato un backup.pngper evitare la scomparsa del backup.pngciclo infinito successivo e come di seguito:

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


Questa è di gran lunga la migliore risposta. L'unica cosa che suggerirei è di usare let isImgErrorHandled = target.src === 'backup.png';in quanto semplifica un po '.
Sabo,

@Sabo, c'è un piccolo problema perché il percorso src potrebbe non essere uguale al percorso che ho assegnato. Ad esempio, target.src='backup.png'ma la prossima volta console.log(target.src)potrebbe non esserebackup.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] || ''; });
    });
};

È possibile passare un percorso segnaposto e accedervi tutte le proprietà dall'oggetto immagine non riuscito tramite $*:

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

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


2

Questo mi ha frustrato per anni. La mia correzione CSS imposta un'immagine di sfondo su img. Quando un'immagine dinamica srcnon viene caricata in primo piano, un segnaposto è visibile sulla imgbg. Funziona se le tue immagini hanno una dimensione predefinita (ad es height. min-height, widthE / o min-width).

Vedrai l'icona dell'immagine rotta ma è un miglioramento. Testato correttamente su IE9. iOS Safari e Chrome non mostrano nemmeno un'icona rotta.

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

Aggiungi una piccola animazione per dare il srctempo di caricare senza uno sfarfallio di sfondo. Chrome si attenua in modo uniforme sullo sfondo, mentre Safari desktop no.

.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

Se l'immagine non può essere caricata (ad esempio perché non è presente nell'URL fornito), l'URL dell'immagine verrà modificato in modo predefinito,

Per maggiori informazioni su .error ()

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


1

Ho avuto lo stesso problema. Questo codice funziona bene sul mio caso.

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

1

A volte l'utilizzo errordell'evento non è fattibile, ad esempio perché stai provando a fare qualcosa su una pagina che è già stata caricata, ad esempio quando esegui il codice tramite la console, un bookmarklet o uno script caricato in modo asincrono. In tal caso, controllando che img.naturalWidtheimg.naturalHeight sono 0 sembra fare il trucco.

Ad esempio, ecco uno snippet per ricaricare tutte le immagini rotte dalla console:

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

0

Ho scoperto che funziona meglio, se una qualsiasi immagine non riesce a caricarsi per la prima volta, viene completamente rimossa dal DOM. L'esecuzione console.clear()mantiene pulita la finestra della console, poiché gli errori 404 non possono essere omessi con blocchi try / catch.

$('img').one('error', function(err) {
    // console.log(JSON.stringify(err, null, 4))
    $(this).remove()
    console.clear()
})
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.