Sprawdź, czy obraz nie jest załadowany (bez błędów) za pomocą jQuery

224

Używam JavaScript z biblioteką jQuery do manipulowania miniaturami obrazów zawartymi na nieuporządkowanej liście. Po załadowaniu obrazu robi jedną rzecz, a gdy pojawia się błąd, robi coś innego. Używam jQuery load()i error()metod jako zdarzeń. Po tych zdarzeniach sprawdzam element DOM obrazu dla .complete, aby upewnić się, że obraz nie został już załadowany, zanim jQuery nie będzie mogło zarejestrować zdarzeń.

Działa poprawnie, chyba że wystąpi błąd, zanim jQuery może zarejestrować zdarzenia. Jedyne rozwiązanie, jakie mogę wymyślić, to użyć onerroratrybutu img do przechowywania „flagi” gdzieś globalnie (lub w węźle, który jest sam), który mówi, że się nie udało, więc jQuery może sprawdzić ten „sklep / węzeł” podczas sprawdzania .complete.

Czy ktoś ma lepsze rozwiązanie?

Edycja: Pogrubione główne punkty i dodatkowe szczegóły poniżej: Sprawdzam, czy obraz jest kompletny (aka załadowany) PO dodaniu do obrazu zdarzenia obciążenia i błędu. W ten sposób, jeśli obraz zostanie załadowany przed zarejestrowaniem zdarzeń, nadal będę wiedział. Jeśli obraz nie zostanie załadowany po zdarzeniach, wydarzenia się nim zajmą. Problem polega na tym, że mogę łatwo sprawdzić, czy obraz jest już załadowany, ale nie wiem, czy zamiast tego wystąpił błąd.

William
źródło
Kiedy rejestrujesz zdarzenia jQuery? Może niektóre kody pomogą. :)
okw
Jest dużo kodu, co powiedziałem powyżej, jest po prostu bardzo prostą wersją tego, co robię. Wywołuję zdarzenia po załadowaniu DOM, wywoływana jest metoda dodawania zdarzeń do miniatur.
William

Odpowiedzi:

250

Inną opcją jest wyzwolenie zdarzeń onloadi / lub onerrorzdarzeń poprzez utworzenie elementu obrazu w pamięci i ustawienie jego srcatrybutu na oryginalny srcatrybut oryginalnego obrazu. Oto przykład tego, co mam na myśli:

$("<img/>")
    .on('load', function() { console.log("image loaded correctly"); })
    .on('error', function() { console.log("error loading image"); })
    .attr("src", $(originalImage).attr("src"))
;

Mam nadzieję że to pomoże!

