Jak mogę sprawdzić, czy obraz został załadowany, używając Javascript / jQuery?

85

Piszę trochę JavaScript, aby zmienić rozmiar dużego obrazu, aby zmieścił się w oknie przeglądarki użytkownika. (Niestety nie kontroluję rozmiaru obrazów źródłowych).

Więc coś takiego byłoby w HTML:

<img id="photo"
     src="a_really_big_file.jpg"
     alt="this is some alt text"
     title="this is some title text" />

Czy istnieje sposób, aby sprawdzić, czy srcobraz w imgtagu został pobrany?

Potrzebuję tego, ponieważ mam problem, jeśli $(document).ready()jest wykonywany przed załadowaniem obrazu przez przeglądarkę. $("#photo").width()i $("#photo").height()zwróci rozmiar symbolu zastępczego (tekst alternatywny). W moim przypadku jest to około 134 x 20.

W tej chwili sprawdzam tylko, czy wysokość zdjęcia jest mniejsza niż 150 i zakładam, że jeśli tak, to tylko tekst alternatywny. Ale to niezły hack i zepsuje się, jeśli zdjęcie ma mniej niż 150 pikseli wysokości (mało prawdopodobne w moim przypadku) lub jeśli tekst alternatywny ma więcej niż 150 pikseli wysokości (może się to zdarzyć w małym oknie przeglądarki) .


Edycja: dla każdego, kto chce zobaczyć kod:

$(function()
{
  var REAL_WIDTH = $("#photo").width();
  var REAL_HEIGHT = $("#photo").height();

  $(window).resize(adjust_photo_size);
  adjust_photo_size();

  function adjust_photo_size()
  {
    if(REAL_HEIGHT < 150)
    {
      REAL_WIDTH = $("#photo").width();
      REAL_HEIGHT = $("#photo").height();
      if(REAL_HEIGHT < 150)
      {
        //image not loaded.. try again in a quarter-second
        setTimeout(adjust_photo_size, 250);
        return;
      }
    }

    var new_width = . . . ;
    var new_height = . . . ;

    $("#photo").width(Math.round(new_width));
    $("#photo").height(Math.round(new_height));
  }

});

Aktualizacja : Dzięki za sugestie. Istnieje ryzyko, że zdarzenie nie zostanie uruchomione, jeśli ustawię wywołanie zwrotne dla $("#photo").loadzdarzenia, więc zdefiniowałem zdarzenie onLoad bezpośrednio w tagu obrazu. Dla przypomnienia, oto kod, z którym skończyłem:

<img id="photo"
     onload="photoLoaded();"
     src="a_really_big_file.jpg"
     alt="this is some alt text"
     title="this is some title text" />

Następnie w Javascript:

//This must be outside $() because it may get called first
var isPhotoLoaded = false;
function photoLoaded()
{
  isPhotoLoaded = true;
}

