Rileva quando un'immagine non viene caricata in Javascript


104

C'è un modo per determinare se il percorso di un'immagine porta a un'immagine reale, cioè rilevare quando un'immagine non riesce a caricarsi in Javascript.

Per un'app Web, sto analizzando un file xml e creando dinamicamente immagini HTML da un elenco di percorsi di immagine. Alcuni percorsi delle immagini potrebbero non esistere più sul server, quindi desidero fallire con grazia rilevando quali immagini non vengono caricate ed eliminando quell'elemento HTML img.

Nota le soluzioni JQuery non potranno essere utilizzate (il capo non vuole usare JQuery, sì, lo so, non farmi iniziare). Conosco un modo in JQuery per rilevare quando un'immagine viene caricata, ma non se ha fallito.

Il mio codice per creare elementi img, ma come posso rilevare se il percorso img porta a un errore di caricamento dell'immagine?

var imgObj = new Image();  // document.createElement("img");
imgObj.src = src;

Questo potrebbe aiutarti: stackoverflow.com/questions/1977871/… (è jQuery, ma potrebbe comunque portarti sulla strada giusta)
Ben Lee

prova uno di questi google.com/…
ajax333221

2
Divertente @ ajax333221 questa stessa domanda è la prima nei risultati del tuo link :)
SSH questo

scrivi un selettore JQuery per trovare un nuovo capo ... Internet è un posto grande.
JJS

Risposte:


115

Potresti provare il seguente codice. Tuttavia, non posso garantire la compatibilità del browser, quindi dovrai provarlo.

function testImage(URL) {
    var tester=new Image();
    tester.onload=imageFound;
    tester.onerror=imageNotFound;
    tester.src=URL;
}

function imageFound() {
    alert('That image is found and loaded');
}

function imageNotFound() {
    alert('That image was not found.');
}

testImage("http://foo.com/bar.jpg");

E le mie simpatie per il capo resistente a jQuery!


2
Qualche idea se c'è un modo per sapere se incontra un reindirizzamento?
quickshiftin

4
Questo non funziona su webKit (Epiphany, Safari, ...) a seconda del server da cui stai ottenendo un'immagine. Ad esempio, a volte imgur non invia un'immagine esistente né uno stato 404 e in questo caso l'evento di errore non viene attivato.

Il problema con questo è il tuo messaggio, "Quell'immagine non è stata trovata". Il codice di risposta potrebbe essere 500 o 403 o qualcos'altro. Non sai che era un 404. In effetti non è molto probabile che sia un 404 poiché probabilmente non proveresti a caricare un'immagine che non c'è.
PHP Guru

1
dovrebbe aggiungere il gestore dell'evento invece dell'assegnazione diretta
cdalxndr

50

La risposta è carina, ma introduce un problema. Ogni volta che si assegna onloado onerrordirettamente, può sostituire la richiamata assegnata in precedenza. Ecco perché c'è un bel metodo che "registra l'ascoltatore specificato sull'EventTarget su cui è chiamato" come si dice su MDN . Puoi registrare tutti gli ascoltatori che desideri sullo stesso evento.

Lasciami riscrivere un po 'la risposta.

function testImage(url) {
    var tester = new Image();
    tester.addEventListener('load', imageFound);
    tester.addEventListener('error', imageNotFound);
    tester.src = url;
}

function imageFound() {
    alert('That image is found and loaded');
}

function imageNotFound() {
    alert('That image was not found.');
}

testImage("http://foo.com/bar.jpg");

Poiché il processo di caricamento delle risorse esterne è asincrono, sarebbe ancora più bello utilizzare JavaScript moderno con promesse, come le seguenti.

function testImage(url) {

    // Define the promise
    const imgPromise = new Promise(function imgPromise(resolve, reject) {

        // Create the image
        const imgElement = new Image();

        // When image is loaded, resolve the promise
        imgElement.addEventListener('load', function imgOnLoad() {
            resolve(this);
        });

        // When there's an error during load, reject the promise
        imgElement.addEventListener('error', function imgOnError() {
            reject();
        })

        // Assign URL
        imgElement.src = url;

    });

    return imgPromise;
}

testImage("http://foo.com/bar.jpg").then(

    function fulfilled(img) {
        console.log('That image is found and loaded', img);
    },

    function rejected() {
        console.log('That image was not found');
    }

);

2
forse mi manca qualcosa, ma come può l'assegnazione (tester.onload = ..) sostituire una richiamata, se il "tester" è stato appena creato? anche se qualche parte oscura del codice aggiunge un evento a ogni immagine creata, non è ovvio che vogliamo mantenere quegli eventi allo scopo di rilevare se esiste un'immagine.
jvilhena

