Wywołanie zwrotne jQuery przy ładowaniu obrazu (nawet gdy obraz jest buforowany)


291

Chcę zrobić:

$("img").bind('load', function() {
  // do stuff
});

Ale zdarzenie ładowania nie jest uruchamiane, gdy obraz jest ładowany z pamięci podręcznej. Dokumenty jQuery sugerują wtyczkę, aby to naprawić, ale to nie działa


12
Od czasu twojego pytania sytuacja się zmieniła. Zepsuta wtyczka została przeniesiona do gist, a później do repozytorium z małą działającą wtyczką jQuery.imagesLoaded . Naprawiają wszystkie małe dziwactwa przeglądarki .
Lode

Wyżej wspomniana biblioteka JQuery działała dla mnie dobrze. Dzięki!
plang

Dziękujemy za wtyczkę, działała dobrze.
onimojo,

Odpowiedzi:


572

Jeśli parametr srcjest już ustawiony, zdarzenie jest uruchamiane w zbuforowanej skrzynce, zanim jeszcze zostanie powiązana procedura obsługi zdarzenia. Aby to naprawić, możesz przejść przez sprawdzanie i wyzwalanie zdarzenia na podstawie .complete:

$("img").one("load", function() {
  // do stuff
}).each(function() {
  if(this.complete) {
      $(this).load(); // For jQuery < 3.0 
      // $(this).trigger('load'); // For jQuery >= 3.0 
  }
});

Zwróć uwagę na zmianę z .bind()na, .one()aby moduł obsługi zdarzeń nie uruchamiał się dwukrotnie.


8
Twoje rozwiązanie działało dla mnie idealnie, ale chcę coś zrozumieć, kiedy ten kod „if (this.complete)” zostanie uruchomiony, po załadowaniu zawartości obrazu, czy wcześniej? ponieważ jak rozumiem z tego .each, że zapętlasz wszystkie $ ("img") i być może zawartość obrazu jest pusta i ładowanie się nie wydarzy. hmmmm, myślę, że czegoś mi brakuje, byłoby miło, gdybyś mógł opisać, że to lepiej zrozumie. dzięki.
Amr Elgarhy

33
Od twojej odpowiedzi wszystko się zmieniło. Teraz jest mała działająca wtyczka jQuery.imagesLoaded . Naprawiają wszystkie małe dziwactwa przeglądarki .
Lode

