Wykryj, kiedy obraz nie ładuje się w JavaScript

105

Czy istnieje sposób na określenie, czy ścieżka obrazu prowadzi do rzeczywistego obrazu, tj. Wykrywa, kiedy obraz nie ładuje się w JavaScript.

W przypadku aplikacji sieci Web analizuję plik xml i dynamicznie tworzę obrazy HTML z listy ścieżek obrazów. Niektóre ścieżki obrazów mogą już nie istnieć na serwerze, więc chcę z wdziękiem zawieść, wykrywając, które obrazy nie mogą się załadować i usuwając ten element HTML img.

Uwaga Rozwiązania JQuery nie będą mogły być używane (szef nie chce używać JQuery, tak, wiem, nie zaczynaj). Znam sposób w JQuery na wykrycie, kiedy obraz jest ładowany, ale nie wiem, czy się nie powiodło.

Mój kod do tworzenia elementów img, ale jak mogę wykryć, czy ścieżka img prowadzi do nieudanego załadowania obrazu?

var imgObj = new Image();  // document.createElement("img");
imgObj.src = src;
sazr
źródło
To może ci pomóc: stackoverflow.com/questions/1977871/… (to jQuery, ale nadal może zaprowadzić cię na właściwą ścieżkę)
Ben Lee
wypróbuj jedną z tych google.com/ ...
ajax333221
2
Funny @ ajax333221 to pytanie jest pierwsze w wynikach twojego linku :)
SSH This
napisz selektor JQuery, aby znaleźć nowego szefa ... Internet to duże miejsce.
JJS

Odpowiedzi:

116

Możesz wypróbować następujący kod. Nie mogę jednak ręczyć za zgodność przeglądarki, więc musisz to przetestować.

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

I moje współczucie dla szefa odpornego na jQuery!

Nikhil
źródło
2
Masz pomysł, czy istnieje sposób, aby sprawdzić, czy napotka przekierowanie?
quickshiftin
4
To nie działa w webKit (Epiphany, Safari, ...) w zależności od serwera, z którego otrzymujesz obraz. Na przykład czasami imgur nie wysyła obrazu, który istnieje, ani statusu 404, a zdarzenie błędu nie jest wyzwalane w tym przypadku.
Problem z tym związany jest z komunikatem „Nie znaleziono tego obrazu”. Kod odpowiedzi może mieć postać 500, 403 lub coś innego. Nie wiesz, że to 404. W rzeczywistości jest mało prawdopodobne, że to 404, ponieważ prawdopodobnie nie próbowałbyś załadować obrazu, którego tam nie ma.
PHP Guru
1
powinien dodać procedurę obsługi zdarzeń zamiast bezpośredniego przypisywania
cdalxndr
50

Odpowiedź jest ładna, ale wprowadza jeden problem. Zawsze, gdy przypisujesz onloadlub onerrorbezpośrednio, może to zastąpić wywołanie zwrotne, które zostało przypisane wcześniej. Dlatego istnieje fajna metoda, która „rejestruje określony odbiornik w EventTarget, do którego jest wywoływany”, jak to mówią w MDN . Możesz zarejestrować dowolną liczbę słuchaczy na to samo wydarzenie.

Przepiszę trochę odpowiedź.

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

Ponieważ proces ładowania zasobów zewnętrznych jest asynchroniczny, byłoby jeszcze przyjemniej używać nowoczesnego JavaScript z obietnicami, takimi jak poniższe.

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
źródło
2
może czegoś mi brakuje, ale jak przypisanie (tester.onload = ..) może zastąpić wywołanie zwrotne, jeśli właśnie utworzono „tester”? nawet jeśli jakaś niejasna część kodu dodaje zdarzenie do każdego utworzonego obrazu, nie jest oczywiste, że chcemy zachować te zdarzenia w celu wykrycia, czy obraz istnieje.
jvilhena
1
Masz całkowitą rację. W tym konkretnym przypadku może to nie stanowić problemu, ponieważ jest wysoce prawdopodobne, że nie zostanie wymieniony. Jednak generalnie dodanie detektora zdarzeń jest lepszą praktyką niż bezpośrednie przypisywanie metody.
emil.c
1
Funkcje @JohnWeisz Arrow są anonimowe, dlatego trudniej jest debugować, używaj ich ostrożnie.
emil.c
10
@GifCo Nie czuję się komfortowo kontynuując taką dyskusję z tobą. Czuję się urażony twoim językiem. Jeśli uważasz, że moja odpowiedź jest niepoprawna lub niekompletna, zaproponuj zmiany i wyjaśnij swoje uzasadnienie. Jeśli uważasz, że coś jest ZŁĄ praktyką, zasugeruj dobrą praktykę, podając własną odpowiedź.
emil.c
2
Dlaczego jest tu dyskusja na temat funkcji strzałek? To jest poza tematem. Odpowiedź jest w porządku, zwłaszcza, że ​​stare przeglądarki nie obsługują funkcji strzałek (na przykład Internet Explorer).
PHP Guru
30