Xavi
źródło
27
Nie działa tak dobrze, jak się spodziewałem. Wygląda na to, że w Google Chrome występują problemy, gdy obraz jest już w pamięci podręcznej. Nie wyzwala metody load (). :(
William
4
Ugg, masz rację. Chrome jest zdecydowanie najbardziej irytującą przeglądarką do opracowania. Jeśli chodzi o jasność, myślę, że mogłem znaleźć rozwiązanie: ustaw źródło obrazu na „”, a następnie z powrotem do oryginalnego źródła. Zaktualizuję moją odpowiedź.
Xavi
1
Należy umieścić .attrlinię po.load i .errorwierszy. W przeciwnym razie ryzykujesz załadowaniem obrazu (lub napotkaniem błędu) przed dodaniem tych wywołań zwrotnych i nie zostaną one wywołane.
callum
19
@Xavi Chrome nie jest najbardziej denerwującą przeglądarką, dla której warto opracować. Spróbuj opracować dla Internet Explorera 7 lub nowszego. Oprócz dodania parametru $ _GET do ładowania obrazu, ładuje nowy obraz za każdym razem, jak sugerował Gromix.
SSH,
10
.load()I .error()metody są mylące i teraz przestarzałe, stosowanie .on()i używać loadierror jak wydarzenia.
Firsh - LetsWP.io
235

Sprawdź completei naturalWidthwłaściwości w tej kolejności.

https://stereochro.me/ideas/detecting-broken-images-js

function IsImageOk(img) {
    // During the onload event, IE correctly identifies any images that
    // weren’t downloaded as not complete. Others should too. Gecko-based
    // browsers act like NS4 in that they report this incorrectly.
    if (!img.complete) {
        return false;
    }

    // However, they do have two very useful properties: naturalWidth and
    // naturalHeight. These give the true size of the image. If it failed
    // to load, either of these should be zero.
    if (img.naturalWidth === 0) {
        return false;
    }

    // No other way of checking: assume it’s ok.
    return true;
}
SLaks
źródło
1
Interesujące, wcześniej patrzyłem na ten post i nie zdawałem sobie sprawy, że oni to robią! kompletny dla IE, ponieważ naturalWidth nie jest zdefiniowany w IE. Zamierzam to sprawdzić teraz.
William
3
Najwyraźniej działa to niezawodnie tylko za pierwszym razem. Jeśli następnie zmienisz src obrazu, przynajmniej safari mobile będzie raportować pierwszą wartość zarówno dla naturalWidth, jak i dla zakończenia.
giorgian
5
return img.complete && typeof img.naturalWidth! = 'undefined' && img.naturalWidth! = 0;
Adrian Seeley
5
@ vsync prawdopodobnie zechcesz użyć tego jako jednorazowej kontroli, a jeśli obraz nie został jeszcze załadowany, skonfiguruj moduł obsługi zdarzeń „load”. Nie powinno być potrzeby młotkować procesora, uruchamiając to sprawdzenie wiele razy.
Michael Martin-Smucker
2
Dla ciekawskich jest to dokładnie to, co robi biblioteka imagesLoaded, do której prowadzi link poniżej. Tak więc, dla jednorazowego użytku, idź na to: github.com/desandro/imagesloaded/blob/master/…
cincodenada
57

W oparciu o moją wiedzę na temat specyfikacji HTML W3C dla tego imgelementu , powinieneś być w stanie to zrobić za pomocą kombinacji atrybutów completei naturalHeight, na przykład:

function imgLoaded(imgElement) {
  return imgElement.complete && imgElement.naturalHeight !== 0;
}

Ze specyfikacji dla completeatrybutu:

Kompletny atrybut IDL musi zwracać wartość true, jeśli spełniony jest jeden z następujących warunków:

  • Atrybut src został pominięty.
  • Ostatnie zadanie umieszczone w kolejce przez źródło zadań sieciowych po pobraniu zasobu zostało umieszczone w kolejce.
  • Element img jest całkowicie dostępny.
  • Element img jest uszkodzony.

W przeciwnym razie atrybut musi zwracać wartość false.

Zasadniczo completezwraca wartość true, jeśli obraz zakończył ładowanie lub ładowanie się nie powiodło. Ponieważ chcemy tylko przypadku, w którym obraz został pomyślnie załadowany, musimy również sprawdzić nauturalHeightatrybut:

IDL atrybuty naturalWidthi naturalHeightmusi wrócić wewnętrzną szerokość i wysokość obrazu w pikselach CSS, jeżeli obraz jest dostępny, albo 0.

I availablejest tak zdefiniowane:

Img jest zawsze w jednym z następujących stanów:

  • Niedostępny - agent użytkownika nie uzyskał żadnych danych obrazu.
  • Częściowo dostępne - agent użytkownika uzyskał niektóre dane obrazu.
  • Całkowicie dostępne - agent użytkownika uzyskał wszystkie dane obrazu i dostępne są przynajmniej wymiary obrazu.
  • Zepsuty - klient użytkownika uzyskał wszystkie dane obrazu, które może, ale nie może nawet zdekodować obrazu wystarczająco, aby uzyskać wymiary obrazu (np. Obraz jest uszkodzony, format nie jest obsługiwany lub nie można uzyskać danych) .

Kiedy element img jest w stanie częściowo dostępnym lub całkowicie dostępnym, mówi się, że jest dostępny.

Więc jeśli obraz jest „uszkodzony” (nie udało się załadować), to będzie w stanie uszkodzonym, a nie w stanie dostępnym, więc naturalHeightbędzie wynosił 0.

Dlatego sprawdzenie imgElement.complete && imgElement.naturalHeight !== 0powinno nam powiedzieć, czy obraz został pomyślnie załadowany.

Możesz przeczytać więcej na ten temat w specyfikacji HTML W3C dla tego imgelementu lub w MDN .

Ajedi32
źródło
1
Nie działa obraz jest ładowany ze stylem „content: url”
deathangel908
1
Wydaje się, że to nie działa w przeglądarce Firefox ze zdjęciami SVG, ponieważ przeglądarka zgłasza, że ​​naturalna wysokość / szerokość wynosi 0. Istotny błąd: bugzilla.mozilla.org/show_bug.cgi?id=1328124
lee
20

Próbowałem na wiele różnych sposobów i ten sposób był dla mnie jedynym

//check all images on the page
$('img').each(function(){
    var img = new Image();
    img.onload = function() {
        console.log($(this).attr('src') + ' - done!');
    }
    img.src = $(this).attr('src');
});

Możesz także dodać funkcję wywołania zwrotnego uruchamianą, gdy wszystkie obrazy zostaną załadowane do DOM i gotowe. Dotyczy to również dynamicznie dodawanych obrazów. http://jsfiddle.net/kalmarsh80/nrAPk/

Kal
źródło
Uszkodzony link jsfiddle
Alex Karshin
1
Testowałem ciekawe odczyty powyżej i nie udaje im się między buforowanymi / niebuforowanymi testami w kilku popularnych przeglądarkach. Ta odpowiedź jest podobna do innej, ale jest to ta, którą właśnie wdrożyłem i przetestowałem. Mogę potwierdzić, że działa na: EdgeWin10, IE11Win8.1, Win7IE10, Win7IE9, iOS 10.2 Chrome, iOS 10.2 Safari, mac os 10.12 Chrome, mac os 10.12 Safari, Mac OS 10.12 Firefox, Google Pixel 7.1, Samsung S5 Android 4.4. Nie zapomnij także użyć addEventListener / removeEventListener: D
danjah
13

Użyj imagesLoadedbiblioteki javascript .

Można go używać ze zwykłym JavaScript i jako wtyczką jQuery.

Cechy:

Zasoby

Josh Harrison
źródło
To działało idealnie dla mnie. function checkImages (imgs) {$ (imgs) .each (function () {$ (imgs) .imagesLoaded () .progress (function (instance, image) {if (image.isLoaded == false) {console.log (image .img.src + 'nie można znaleźć.'); image.img.src = "/templates/img/no-image.jpg";}});}); }
Rooster242,
1
wydaje się, że ma mnóstwo otwartych problemów, które są poważnymi błędami, a deweloper nie wydaje się, aby nadrobił zaległości. obecnie jest to całkowicie bezużyteczne w środowisku produkcyjnym.
vsync
3
@vsync Czy czytałeś niektóre z otwartych problemów? Reaguje na prawie wszystkie z nich, a większość z nich wydaje się być niemożliwymi do odtworzenia przypadkowymi przypadkami, na które napotyka niewielu innych. Nigdy nie miałem z tym problemu w produkcji.
Josh Harrison
1
tak, używam go sam, a czasem to nie działa, więc używam setTimeout, aby objąć przypadki, w których się nie udaje
vsync
Istnieją dobre powody, dla których należy użyć wtyczki imagesLoaded: completewłaściwość nie jest wyraźnie obsługiwana: nie można znaleźć wiarygodnego źródła informacji dotyczących obsługi przeglądarki. completewłaściwość również nie wydaje się mieć jasnej specyfikacji: specyfikacja HTML5 jest jedyną, która o niej wspomina i wciąż jest w wersji roboczej w momencie pisania. Jeśli używasz naturalWidthi naturalHeightmasz tylko obsługę IE9 +, więc jeśli używasz go, aby dowiedzieć się, czy obraz jest załadowany, potrzebujesz trochę „sprytnej” sztuczki, aby działał na starych przeglądarkach, i musisz sprawdzić, czy zachowanie w różnych przeglądarkach jest spójne
Adrien Bądź
8

Pobierz informacje z elementów obrazu na stronie
Testuj działanie na Chrome i Firefox
Działający jsFiddle (otwórz konsolę, aby zobaczyć wynik)

$('img').each(function(){ // selecting all image element on the page

    var img = new Image($(this)); // creating image element

    img.onload = function() { // trigger if the image was loaded
        console.log($(this).attr('src') + ' - done!');
    }

    img.onerror = function() { // trigger if the image wasn't loaded
        console.log($(this).attr('src') + ' - error!');
    }

    img.onAbort = function() { // trigger if the image load was abort
        console.log($(this).attr('src') + ' - abort!');
    }

    img.src = $(this).attr('src'); // pass src to image object

    // log image attributes
    console.log(img.src);
    console.log(img.width);
    console.log(img.height);
    console.log(img.complete);

});

Uwaga: Użyłem jQuery , myślałem, że może to być skuteczne przy pełnym javascript

Znajduję tutaj dobre informacje OpenClassRoom -> to francuskie forum

Julha
źródło
3

Detektor sieci w czasie rzeczywistym - sprawdź stan sieci bez odświeżania strony: (to nie jest jquery, ale przetestowane, i działa w 100%: (testowane w przeglądarce Firefox v25.0)

Kod:

<script>
 function ImgLoad(myobj){
   var randomNum = Math.round(Math.random() * 10000);
   var oImg=new Image;
   oImg.src="YOUR_IMAGELINK"+"?rand="+randomNum;
   oImg.onload=function(){alert('Image succesfully loaded!')}
   oImg.onerror=function(){alert('No network connection or image is not available.')}
}
window.onload=ImgLoad();
</script>

<button id="reloadbtn" onclick="ImgLoad();">Again!</button>

w przypadku utraty połączenia wystarczy nacisnąć przycisk Ponownie.

Aktualizacja 1: Automatyczne wykrywanie bez odświeżania strony:

<script>
     function ImgLoad(myobj){
       var randomNum = Math.round(Math.random() * 10000);
       var oImg=new Image;
       oImg.src="YOUR_IMAGELINK"+"?rand="+randomNum;
       oImg.onload=function(){networkstatus_div.innerHTML="";}
       oImg.onerror=function(){networkstatus_div.innerHTML="Service is not available. Please check your Internet connection!";}
}

networkchecker = window.setInterval(function(){window.onload=ImgLoad()},1000);
</script>

<div id="networkstatus_div"></div>
Jenei Tamás
źródło
100%! Co się stanie, jeśli łącze do obrazu zostanie zablokowane przez serwer proxy / zaporę lub serwer obrazów nie odpowiada? Sieć wciąż działa :)
Sen Jacob
Spowoduje to ponowne załadowanie każdego obrazu z serwera, niszcząc pamięć podręczną (celowo). Więc dla sprawdzenia, (przynajmniej) podwajasz przepustowość dla wszystkich swoich zdjęć. Poza tym sprawdza tylko, czy obraz istnieje na serwerze, a nie czy został załadowany w określonym miejscu. Również nie ma sposobu, aby wiedzieć, kiedy każdy obraz jest ładowany.
Marius Balčytis
3

Po przeczytaniu interesujących rozwiązań na tej stronie, stworzyłem łatwe w użyciu rozwiązanie, na które duży wpływ ma post SLaksa i Noyo, który wydaje się działać na całkiem nowych wersjach (w momencie pisania) Chrome, IE, Firefox, Safari i Opera (wszystkie w systemie Windows). Ponadto działał na emulatorze iPhone / iPad, którego użyłem.

Jedną z głównych różnic między tym rozwiązaniem a SLaksem i postem Noyo jest to, że to rozwiązanie sprawdza głównie właściwości naturalWidth i naturalHeight. Przekonałem się, że w obecnych wersjach przeglądarki te dwie właściwości wydają się zapewniać najbardziej pomocne i spójne wyniki.

Ten kod zwraca PRAWDA, gdy obraz został w pełni załadowany ORAZ pomyślnie. Zwraca FAŁSZ, gdy obraz nie został jeszcze w pełni załadowany LUB LUB nie udało się załadować.

Jedną z rzeczy, o których musisz wiedzieć, jest to, że ta funkcja zwróci również FAŁSZ, jeśli obraz ma rozmiar piksela 0x0. Ale te obrazy są dość rzadkie i nie mogę wymyślić bardzo przydatnego przypadku, w którym chciałbyś sprawdzić, czy załadowano jeszcze obraz piksela 0x0 :)

