Ho una pagina web che include un sacco di immagini. A volte l'immagine non è disponibile, quindi un'immagine difettosa viene visualizzata nel browser del client.
Come posso usare jQuery per ottenere il set di immagini, filtrarlo in immagini spezzate e sostituire lo src?
- Ho pensato che sarebbe stato più semplice farlo con jQuery, ma è risultato molto più semplice usare semplicemente una soluzione JavaScript pura, ovvero quella fornita da Prestaul.
Gestisci l'evento onError
per l'immagine per riassegnarne l'origine utilizzando 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:
Uso il gestore error
integrato:
$("img").error(function () {
$(this).unbind("error").attr("src", "broken.gif");
});
Modifica: Il metodo error()
è deprecato in 1.8 e versioni successive. Invece, dovresti usare .on("error")
invece:
$("img").one("error", function () {
$(this).attr("src", "broken.gif");
});
Nel caso qualcuno come me, prova ad associare l'evento error
a un tag img
HTML dinamico, vorrei sottolineare che c'è un problema:
A quanto pare, gli eventi di errore img
non creano bolle nella maggior parte dei browser, contrariamente a ciò che standard dice.
Quindi, qualcosa come il seguente non funziona :
$(document).on('error', 'img', function () { ... })
Spero che questo sarà utile a qualcun altro. Vorrei aver visto questo qui in questo thread. Ma non l'ho fatto. Quindi, lo sto aggiungendo
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';
}
});
});
Credo che questo sia quello che stai cercando: jQuery.Preload
Ecco il codice di esempio della demo, specifichi il caricamento e non trovi le immagini e sei tutto pronto:
$('#images img').preload({
placeholder:'placeholder.jpg',
notFound:'notfound.jpg'
});
$(window).bind('load', function() {
$('img').each(function() {
if((typeof this.naturalWidth != "undefined" &&
this.naturalWidth == 0 )
|| this.readyState == 'uninitialized' ) {
$(this).attr('src', 'missing.jpg');
}
}); })
Fonte: http://www.developria.com/2009/03/jquery-quickie---broken-images.html
Ecco un modo rapido e sporco per sostituire tutte le immagini rotte e non è necessario modificare il codice 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"),
});
}
});
Mentre l'OP stava cercando di sostituire l'SRC, sono sicuro che molte persone che hanno risposto a questa domanda potrebbero voler solo nascondere l'immagine spezzata, nel qual caso questa semplice soluzione ha funzionato alla grande per me:
<img src="someimage.jpg" onerror="this.style.display='none';" />
Non sono riuscito a trovare uno script adatto alle mie esigenze, quindi ho eseguito una funzione ricorsiva per verificare la presenza di immagini rotte e tentare di ricaricarle ogni quattro secondi finché non vengono riparate.
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);
});
Questa è una tecnica schifosa, ma è praticamente garantita:
<img ... onerror="this.parentNode.removeChild(this);">
Questo è JavaScript, dovrebbe essere compatibile con più browser e fornisce 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] );
}
Puoi utilizzare il recupero di GitHub per questo:
Frontend: https://github.com/github/fetch
o per Backend, una versione di 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 apparentemente è già stato in Chrome. A chiunque legga questo in futuro, potrebbe non essere necessario includere la suddetta libreria.
Se hai inserito img
con innerHTML
, come: $("div").innerHTML = <img src="wrong-uri">
, puoi caricare un'altra immagine se fallisce, ad es.
<script>
function imgError(img) {
img.error="";
img.src="valid-uri";
}
</script>
<img src="wrong-uri" onerror="javascript:imgError(this)">
Perché javascript: _
needed? Poiché gli script iniettati nel DOM tramite i tag di script in innerHTML
non vengono eseguiti al momento della loro immissione, quindi devi essere esplicito.
Ecco un esempio che utilizza l'oggetto Image HTML5 avvolto da JQuery. Chiamare la funzione di caricamento per l'URL dell'immagine principale e se tale carico causa 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>
Ho creato un violino per sostituire l'immagine rotta utilizzando l'evento "onerror". Questo potrebbe 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))
})
Usando la risposta di Prestaul , ho aggiunto alcuni assegni 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;
}
Meglio chiamare usando
jQuery(window).load(function(){
$.imgReload();
});
Perché l'uso di document.ready
non richiede necessariamente che le immagini vengano caricate, solo l'HTML. Quindi, non c'è bisogno di una chiamata ritardata.
CoffeeScript variante:
L'ho fatto per risolvere un problema con Turbolinks che a volte causa il metodo .error () in Firefox, anche se l'immagine è davvero lì.
$("img").error ->
e = $(@).get 0
$(@).hide() if !$.browser.msie && (typeof this.naturalWidth == "undefined" || this.naturalWidth == 0)
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 ha bisogno di 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à correttamente 'needed.png' nel mio src o 'no-image.png' dalla directory corrente.
Ho trovato questo post mentre guardavo quest'altro SO post . Qui sotto c'è 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 cercando una risposta allo stesso problema.
Quindi, se stai usando React, puoi fare qualcosa di simile al 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} />;
);
}
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" );
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');
}
});
}
Non sono sicuro che ci sia un modo migliore, ma posso pensare a un trucco per ottenerlo - potresti postare Ajax sull'URL img e analizzare la risposta per vedere se l'immagine è effettivamente tornata. Se è tornato come 404 o qualcosa del genere, quindi scambiare il img. Anche se mi aspetto che questo sia piuttosto lento.
Questo mi ha frustrato per anni. La mia correzione CSS imposta un'immagine di sfondo su img
. Quando un'immagine dinamica src
non viene caricata in primo piano, un segnaposto è visibile sul img
's bg. Questo funziona se le tue immagini hanno una dimensione predefinita (ad esempio height
, min-height
, width
e/o min-width
).
Vedrai l'icona dell'immagine spezzata ma è un miglioramento. Testato fino a IE9 con successo. iOS Safari e Chrome non mostrano nemmeno un'icona spezzata.
.dynamicContainer img {
background: url('/images/placeholder.png');
background-size: contain;
}
Aggiungi una piccola animazione per dare a src
il tempo di caricare senza uno sfarfallio di sfondo. Chrome si dissolve in background senza problemi, ma 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; }
}
Penso di avere un modo più elegante con la delega degli eventi e la cattura degli eventi su window
error
anche quando l'immagine di backup non viene caricata.
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 è :
Inserisci il codice in head
ed eseguito come primo script inline. Quindi ascolterà gli errori accaduti dopo la sceneggiatura.
Usa la cattura degli eventi per catturare gli errori, specialmente per quegli eventi che non hanno bolle.
Utilizza la delega degli eventi che evita gli eventi di associazione su ciascuna immagine.
Dare l'errore img
element un attributo dopo aver dato loro un backup.png
per evitare la scomparsa del backup.png
e del successivo ciclo infinito come di seguito:
errore img-> backup.png-> errore-> backup.png-> errore -> ,
Se l'immagine non può essere caricata (ad esempio, poiché non è presente nell'URL fornito), l'URL dell'immagine verrà modificato come predefinito,
Per ulteriori informazioni su . Error ()
$('img').on('error', function (e) {
$(this).attr('src', 'broken.png');
});
;(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 accedere a tutte le proprietà dall'oggetto immagine non riuscito tramite $*
:
$('img').fallback('http://dummyimage.com/$widthx$height&text=$src');
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';
}
});
A volte l'utilizzo dell'evento error
non è fattibile, ad es. perché stai provando a fare qualcosa su una pagina già caricata, ad esempio quando esegui il codice tramite la console, un bookmarklet o uno script caricato in modo asincrono. In tal caso, controllare che img.naturalWidth
e img.naturalHeight
siano 0 sembra fare il trucco.
Ad esempio, ecco uno snippet per ricaricare tutte le immagini danneggiate dalla console:
$$("img").forEach(img => {
if (!img.naturalWidth && !img.naturalHeight) {
img.src = img.src;
}
}
Ho trovato che funziona meglio, se una qualsiasi immagine non viene caricata la prima volta, viene completamente rimossa dal DOM. L'esecuzione di console.clear()
mantiene la finestra della console pulita, poiché gli errori 404 non possono essere omessi con i blocchi try/catch.
$('img').one('error', function(err) {
// console.log(JSON.stringify(err, null, 4))
$(this).remove()
console.clear()
})