To:

<img onerror="this.src='/images/image.png'" src="...">
Rafael Martins
źródło
1
Chociaż jest to przydatny atrybut, którego można użyć jako techniki przełączania awaryjnego obrazu, bardziej szczegółowa odpowiedź byłaby pomocna dla czytelników, aby ją zrozumieć.
sorak
2
Tak krótkie i tak przydatne! Aby to po prostu ukryć:<img src="your/path/that/might/fail.png" onerror="$(this).hide();"/>
J0ANMM
2
Komentarz @ sorak rozśmieszył mnie z niesmakiem. To jest poprawna odpowiedź.
user3751385
@ J0ANMM Innym sposobem na ukrycie obrazu jest dodanie pustego atrybutu alt: <img alt = "" src = "yourpicture.png">
Rick Glimmer
Doskonały! Zamiast przełączać się na obrazek, drukuję ten tekst onerror = "this.alt = 'Nieprawidłowy obraz, użyj linku ^'"
spocony
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
źródło
4

jQuery + CSS dla img

Z jQuery działa to dla mnie:

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

I mogę używać tego obrazu wszędzie w mojej witrynie, niezależnie od jej rozmiaru, z następującą właściwością CSS3:

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

WSKAZÓWKA 1: użyj kwadratowego obrazu o wymiarach co najmniej 800 x 800 pikseli.

WSKAZÓWKA 2: do portretu ludzi , użyjobject-position: 20% 50%;

CSS tylko dla background-img

W przypadku brakujących obrazów tła na każdej background-imagedeklaracji dodałem również następujące informacje :

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

UWAGA: nie działa dla przezroczystych obrazów.

Strona serwera Apache

Innym rozwiązaniem jest wykrycie brakującego obrazu za pomocą Apache przed wysłaniem go do przeglądarki i zastąpienie go domyślną zawartością no-img.png.

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

Oto funkcja, którą napisałem dla innej odpowiedzi: Javascript Image Url Verify . I nie wiem, czy to jest dokładnie to, co trzeba, ale używa różnych technik, które byłoby użyć które obejmują teleskopowe dla onload, onerror,onabort oraz ogólny limit czasu.

Ponieważ ładowanie obrazu jest asynchroniczne, wywołujesz tę funkcję ze swoim obrazem, a następnie wywołuje wywołanie zwrotne później z wynikiem.

jfriend00
źródło
-19

tak jak poniżej:

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

if (!img.complete) {

//has picture
}
else //not{ 

}
JamesChen
źródło
3
Nie. Może to podpowiedzieć ci, czy obraz jest przechowywany w pamięci podręcznej, w niektórych przeglądarkach - ale bez wywołania zwrotnego ładowania nie czekasz ani chwili, aby dać przeglądarce szansę na zainicjowanie żądania HTTP dla tego obrazu.
ChaseMoskal
to tylko po to, żeby coś powiedzieć!
Hitmands
4
Ładowanie obrazu jest asynchroniczne.
emil.c
@ emil.c z jakiegoś powodu nie zawsze. ChaseMoskal (którego SO nie pozwala mi powiadomić ...) ma rację - działa to tylko wtedy, gdy obraz zakończy się ładowaniem przed kontynuowaniem wykonywania JS, co wydaje się mieć miejsce za każdym razem, gdy jest buforowany.
tristan