1
Hai assolutamente ragione. In questo caso particolare potrebbe non essere un problema perché è molto probabile che non venga sostituito. Tuttavia, in generale, aggiungere un listener di eventi è una pratica migliore rispetto all'assegnazione diretta di un metodo.
emil.c

1
Le funzioni di @JohnWeisz Arrow sono anonime, quindi è più difficile eseguire il debug, usale con attenzione.
emil.c

10
@GifCo Non mi sento a mio agio a continuare questa discussione con te. Mi sento offeso dalla tua lingua. Se ritieni che la mia risposta sia errata o incompleta, suggerisci modifiche e spiega il tuo ragionamento. Se pensi che qualcosa sia una cattiva pratica, suggerisci una buona pratica con la tua risposta.
emil.c

2
Perché c'è una discussione sulle funzioni delle frecce qui? È fuori tema. La risposta va bene così com'è, soprattutto perché i vecchi browser non supportano le funzioni delle frecce (Internet Explorer ad esempio).
PHP Guru

29

Questo:

<img onerror="this.src='/images/image.png'" src="...">

1
Sebbene questo sia un attributo utile che può essere utilizzato come tecnica di failover dell'immagine, una risposta più dettagliata sarebbe utile per i lettori per comprenderla.
sorak

1
Così breve e così utile! Per nasconderlo:<img src="your/path/that/might/fail.png" onerror="$(this).hide();"/>
J0ANMM

2
Il commento di @ sorak mi ha fatto ridere con disgusto. Questa è la risposta corretta.
user3751385

@ J0ANMM Un altro modo per nascondere l'immagine è aggiungere un attributo alt vuoto: <img alt = "" src = "yourpicture.png">
Rick Glimmer

Eccellente! Invece di eseguire il failover su un'immagine, stampo questo testo onerror = "this.alt = 'Not Valid Image, Use Link ^'"
sudato

6
/**
 * Tests image load.
 * @param {String} url
 * @returns {Promise}
 */
function testImageUrl(url) {
  return new Promise(function(resolve, reject) {
    var image = new Image();
    image.addEventListener('load', resolve);
    image.addEventListener('error', reject);
    image.src = url;
  });
}

return testImageUrl(imageUrl).then(function imageLoaded(e) {
  return imageUrl;
})
.catch(function imageFailed(e) {
  return defaultImageUrl;
});

4

jQuery + CSS per img

Con jQuery questo funziona per me:

$('img').error(function() {
    $(this).attr('src', '/no-img.png').addClass('no-img');
});

E posso usare questa immagine ovunque sul mio sito web indipendentemente dalle sue dimensioni con la seguente proprietà CSS3:

img.no-img {
    object-fit: cover;
    object-position: 50% 50%;
}

SUGGERIMENTO 1: usa un'immagine quadrata di almeno 800 x 800 pixel.

SUGGERIMENTO 2: da utilizzare con ritratti di persone , utilizzareobject-position: 20% 50%;

CSS solo per background-img

Per le immagini di sfondo mancanti, ho anche aggiunto quanto segue in ogni background-imagedichiarazione:

background-image: url('path-to-image.png'), url('no-img.png');

NOTA: non funziona per immagini trasparenti.

Lato server Apache

Un'altra soluzione è rilevare l'immagine mancante con Apache prima di inviarla al browser e sostituirla con il contenuto no-img.png predefinito.

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_URI} /images/.*\.(gif|jpg|jpeg|png)$
RewriteRule .* /images/no-img.png [L,R=307]

3

Ecco una funzione che ho scritto per un'altra risposta: Javascript Image Url Verify . Non so se è esattamente quello che ti serve, ma utilizza le varie tecniche che si usa tra cui i gestori per onload, onerror, onaborte un timeout generale.

Poiché il caricamento dell'immagine è asincrono, chiami questa funzione con la tua immagine e poi chiama il tuo callback qualche tempo dopo con il risultato.


-19

proprio come sotto:

var img = new Image(); 
img.src = imgUrl; 

if (!img.complete) {

//has picture
}
else //not{ 

}

3
No. Questo potrebbe indurti a capire se l'immagine è memorizzata nella cache, in alcuni browser, ma senza un callback di caricamento, non stai aspettando nemmeno un momento per dare al browser la possibilità di avviare una richiesta HTTP per quell'immagine.
ChaseMoskal

questo è solo per dire qualcosa!
Hitmands

4
Il caricamento dell'immagine è asincrono.
emil.c

@ emil.c non è sempre, per qualche motivo. ChaseMoskal (che SO non mi consente di notificare ...) ha ragione: funziona solo se l'immagine termina il caricamento prima che l'esecuzione di JS continui, il che sembra accadere ogni volta che viene memorizzato nella cache.
Tristan
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.