Najpierw dołączamy nową funkcję o nazwie „isLoaded” do prototypu HTMLImageElement, dzięki czemu można jej użyć w dowolnym elemencie obrazu.

HTMLImageElement.prototype.isLoaded = function() {

    // See if "naturalWidth" and "naturalHeight" properties are available.
    if (typeof this.naturalWidth == 'number' && typeof this.naturalHeight == 'number')
        return !(this.naturalWidth == 0 && this.naturalHeight == 0);

    // See if "complete" property is available.
    else if (typeof this.complete == 'boolean')
        return this.complete;

    // Fallback behavior: return TRUE.
    else
        return true;

};

Następnie, za każdym razem, gdy musimy sprawdzić status ładowania obrazu, wywołujemy funkcję „isLoaded”.

if (someImgElement.isLoaded()) {
    // YAY! The image loaded
}
else {
    // Image has not loaded yet
}

Według komentarza giorgiana do postów SLaks i Noyo, to rozwiązanie prawdopodobnie może być użyte jako jednorazowa kontrola w Safari Mobile, jeśli planujesz zmienić atrybut SRC. Można jednak obejść ten problem, tworząc element obrazu z nowym atrybutem SRC zamiast zmieniać atrybut SRC na istniejącym elemencie obrazu.

data-dan
źródło
2