$(function()
{
  //Hides scrollbars, so we can resize properly.  Set with JS instead of
  //  CSS so that page doesn't break with JS disabled.
  $("body").css("overflow", "hidden");

  var REAL_WIDTH = -1;
  var REAL_HEIGHT = -1;

  $(window).resize(adjust_photo_size);
  adjust_photo_size();

  function adjust_photo_size()
  {
    if(!isPhotoLoaded)
    {
      //image not loaded.. try again in a quarter-second
      setTimeout(adjust_photo_size, 250);
      return;
    }
    else if(REAL_WIDTH < 0)
    {
      //first time in this function since photo loaded
      REAL_WIDTH = $("#photo").width();
      REAL_HEIGHT = $("#photo").height();
    }

    var new_width = . . . ;
    var new_height = . . . ;

    $("#photo").width(Math.round(new_width));
    $("#photo").height(Math.round(new_height));
  }

});
Wyrko
źródło
1
To jest tak stare i już zaakceptowałeś odpowiedź, więc tylko skomentuję tutaj. Dlaczego nie możesz użyć wtyczki jQuery „onImagesLoad”? A także, jQuery czy nie, co jest złego w ustawianiu max-widthi max-heightstylizowaniu obrazu za pomocą JavaScript? W ten sposób unikniesz CAŁEGO kodu, który musiałeś napisać, ustawiając maksymalną szerokość / wysokość na rozmiar szerokości / wysokości widoku.
@ jon.wd7: Nie jestem zaznajomiony z tą wtyczką i mogła nie istnieć już w '08. jeśli chodzi o max-width i max-height, dwie rzeczy: 1) nie są obsługiwane w IE (cóż, nie jestem pewien co do IE 8); 2) jeśli obszar roboczy zmieni rozmiar (rozmiar okna zostanie zmieniony itp.), To nadal potrzebowałbym JavaScript, aby zmienić maksymalną szerokość / maksymalną wysokość (chociaż mogę nie, jeśli użyję „100%” zamiast pomiaru pikseli)
Kip
Zdecydowanie jest obsługiwany w IE7 i IE8, zgodnie z quirksmode.org. Więc nie jestem pewien twojego problemu. Osobiście nie obsługuję IE6 w takich drobiazgach, więc zobaczą to samo, co zobaczy użytkownik bez włączonego JS. I oczywiście będziesz musiał zresetować max-widthi max-heightzmienić rozmiar. Ale to dość łatwe zadanie, .resize()w rzeczywistości jedno liniowiec .
3
Właściwość complete daje konieczny sposób sprawdzenia, czy obraz został załadowany.
BorisOkunskiy
1
@Adrien, Mozilla jest teraz wyświetlana jako kompletna, jako obsługiwana przez wszystkie główne przeglądarki, z wyjątkiem Andoid (nieznane).
Oliver Bock

Odpowiedzi:

33

Dodaj detektor zdarzeń lub poproś, aby obraz ogłosił się za pomocą funkcji onload. Następnie ustal wymiary stamtąd.

<img id="photo"
     onload='loaded(this.id)'
     src="a_really_big_file.jpg"
     alt="this is some alt text"
     title="this is some title text" />
Diodeus - James MacFarlane
źródło
Zgodnie ze specyfikacją onload jest tylko zdarzeniem dla elementów body i frameset; to powiedziawszy, myślę, że to działa w IE, ale nie wiem, czy działa w innych przeglądarkach ani że jest to coś, co możesz założyć ... Właśnie się temu przyglądałem kilka tygodni temu ...
Jason Bunting
Przetestowałem go w IE6, IE7, FF3, Opera9, Safair (Windows Beta) i Chrome (Windows Beta).
Kip
9
Odradza się wysyłanie wiadomości i treści. Zdarzenia należy stosować przy użyciu JavaScript, a nie HTML.
dionyziz
10
Jego komentarz JEST istotny, ponieważ nie utknęliśmy w 2008 roku. Ludzie nadal to czytają i nawet jeśli nie uważano tego za złą praktykę w 08, nie chcesz, aby ludzie myśleli teraz, że jest to dobra praktyka.
Spoeken
3
document.querySelector ("img"). addEventListener ("load", function () {alert ('onload!');});
Frank Schwieterman
14

Korzystając z magazynu danych jquery można zdefiniować stan „załadowany”.

<img id="myimage" onload="$(this).data('loaded', 'loaded');" src="lolcats.jpg" />

Następnie możesz zrobić gdzie indziej:

if ($('#myimage').data('loaded')) {
    // loaded, so do stuff
}
Mike Fogel
źródło
2
Użyj jQuery(this)zamiast $(this)dla lepszej kompatybilności z innymi bibliotekami (jquery nie jest właścicielem $), zwłaszcza gdy masz JavaScript w html.
John Magnolia
11

Prawidłową odpowiedzią jest użycie event.special.load

Możliwe, że zdarzenie ładowania nie zostanie wywołane, jeśli obraz zostanie załadowany z pamięci podręcznej przeglądarki. Aby uwzględnić tę możliwość, możemy użyć specjalnego zdarzenia ładowania, które jest uruchamiane natychmiast, gdy obraz jest gotowy. event.special.load jest obecnie dostępny jako wtyczka.

Zgodnie z dokumentacją w .load ()

Evan Carroll
źródło
2
Zamierzałem dodać odpowiedź z taką sugestią, cieszę się, że widziałem tę odpowiedź przed jej zakodowaniem. Zasadniczo, twoje programy obsługi muszą najpierw sprawdzić, czy obraz jest załadowany przed ustawieniem modułu obsługi. Jeśli jest już załadowany, natychmiast uruchom wywołanie zwrotne. To co $.readyrobi. Miałem zamiar zasugerować sprawdzenie szerokości, ale to ma swoje problemy, naprawdę podoba mi się to rozwiązanie.
Juan Mendes,
5