3
Dodałbym w setTimeout(function(){//do stuff},0)ramach funkcji „load” ... 0 daje systemowi przeglądarki (DOM) jeden dodatkowy haczyk, aby upewnić się, że wszystko jest poprawnie aktualizowane.
dsdsdsdsd,

14
@Lode - to OGROMNA wtyczka, wcale nie mała w żadnej skali !!
vsync

5
ponieważ metoda JQuery 3 .load()nie wyzwala zdarzenia i ma 1 wymagany argument, użyj .trigger("load")zamiast tego
ForceUser

48

Czy mogę zasugerować ponowne załadowanie go do obiektu obrazu innego niż DOM? Jeśli jest buforowany, nie zajmie to wcale czasu, a obciążenie nadal będzie działać. Jeśli nie jest przechowywany w pamięci podręcznej, wyzwoli obciążenie po załadowaniu obrazu, co powinno być w tym samym czasie, gdy zakończy się ładowanie wersji DOM obrazu.

JavaScript:

$(document).ready(function() {
    var tmpImg = new Image() ;
    tmpImg.src = $('#img').attr('src') ;
    tmpImg.onload = function() {
        // Run onload code.
    } ;
}) ;

Zaktualizowano (do obsługi wielu obrazów i z poprawnie uporządkowanym załącznikiem onload):

$(document).ready(function() {
    var imageLoaded = function() {
        // Run onload code.
    }
    $('#img').each(function() {
        var tmpImg = new Image() ;
        tmpImg.onload = imageLoaded ;
        tmpImg.src = $(this).attr('src') ;
    }) ;
}) ;

1
Powinieneś spróbować dołączyć loadmoduł obsługi przed dodaniem, również to nie zadziała, ale dla jednego obrazu kod OP działa dla wielu :)
Nick Craver

Dzięki, dodałem zaktualizowaną wersję, która, mam nadzieję, rozwiązuje te dwa problemy.
Gus

#imgjest identyfikatorem, a nie selektorem elementów :) this.srcDziała również , nie trzeba używać jQuery tam, gdzie nie jest to potrzebne :) Ale tworzenie kolejnego obrazu wydaje się przesadą w obu przypadkach IMO.
Nick Craver

powinno to być $ („img”). ale rozwiązaniem jest także gr8 w 2k15
Dimag Kharab

Również w przypadku wielu obrazów, jeśli chcesz operować na elemencie DOM dla każdego obrazu imageLoaded, użyjtmp.onload = imageLoaded.bind(this)
blisstdev

38

Moje proste rozwiązanie, nie wymaga żadnej zewnętrznej wtyczki, a w typowych przypadkach powinno wystarczyć:

/**
 * Trigger a callback when the selected images are loaded:
 * @param {String} selector
 * @param {Function} callback
  */
var onImgLoad = function(selector, callback){
    $(selector).each(function(){
        if (this.complete || /*for IE 10-*/ $(this).height() > 0) {
            callback.apply(this);
        }
        else {
            $(this).on('load', function(){
                callback.apply(this);
            });
        }
    });
};

użyj tego w ten sposób:

onImgLoad('img', function(){
    // do stuff
});

na przykład, aby zanikać pod obciążeniem, możesz wykonać:

$('img').hide();
onImgLoad('img', function(){
    $(this).fadeIn(700);
});

Lub alternatywnie, jeśli wolisz podejście podobne do wtyczek jquery:

/**
 * Trigger a callback when 'this' image is loaded:
 * @param {Function} callback
 */
(function($){
    $.fn.imgLoad = function(callback) {
        return this.each(function() {
            if (callback) {
                if (this.complete || /*for IE 10-*/ $(this).height() > 0) {
                    callback.apply(this);
                }
                else {
                    $(this).on('load', function(){
                        callback.apply(this);
                    });
                }
            }
        });
    };
})(jQuery);

i użyj go w ten sposób:

$('img').imgLoad(function(){
    // do stuff
});

na przykład:

$('img').hide().imgLoad(function(){
    $(this).fadeIn(700);
});

urm .. a co jeśli moje obrazy mają ustawiony rozmiar z css?
Simon_Weaver

Zestaw width, max-heighti odchodzenie height:auto, często jest to konieczne, aby ustawić wysokość i szerokość tylko wtedy, gdy trzeba rozciągnąć obraz; bardziej niezawodne rozwiązanie byłoby użyć wtyczki jak ImagesLoaded
Guari

1
tak, to był błąd w pisaniu, jsdoc był w porządku, poprawiłem przykład linii
guari

1
To rozwiązanie z $ .fn.imgLoad działa w różnych przeglądarkach. Naprawienie tego będzie jeszcze lepsze: && / * dla IE 10 - * / this.naturalHeight> 0 Ta linia nie bierze pod uwagę stylu css, ponieważ img może być blokowany, ale ma wysokość. Praca w IE10
Patryk Padus

1
Ma to (mały) stan wyścigu. Obraz może być ładowany do skryptu w innym wątku, a zatem może się ładować między sprawdzeniem zakończenia a dołączeniem zdarzenia onload, w którym to przypadku nic się nie stanie. Zwykle JS jest zsynchronizowany z renderowaniem, więc nie musisz się martwić warunkami wyścigu, ale jest to wyjątek. html.spec.whatwg.org/multipage/…
Mog0

23

Czy naprawdę musisz to zrobić z jQuery? Możesz również dołączyć onloadwydarzenie bezpośrednio do swojego obrazu;

<img src="/path/to/image.jpg" onload="doStuff(this);" />

Będzie uruchamiany za każdym razem, gdy obraz zostanie załadowany, z pamięci podręcznej lub nie.


5
Jest czystszy bez wbudowanego javascript
orzech laskowy

1
Cześć, Bjorn, czy program onloadobsługi uruchomi się, gdy obraz zostanie załadowany po raz drugi z pamięci podręcznej?
SexyBeast

1
@Cupidvogel nie, nie będzie.
Reklamy

3
Koleś, uratowałeś mi życie, próbowałem co najmniej tuzin innych rozwiązań, a twoje jest jedyne, które faktycznie działało. Poważnie myślę o utworzeniu 10 kolejnych kont, aby głosować na twoją odpowiedź jeszcze 10 razy. Poważnie dzięki!
Carlo,

1
Nigdy nie rozumiałem niechęci ludzi do wbudowanego JS. Ładowanie obrazu odbywa się osobno i poza kolejnością w stosunku do reszty procesu ładowania strony. Dlatego jest to jedyne prawidłowe rozwiązanie, aby uzyskać natychmiastową informację zwrotną na temat zakończenia ładowania obrazu. Jeśli jednak ktoś będzie musiał czekać na załadowanie jQuery, co nastąpi później, wówczas nie będzie musiał użyć prostego rozwiązania tutaj i zastosować jednego z pozostałych rozwiązań (Piotr jest prawdopodobnie najlepszą opcją, ponieważ nie jest zależny od pseudo- hacki, ale kontrola guari () może być również ważna). Przydałaby się również kolejka przetwarzania.
CubicleSoft,

16

Możesz także użyć tego kodu z obsługą błędu ładowania:

$("img").on('load', function() {
  // do stuff on success
})
.on('error', function() {
  // do stuff on smth wrong (error 404, etc.)
})
.each(function() {
    if(this.complete) {
      $(this).load();
    } else if(this.error) {
      $(this).error();
    }
});

1
Ten działał dla mnie najlepiej. Myślę, że kluczem jest ustawienie modułu loadobsługi, a następnie wywołanie go później w eachfunkcji.
GreenRaccoon23

ustawienie obrazu za pomocą poniższego kodu 'var img = new Image (); img.src = sosImgURL; $ (img) .on ("load", function () {''
imdadhusen

13

Właśnie miałem ten problem, szukałem wszędzie rozwiązania, które nie wymagałoby zabicia mojej pamięci podręcznej ani pobrania wtyczki.

Nie zobaczyłem tego wątku od razu, więc zamiast tego znalazłem coś innego, co jest interesującą poprawką i (myślę) warte opublikowania tutaj:

$('.image').load(function(){
    // stuff
}).attr('src', 'new_src');

Właściwie ten pomysł pochodzi z komentarzy tutaj: http://www.witheringtree.com/2009/05/image-load-event-binding-with-ie-using-jquery/

Nie mam pojęcia, dlaczego to działa, ale przetestowałem to na IE7 i gdzie się zepsuł, zanim teraz działa.

Mam nadzieję, że to pomoże,

Edytować

Przyjęta odpowiedź faktycznie wyjaśnia, dlaczego:

Jeśli src jest już ustawiony, zdarzenie jest uruchamiane w pamięci podręcznej, zanim zostanie powiązana procedura obsługi zdarzenia.


4
Testowałem to w wielu przeglądarkach i nigdzie nie znalazłem. Kiedyś $image.load(function(){ something(); }).attr('src', $image.attr('src'));resetowałem oryginalne źródło.
Maurice

Przekonałem się, że to podejście nie działa na iOS, przynajmniej w Safari / 601.1 i Safari / 602.1
cronfy

Byłoby wspaniale dowiedzieć się, dlaczego, w jakiej przeglądarce jest?
Sammaye

5

Dzięki użyciu jQuery do wygenerowania nowego obrazu za pomocą src obrazu i przypisaniu bezpośrednio do niego metody load, metoda load jest z powodzeniem wywoływana, gdy jQuery zakończy generowanie nowego obrazu. Działa to dla mnie w IE 8, 9 i 10

$('<img />', {
    "src": $("#img").attr("src")
}).load(function(){
    // Do something
});

Dla tych, którzy używają Ruby on Rails, ze zdalnym żądaniem, używając szablonów __rjs.erb, jest to jedyne rozwiązanie, jakie znalazłem. Ponieważ użycie części w js.erb powoduje utratę wiązania ($ („# img” .on („load”) ...), a dla obrazów NIE jest możliwe delegowanie metody „on”: $ („body”). on („load”, „# img”, function () ...
Albert Català

5

Rozwiązanie znalazłem https://bugs.chromium.org/p/chromium/issues/detail?id=7731#c12 (ten kod pochodzi bezpośrednio z komentarza)

var photo = document.getElementById('image_id');
var img = new Image();
img.addEventListener('load', myFunction, false);
img.src = 'http://newimgsource.jpg';
photo.src = img.src;

Właśnie tego szukałem. Pojedynczy obraz przychodzi do mnie z serwera. Chciałem go wstępnie załadować, a następnie podać. Nie zaznaczanie wszystkich zdjęć, jak w przypadku wielu odpowiedzi. Dobry.
Kai Qing

4

Modyfikacja przykładu GUS:

$(document).ready(function() {
    var tmpImg = new Image() ;
    tmpImg.onload = function() {
        // Run onload code.
    } ;

tmpImg.src = $('#img').attr('src');
})

Ustaw źródło przed i po obciążeniu.


Podobnie jak odpowiedź @ Gusa, nie zadziała to w przypadku wielu obrazów ... i nie ma potrzeby ustawiania srcprzed dołączeniem programu onloadobsługi, raz wystarczy.
Nick Craver

3

Wystarczy ponownie dodać argument src w osobnym wierszu po zdefiniowaniu obiektu img. Spowoduje to, że IE uruchomi zdarzenie lad. To brzydkie, ale jest to najprostsze obejście, jakie do tej pory znalazłem.

jQuery('<img/>', {
    src: url,
    id: 'whatever'
})
.load(function() {
})
.appendTo('#someelement');
$('#whatever').attr('src', url); // trigger .load on IE

1

Mogę dać ci małą wskazówkę, jeśli chcesz to zrobić:

<div style="position:relative;width:100px;height:100px">
     <img src="loading.jpg" style='position:absolute;width:100px;height:100px;z-index:0'/>
     <img onLoad="$(this).fadeIn('normal').siblings('img').fadeOut('normal')" src="picture.jpg" style="display:none;position:absolute;width:100px;height:100px;z-index:1"/>
</div>

Jeśli zrobisz to, gdy przeglądarka buforuje zdjęcia, nie ma problemu, że zawsze wyświetla się img, ale ładuje obraz pod prawdziwym obrazem.


1

Miałem ten problem z IE, w którym e.target.width byłby niezdefiniowany. Zdarzenie load zadziałałoby, ale nie mogłem uzyskać wymiarów obrazu w IE (działało chrome + FF).

Okazuje się, że musisz poszukać e.currentTarget.naturalWidth i e.currentTarget.naturalHeight .

Po raz kolejny IE robi rzeczy po swojemu (bardziej skomplikowane).


0

Możesz rozwiązać problem za pomocą wtyczki JAIL, która pozwala również na leniwe ładowanie obrazów (poprawa wydajności strony) i przekazywanie wywołania zwrotnego jako parametru

$('img').asynchImageLoader({callback : function(){...}});

HTML powinien wyglądać

<img name="/global/images/sample1.jpg" src="/global/images/blank.gif" width="width" height="height" />

0

Jeśli potrzebujesz czystego rozwiązania CSS, ta sztuczka działa bardzo dobrze - użyj obiektu transformacji. Działa to również z obrazami, gdy są one buforowane lub nie:

CSS:

.main_container{
    position: relative;
    width: 500px;
    height: 300px;
    background-color: #cccccc;
}

.center_horizontally{
  position: absolute;
  width: 100px;
  height: 100px;
  background-color: green;
  left: 50%;
  top: 0;
  transform: translate(-50%,0);
}

.center_vertically{
  position: absolute;
  top: 50%;
  left: 0;
  width: 100px;
  height: 100px;
  background-color: blue;
  transform: translate(0,-50%);
}

.center{
  position: absolute;
  top: 50%;
  left: 50%;
  width: 100px;
  height: 100px;
  background-color: red;
  transform: translate(-50%,-50%);
}

HTML:

<div class="main_container">
  <div class="center_horizontally"></div>
  <div class="center_vertically"></div>
  <div class="center"></div>
  </div>
</div

Przykład Codepen

Przykład Codepen MNIEJ


Czy możesz podać przykład, jak to działa z ładowaniem obrazu?
Hastig Zusammenstellen,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.