Oto jak dostałem go do pracy w różnych przeglądarkach, używając kombinacji powyższych metod (musiałem także dynamicznie wstawiać obrazy do domeny):

$('#domTarget').html('<img src="" />');

var url = '/some/image/path.png';

$('#domTarget img').load(function(){}).attr('src', url).error(function() {
    if ( isIE ) {
       var thisImg = this;
       setTimeout(function() {
          if ( ! thisImg.complete ) {
             $(thisImg).attr('src', '/web/css/img/picture-broken-url.png');
          }
       },250);
    } else {
       $(this).attr('src', '/web/css/img/picture-broken-url.png');
    }
});

Uwaga: Musisz podać poprawny stan logiczny dla zmiennej isIE.

Justin Vincent
źródło
2
var isImgLoaded = function(imgSelector){
  return $(imgSelector).prop("complete") && $(imgSelector).prop("naturalWidth") !== 0;
}

// Lub jako wtyczka

    $.fn.extend({
      isLoaded: function(){
        return this.prop("complete") && this.prop("naturalWidth") !== 0;
      }
    })

// $(".myImage").isLoaded() 
KhaledDev
źródło
1

Jak rozumiem, właściwość .complete jest niestandardowa. To może nie być uniwersalne ... Widzę, że wydaje się działać inaczej w wersetach Firefox IE. Ładuję kilka obrazów do javascript, a następnie sprawdzam, czy są kompletne. W Firefoksie wydaje się, że działa to świetnie. W IE nie dzieje się tak, ponieważ obrazy wydają się ładować w innym wątku. Działa tylko wtedy, gdy opóźnię moje zadanie do image.src i sprawdzę właściwość image.complete.