Chcesz zrobić to, co powiedział Allain, jednak pamiętaj, że czasami obraz ładuje się, zanim dom gotowy, co oznacza, że ​​twój nośnik ładunku nie uruchomi się. Najlepszym sposobem jest zrobienie tego, co mówi Allain, ale ustaw src obrazu za pomocą javascript po dołączeniu modułu obsługi ładowania. W ten sposób możesz zagwarantować, że odpali.

Jeśli chodzi o dostępność, czy Twoja witryna będzie nadal działać dla osób bez javascript? Możesz zechcieć nadać tagowi img poprawne src, dołączyć moduł obsługi gotowej do uruchomienia js: wyczyść obraz src (nadaj mu stałą z i wysokość za pomocą css, aby zapobiec migotaniu strony), a następnie ustaw moduł obsługi ładowania img, następnie zresetuj src do prawidłowego pliku. W ten sposób pokryjesz wszystkie bazy :)

Andrew Bullock
źródło
Witryna nadal działa dla osób bez Javascript, wystarczy przewinąć, aby zobaczyć cały obraz.
Kip
4

Zgodnie z jednym z ostatnich komentarzy do Twojego pierwotnego pytania

$(function() {

  $(window).resize(adjust_photo_size);
  adjust_photo_size();

  function adjust_photo_size()  {
    if (!$("#photo").get(0).complete) {
       $("#photo").load(function() {
          adjust_photo_size();
       });
    } else {
      ... 
    }
});

Ostrzeżenie Ta odpowiedź może spowodować poważną pętlę w wersji IE8 i wcześniejszych, ponieważ img.complete nie zawsze jest poprawnie ustawiana przez przeglądarkę. Jeśli musisz obsługiwać IE8, użyj flagi, aby zapamiętać, że obraz został załadowany.

pospolity
źródło
Trzymaj to. Ta odpowiedź może spowodować poważną pętlę w IE8 i niższych wersjach, ponieważ img.complete nie zawsze jest poprawnie ustawiana przez przeglądarkę. Jeśli musisz obsługiwać IE8, użyj flagi, aby zapamiętać, że obraz został załadowany.
Commonpike
2

Spróbuj czegoś takiego:

$("#photo").load(function() {
    alert("Hello from Image");
});
Allain Lalonde
źródło
6
Dzięki. Istnieje jednak możliwość, że obraz został już załadowany, zanim to się stanie, w takim przypadku zdarzenie load nie zostanie uruchomione.
Kip
A co z tagiem skryptu ustawiającym to zdarzenie zaraz po tagu graficznym? Powodem używania ready jest upewnienie się, że cały dokument jest załadowany, co w tym przypadku jest niepotrzebne, prawda?
Jason Goemaat
2

Istnieje wtyczka jQuery o nazwie „imagesLoaded”, która zapewnia metodę zgodną z różnymi przeglądarkami, aby sprawdzić, czy obrazy elementu zostały załadowane.

Witryna: https://github.com/desandro/imagesloaded/

Użycie dla kontenera, który ma wiele obrazów w środku:

$('container').imagesLoaded(function(){
 console.log("I loaded!");
})

Wtyczka jest świetna:

  1. działa przy sprawdzaniu kontenera z wieloma obrazami w środku
  2. działa dla sprawdzenia img, aby zobaczyć, czy został załadowany
sójka
źródło
wydaje się trochę ciężki 5KB zminimalizowany tylko po to, aby sprawdzić, czy załadowano obraz. Powinna być łatwiejsza i lżejsza dostępna metoda ...
cdalxndr
1

Jakieś komentarze na ten temat?

...

doShow = function(){
  if($('#img_id').attr('complete')){
    alert('Image is loaded!');
  } else {
    window.setTimeout('doShow()',100);
  }
};

$('#img_id').attr('src','image.jpg');

doShow();

...

Wygląda na to, że działa wszędzie ...

Sójka
źródło
jest to zła praktykasetTimeout
cdalxndr
1

Właśnie utworzyłem funkcję jQuery do ładowania obrazu za pomocą jQuerys Deferred Object, co bardzo ułatwia reakcję na zdarzenie ładowania / błędu:

$.fn.extend({
    loadImg: function(url, timeout) {
        // init deferred object
        var defer = $.Deferred(),
            $img = this,
            img = $img.get(0),
            timer = null;

        // define load and error events BEFORE setting the src
        // otherwise IE might fire the event before listening to it
        $img.load(function(e) {
            var that = this;
            // defer this check in order to let IE catch the right image size
            window.setTimeout(function() {
                // make sure the width and height are > 0
                ((that.width > 0 && that.height > 0) ? 
                    defer.resolveWith : 
                    defer.rejectWith)($img);
            }, 1);
        }).error(function(e) {
            defer.rejectWith($img);
        });

        // start loading the image
        img.src = url;

        // check if it's already in the cache
        if (img.complete) {
            defer.resolveWith($img);
        } else if (0 !== timeout) {
            // add a timeout, by default 15 seconds
            timer = window.setTimeout(function() {
                defer.rejectWith($img);
            }, timeout || 15000);
        }

        // return the promise of the deferred object
        return defer.promise().always(function() {
            // stop the timeout timer
            window.clearTimeout(timer);
            timer = null;
            // unbind the load and error event
            this.off("load error");
        });
    }
});

Stosowanie:

var image = $('<img />').loadImg('http://www.google.com/intl/en_com/images/srpr/logo3w.png')
.done(function() {
    alert('image loaded');
    $('body').append(this);
}).fail(function(){
    alert('image failed');
});

Zobacz, jak działa: http://jsfiddle.net/roberkules/AdWZj/

roberkules
źródło
To brzmi wspaniale. Czy możesz podać dokładny przykład, kiedy Twój kod jest lepszy niż img.onload i img.onerror? Czy twój kod jest przeznaczony tylko do obsługi pozornych niedociągnięć obsługi błędów jQuery, o których właśnie zostałem poinformowany: ten błąd nie pojawia się w IE, jeśli nie ma rozszerzenia pliku?
mplungjan
ciekawy pomysł. więc wiedząc, kiedy obraz zrobił się łatwo. Ale co, jeśli nie można załadować obrazu. Szukałem sposobu, aby dowiedzieć się, czy ładowanie obrazu nie powiodło się. i dałeś mi pomysł przekroczenia limitu czasu podczas jego ładowania. +1 za to
dąb
1
@oak the timeout powinno być tylko rezerwą. większość przeglądarek powinna uruchamiać errorzdarzenie obsługiwane przez .error(function(e) { defer.rejectWith($img); }). Jeśli spojrzysz na jsFiddle, zobaczysz, że http://www.google.com/abc.png not foundtekst jest wyświetlany natychmiast w okienku wyników (nie czeka na
przekroczenie
zwróć uwagę, że aby jquery podniósł .error, dwie rzeczy powinny być 1. uchwytem, ​​ale powinny być ustawione przed zgłoszeniem błędu. 2. .error obsługuje tylko protokół http, a nie file: //, więc nie pojawi się tam błąd ..
dąb,
w moim przykładzie kodu procedury obsługi sukcesu / błędu są ustawiane przed przypisaniem atrybutu src dokładnie z tego powodu. o file://ograniczeniach - nigdy nie musiałem łączyć obrazów za pomocą protokołu pliku i nie sądzę, aby było to zbyt potrzebne.
roberkules
1

Ta funkcja sprawdza, czy obraz jest załadowany na podstawie mierzalnych wymiarów. Ta technika jest przydatna, jeśli skrypt jest wykonywany po załadowaniu niektórych obrazów.

imageLoaded = function(node) {
    var w = 'undefined' != typeof node.clientWidth ? node.clientWidth : node.offsetWidth;
    var h = 'undefined' != typeof node.clientHeight ? node.clientHeight : node.offsetHeight;
    return w+h > 0 ? true : false;
};
Ralph Ritoch
źródło
Odkryłem, że to rozwiązanie może wymagać, aby na obrazie nie było marginesów ani dopełnienia i że podano tekst alternatywny pustego ciągu. Jednak w większości przypadków działa zgodnie z przeznaczeniem. Nie powiedzie się tylko wtedy, gdy rozmiar wyładowanego obrazu jest większy niż zero.
Ralph Ritoch
1

Odkryłem, że to działa dla mnie

document.querySelector("img").addEventListener("load", function() { alert('onload!'); });

Kredyt w całości przypada Frankowi Schwietermanowi, który skomentował zaakceptowaną odpowiedź. Musiałem to tutaj umieścić, jest zbyt cenne ...

Armand
źródło
0

Stworzyliśmy stronę, na której ładowano kilka obrazów, a następnie wykonywaliśmy inne funkcje dopiero po załadowaniu obrazu. To była ruchliwa witryna, która generowała duży ruch. Wygląda na to, że następujący prosty skrypt działał praktycznie na wszystkich przeglądarkach:

$(elem).onload = function() {
    doSomething();
}

ALE JEST TO POTENCJALNY PROBLEM DLA IE9!

JEDYNA przeglądarka, w której zgłaszaliśmy problemy, to IE9. Czy nie jesteśmy zaskoczeni? Wydaje się, że najlepszym sposobem rozwiązania tego problemu jest nie przypisywanie src do obrazu, dopóki nie zostanie zdefiniowana funkcja onload PO:

$(elem).onload = function() {
    doSomething();
}
$(elem).attr('src','theimage.png');

Wygląda na to, że IE 9 czasami nie wyrzuca pliku onload jakiegokolwiek powodu zdarzenia. Inne rozwiązania na tej stronie (na przykład to od Evana Carrolla) nadal nie działały. Logicznie rzecz biorąc, sprawdziliśmy, czy stan ładowania już się powiódł i wyzwolił funkcję, a jeśli nie, to ustaw procedurę obsługi ładowania, ale nawet gdy to zrobisz, zademonstrowaliśmy w testowaniu, że obraz może załadować się między tymi dwoma wierszami js pojawia się jako nie załadowany w pierwszym wierszu, a następnie ładuje się przed ustawieniem modułu obsługi ładowania.

Odkryliśmy, że najlepszym sposobem uzyskania tego, czego chcesz, jest nie definiowanie źródła obrazu, dopóki nie ustawisz onloadwyzwalacza zdarzenia.

Dopiero niedawno przestaliśmy wspierać IE8, więc nie mogę mówić o wersjach wcześniejszych niż IE9, w przeciwnym razie ze wszystkich innych przeglądarek używanych w witrynie - IE10 i 11, a także Firefox, Chrome, Opera, Safari i tak dalej przeglądarka mobilna, z której ludzie korzystali - ustawienie src przed przypisaniem onloadobsługi nie było nawet problemem.

Ośmiornica
źródło
0

Czy mogę w ogóle zaproponować czyste rozwiązanie CSS?

Wystarczy mieć element DIV, w którym chcesz wyświetlić obraz. Ustaw obraz jako tło. Następnie masz nieruchomość background-size: coverlub w background-size: containzależności od tego, jak chcesz.

cover przycina obraz, aż mniejsze boki zakryją pudełko. containzachowa cały obraz wewnątrz elementu div, pozostawiając spacje po bokach.

Sprawdź poniższy fragment.

div {
  height: 300px;
  width: 300px;
  border: 3px dashed grey;
  background-position: center;
  background-repeat: no-repeat;
}

.cover-image {
  background-size: cover;
}

.contain-image {
  background-size: contain;
}
<div class="cover-image" style="background-image:url(https://assets1.ignimgs.com/2019/04/25/avengers-endgame-1280y-1556226255823_1280w.jpg)">
</div>
<br/>
<div class="contain-image" style="background-image:url(https://assets1.ignimgs.com/2019/04/25/avengers-endgame-1280y-1556226255823_1280w.jpg)">
</div>

arunwithasmile
źródło
0

Uważam, że to proste rozwiązanie działa najlepiej dla mnie:

        function setEqualHeight(a, b) {
            if (!$(a).height()) {
                return window.setTimeout(function(){ setEqualHeight(a, b); }, 1000);
            }
            $(b).height($(a).height());
        }

        $(document).ready(function() {
            setEqualHeight('#image', '#description');
            $(window).resize(function(){setEqualHeight('#image', '#description')});
        });
    </script>
Martin Francis
źródło