Détecter quand une image ne se charge pas en Javascript

105

Existe-t-il un moyen de déterminer si un chemin d'image mène à une image réelle, c'est-à-dire de détecter lorsqu'une image ne se charge pas en Javascript.

Pour une application Web, j'analyse un fichier xml et je crée dynamiquement des images HTML à partir d'une liste de chemins d'images. Certains chemins d'image peuvent ne plus exister sur le serveur, je souhaite donc échouer en douceur en détectant les images qui ne parviennent pas à se charger et en supprimant cet élément HTML img.

Remarque Les solutions JQuery ne pourront pas être utilisées (le patron ne veut pas utiliser JQuery, oui je sais ne pas me lancer). Je connais un moyen dans JQuery de détecter quand une image est chargée, mais pas si elle a échoué.

Mon code pour créer des éléments img mais comment puis-je détecter si le chemin img conduit à l'échec du chargement de l'image?

var imgObj = new Image();  // document.createElement("img");
imgObj.src = src;
sazr
la source
Cela pourrait vous aider: stackoverflow.com/questions/1977871/… (c'est jQuery, mais cela peut toujours vous conduire sur la bonne voie)
Ben Lee
essayez l'un de ces google.com
...
2
Drôle @ ajax333221 cette question est la première dans les résultats de votre lien :)
SSH ce
écrivez un sélecteur JQuery pour trouver un nouveau Boss ... Internet est une grande place.
JJS

Réponses:

116

Vous pouvez essayer le code suivant. Cependant, je ne peux pas garantir la compatibilité du navigateur, vous devrez donc le tester.

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

Et mes sympathies pour le boss résistant à jQuery!

Nikhil
la source
2
Une idée s'il y a un moyen de savoir s'il rencontre une redirection?
quickshift du
4
Cela ne fonctionne pas sur webKit (Epiphany, Safari, ...) selon le serveur à partir duquel vous obtenez une image. Par exemple, parfois imgur n'envoie pas une image qui existe ni un statut 404 et l'événement d'erreur n'est pas déclenché dans ce cas.
Le problème avec ceci est votre message, "Cette image n'a pas été trouvée." Le code de réponse peut être un 500 ou 403 ou autre chose. Vous ne savez pas que c'était un 404. En fait, il est peu probable que ce soit un 404 puisque vous n'essaieriez probablement pas de charger une image qui n'est pas là.
PHP Guru
1
devrait ajouter un gestionnaire d'événements au lieu de l'
attribution
50

La réponse est agréable, mais elle pose un problème. Chaque fois que vous attribuez onloadou onerrordirectement, il peut remplacer le rappel qui a été attribué précédemment. C'est pourquoi il existe une méthode intéressante qui "enregistre l'auditeur spécifié sur le EventTarget sur lequel il est appelé" comme on dit sur MDN . Vous pouvez enregistrer autant d'auditeurs que vous le souhaitez sur le même événement.

Permettez-moi de réécrire un peu la réponse.

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

Étant donné que le processus de chargement des ressources externes est asynchrone, il serait encore plus agréable d'utiliser du JavaScript moderne avec des promesses, telles que les suivantes.

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

);
emil.c
la source
2
peut-être qu'il me manque quelque chose, mais comment l'affectation (tester.onload = ..) peut-elle remplacer un rappel, si le "testeur" vient d'être créé? même si une partie obscure du code ajoute un événement à chaque image créée, il n'est pas évident que nous souhaitons conserver ces événements dans le but de détecter si une image existe.
jvilhena
1
Vous avez tout à fait raison. Dans ce cas particulier, cela peut ne pas être un problème car il est fort probable qu'il ne sera pas remplacé. Cependant, en général, l'ajout d'un écouteur d'événements est une meilleure pratique que l'affectation directe d'une méthode.
emil.c
1
Les fonctions @JohnWeisz Arrow sont anonymes, il est donc plus difficile de déboguer, utilisez-les avec précaution.
emil.c
10
@GifCo Je ne me sens pas à l'aise de continuer cette discussion avec vous. Je me sens offensé par votre langue. Si vous pensez que ma réponse est incorrecte ou incomplète, suggérez des changements et expliquez votre raisonnement. Si vous pensez que quelque chose est une mauvaise pratique, suggérez une bonne pratique avec votre propre réponse.
emil.c
2
Pourquoi y a-t-il une discussion sur les fonctions des flèches ici? C'est hors sujet. La réponse est correcte, d'autant plus que les anciens navigateurs ne prennent pas en charge les fonctions fléchées (Internet Explorer par exemple).
PHP Guru
29

Ce:

<img onerror="this.src='/images/image.png'" src="...">
Rafael Martins
la source
1
Bien qu'il s'agisse d'un attribut utile pouvant être utilisé comme technique de basculement d'image, une réponse plus détaillée serait utile pour que les lecteurs le comprennent.
sorak
1
Si court et si utile! Pour simplement le cacher:<img src="your/path/that/might/fail.png" onerror="$(this).hide();"/>
J0ANMM
2
Le commentaire de @ sorak m'a fait rire de dégoût. C'est la bonne réponse.
user3751385
@ J0ANMM Une autre façon de masquer l'image consiste à ajouter un attribut alt vide: <img alt = "" src = "yourpicture.png">
Rick Glimmer
Excellent! Au lieu de basculer vers une image, j'imprime ce texte onerror = "this.alt = 'Not Valid Image, Use Link ^'"
sueur
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;
});
Holmberd
la source
4

jQuery + CSS pour img

Avec jQuery, cela fonctionne pour moi:

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

Et je peux utiliser cette image partout sur mon site Web quelle que soit sa taille avec la propriété CSS3 suivante:

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

CONSEIL 1: utilisez une image carrée d'au moins 800 x 800 pixels.

CONSEIL 2: pour une utilisation avec un portrait de personnes , utilisezobject-position: 20% 50%;

CSS uniquement pour background-img

Pour les images d'arrière-plan manquantes, j'ai également ajouté ce qui suit sur chaque background-imagedéclaration:

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

REMARQUE: ne fonctionne pas pour les images transparentes.

Côté serveur Apache

Une autre solution consiste à détecter l'image manquante avec Apache avant de l'envoyer au navigateur et à la remplacer par le contenu par défaut no-img.png.

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

Voici une fonction que j'ai écrite pour une autre réponse: Javascript Image Url Verify . Je ne sais pas si c'est exactement ce dont vous avez besoin, mais il utilise les différentes techniques que vous utiliseriez qui comprennent des gestionnaires pour onload, onerror,onabort et un délai d' attente général.

Le chargement de l'image étant asynchrone, vous appelez cette fonction avec votre image, puis elle appelle votre rappel un peu plus tard avec le résultat.

jfriend00
la source
-19

comme ci-dessous:

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

if (!img.complete) {

//has picture
}
else //not{ 

}
JamesChen
la source
3
Nan. Cela peut vous indiquer si l'image est mise en cache ou non, dans certains navigateurs - mais sans rappel de charge, vous n'attendez même pas un instant pour donner au navigateur une chance d'initier une requête HTTP pour cette image.
ChaseMoskal
c'est juste pour dire quelque chose!
Hitmands
4
Le chargement de l'image est asynchrone.
emil.c
@ emil.c ce n'est pas toujours, pour une raison quelconque. ChaseMoskal (que SO ne me laisse pas notifier ...) a raison - cela ne fonctionne que si l'image finit de se charger avant que l'exécution de JS ne continue, ce qui semble se produire chaque fois qu'elle est mise en cache.
tristan le