Użycie image.onload i image.onerror też nie działa, ponieważ muszę przekazać parametr, aby wiedzieć, o którym obrazku mówię, kiedy funkcja jest wywoływana. Jakikolwiek sposób robienia tego wydaje się zawodzić, ponieważ wydaje się, że faktycznie spełnia tę samą funkcję, a nie różne wystąpienia tej samej funkcji. Tak więc wartość, którą przekazuję, aby zidentyfikować obraz, zawsze jest ostatnią wartością w pętli. Nie mogę wymyślić żadnego sposobu na obejście tego problemu.

W Safari i Chrome widzę image.complete true i naturalWidth ustawione, nawet gdy konsola błędów pokazuje 404 dla tego obrazu ... i celowo usunąłem ten obraz, aby to przetestować. Ale powyższe działa dobrze dla Firefox i IE.

użytkownik1018645
źródło
Hmm, to interesujące. Czy mógłbyś opublikować przykładowy kod, więc chcę wypróbować kilka rzeczy?
Xavi
1

Ten fragment kodu pomógł mi naprawić problemy z buforowaniem przeglądarki:

$("#my_image").on('load', function() {

    console.log("image loaded correctly"); 
}).each(function() {

     if($(this).prop('complete')) $(this).load();
});

Gdy pamięć podręczna przeglądarki jest wyłączona, tylko ten kod nie działa:

$("#my_image").on('load', function() {
     console.log("image loaded correctly"); 
})

aby działało, musisz dodać:

.each(function() {
     if($(this).prop('complete')) $(this).load();
});
Szczery
źródło
0

Za pomocą tego JavaScriptkodu możesz sprawdzić, czy obraz został pomyślnie załadowany, czy nie.

document.onready = function(e) {
        var imageobj = new Image();
        imageobj.src = document.getElementById('img-id').src;
        if(!imageobj.complete){ 
            alert(imageobj.src+"  -  Not Found");
        }
}

Wypróbuj to

Ajay Gupta
źródło
0

Miałem wiele problemów z pełnym ładowaniem obrazu i EventListener.

Cokolwiek próbowałem, wyniki nie były wiarygodne.

Ale potem znalazłem rozwiązanie. Technicznie nie jest to przyjemne, ale teraz nigdy nie miałem nieudanego ładowania obrazu.

Co ja zrobiłem:

                    document.getElementById(currentImgID).addEventListener("load", loadListener1);
                    document.getElementById(currentImgID).addEventListener("load", loadListener2);

                function loadListener1()
                    {
                    // Load again
                    }

                function loadListener2()
                {
                    var btn = document.getElementById("addForm_WithImage"); btn.disabled = false;
                    alert("Image loaded");
                }

Zamiast wczytywać obraz za jednym razem, ładuję go po raz drugi bezpośrednio za pierwszym razem i oba uruchamiam w module obsługi zdarzeń.

Wszystkie moje bóle głowy zniknęły!


Nawiasem mówiąc: Wy z stackoverflow pomogliście mi już ponad sto razy. Za to bardzo dziękuję!

Scoobeedo Cool
źródło
0

Ten działał dla mnie dobrze :)

$('.progress-image').each(function(){
    var img = new Image();
    img.onload = function() {
        let imgSrc = $(this).attr('src');
        $('.progress-image').each(function(){
            if($(this).attr('src') == imgSrc){
                console.log($(this).parent().closest('.real-pack').parent().find('.shimmer').fadeOut())
            }
        })
    }
    img.src = $(this).attr('src');
});
Kannan Uthaman
źródło