Обратный вызов jQuery при загрузке изображения (даже когда изображение кэшируется)

291

Я хочу делать:

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

Но событие загрузки не срабатывает, когда изображение загружается из кэша. Документы jQuery предлагают плагин, чтобы это исправить, но он не работает

Том Леман
источник
12
Со времени твоего вопроса все изменилось. Разбитый плагин был перенесен в суть, а затем в репозиторий с небольшим рабочим плагином jQuery.imagesLoaded . Они исправляют все маленькие странности браузера .
Lode
Вышеупомянутая библиотека JQuery прекрасно работала для меня. Спасибо!
План
Спасибо за плагин, он работал нормально.
onimojo

Ответы:

572

Если srcон уже установлен, то событие запускается в кэшированном случае еще до того, как вы привязали обработчик события. Чтобы это исправить, вы можете циклически проверять и запускать событие на основе.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 
  }
});

Обратите внимание на изменение с .bind()на, .one()чтобы обработчик событий не запускался дважды.

Ник Крейвер
источник
8
Ваше решение отлично сработало для меня, но я хочу кое-что понять, когда этот код "if (this.complete)" будет запущен, после загрузки содержимого изображения или до этого? потому что, как я понимаю из этого .each, вы зацикливаетесь на всех $ ("img") и, возможно, содержимое изображения пустое, и загрузка не произойдет. хммммм, я думаю, что мне чего-то не хватает, было бы хорошо, если бы вы могли описать, что происходит, чтобы лучше понять это Спасибо.
Амр Элгархи
33
С тех пор, как ваш ответ изменился. Там сейчас есть небольшой рабочий плагин jQuery.imagesLoaded . Они исправляют все маленькие странности браузера .
Лоде
3
Я бы добавил функцию setTimeout(function(){//do stuff},0)'load' ... 0 дает системе браузера (DOM) один дополнительный тик, чтобы убедиться, что все правильно обновлено.
dsdsdsdsd
14
@Lode - это ОГРОМНЫЙ плагин, совсем не маленький!
vsync
5
поскольку метод JQuery 3 .load()не вызывает событие и имеет 1 обязательный аргумент, используйте .trigger("load")вместо него
ForceUser
48

Могу ли я предложить вам перезагрузить его в объект изображения не из DOM? Если он кешируется, это совсем не займет времени, а нагрузка все равно сработает. Если он не кэшируется, он будет запускать загрузку при загрузке изображения, что должно быть одновременно с завершением загрузки DOM-версии образа.

Javascript:

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

Обновлено (для обработки нескольких изображений и с правильно упорядоченным вложенным приложением):

$(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
Вы должны попытаться подключить loadобработчик до того, как он будет добавлен, также это не будет работать, но для одного изображения код OPs работает для многих :)
Ник Крейвер
Спасибо, я добавил обновленную версию, которая, я надеюсь, решает эти две проблемы.
Гас
#imgявляется идентификатором, а не селектором элемента :) Также this.srcработает, нет необходимости использовать jQuery там, где он не нужен :) Но создание другого изображения кажется избыточным в любом случае IMO.
Ник Крейвер
это должно быть $ ('img'). но решение gr8 в 2k15 также
Димаг Хараб
Кроме того, для случая нескольких изображений, если вы хотите работать с элементом DOM для каждого изображения в imageLoaded, то используйтеtmp.onload = imageLoaded.bind(this)
blisstdev
38

Мое простое решение, для него не нужен внешний плагин и для общих случаев должно быть достаточно:

/**
 * 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);
            });
        }
    });
};

используйте это так:

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

Например, чтобы добавить изображения к загрузке, вы можете сделать:

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

Или в качестве альтернативы, если вы предпочитаете 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);

и использовать его таким образом:

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

например:

$('img').hide().imgLoad(function(){
    $(this).fadeIn(700);
});
Гуари
источник
хм .. а что если мои изображения имеют размер, заданный с помощью css?
Simon_Weaver
Набор width, max-heightи отпуск height:auto, часто необходимо установить как ширину и высоту , только если вам нужно , чтобы растянуть изображение; для более надежного решения я бы использовал плагин, такой как ImagesLoaded
guari
1
да, это было опечатка, jsdoc был в порядке, я исправил пример строки
guari
1
Это решение с $ .fn.imgLoad работает в разных браузерах. Исправить это будет еще лучше: && / * для IE 10 - * / this.naturalHeight> 0 Эта строка не учитывает стиль css, поскольку img может быть заблокирован, но иметь высоту. Работа в IE10
Патрик Падус
1
Это имеет (маленькое) состояние гонки. Изображение может загружаться в другой поток в сценарий и может загружаться между проверкой завершения и присоединением события onload, и в этом случае ничего не произойдет. Обычно JS работает синхронно с рендерингом, поэтому вам не нужно беспокоиться о состоянии гонки, но это исключение. html.spec.whatwg.org/multipage/…
Mog0
23

Вы действительно должны сделать это с JQuery? Вы можете прикрепить onloadсобытие непосредственно к вашему изображению;

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

Он будет срабатывать каждый раз, когда изображение загружено, из кэша или нет.

Бьерн
источник
5
Это чище без встроенного JavaScript
фундук
1
Привет, Бьорн, будет ли onloadобработчик срабатывать при повторной загрузке изображения из кэша?
SexyBeast
1
@Cupidvogel нет, не будет.
Объявления
3
Чувак, ты спас мне жизнь, я попробовал по крайней мере дюжину других решений, и твое единственное, которое действительно сработало. Я серьезно думаю о создании еще 10 учетных записей, чтобы проголосовать за ваш ответ еще 10 раз. Серьезно спасибо!
Карло
1
Я никогда не понимал отвращение людей к встроенному JS. Загрузка изображения происходит отдельно и не по порядку от остальной части процесса загрузки страницы. Таким образом, это единственное правильное решение для получения немедленной обратной связи о завершении загрузки изображения. Однако, если кому-то нужно дождаться загрузки jQuery, и это произойдет позже, тогда ему не придется использовать простое решение здесь и использовать одно из других решений (вероятно, Piotr - лучший вариант, так как он не зависит от псевдо- хаки, но проверка guari's height () тоже может быть важна). Очередь обработки также может быть полезна.
CubicleSoft
16

Вы также можете использовать этот код с поддержкой ошибки загрузки:

$("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
Этот работал лучше всего для меня. Я думаю, что ключ состоит в том, чтобы loadсначала установить обработчик, а затем вызвать его позже в eachфункции.
GreenRaccoon23
установка изображения с помощью кода ниже '' var img = new Image (); img.src = sosImgURL; $ (img) .on ("load", function () {''
imdadhusen
13

У меня просто была эта проблема, я всюду искал решение, которое не включало бы уничтожение моего кэша или загрузку плагина.

Я не видел эту ветку сразу, поэтому нашел что-то еще, что является интересным исправлением и (я думаю) достойным публикации здесь:

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

Я на самом деле получил эту идею из комментариев здесь: http://www.witheringtree.com/2009/05/image-load-event-binding-with-ie-using-jquery/

Я понятия не имею, почему это работает, но я проверил это на IE7 и где он сломался, прежде чем теперь работает.

Надеюсь, поможет,

редактировать

Принятый ответ фактически объясняет, почему:

Если src уже установлен, то событие запускается в кеше, перед тем как вы получите привязанный обработчик события.

Sammaye
источник
4
Я проверял это во многих браузерах и нигде не обнаружил сбоев. Я использовал $image.load(function(){ something(); }).attr('src', $image.attr('src'));для сброса исходного источника.
Морис
Я обнаружил, что этот подход не работает на iOS, по крайней мере, для Safari / 601.1 и Safari / 602.1
cronfy
Было бы здорово узнать почему, в каком браузере?
Саммайе
5

Используя jQuery для генерации нового изображения с помощью src изображения и назначая непосредственно для него метод загрузки, метод загрузки успешно вызывается, когда jQuery завершает генерацию нового изображения. Это работает для меня в IE 8, 9 и 10

$('<img />', {
    "src": $("#img").attr("src")
}).load(function(){
    // Do something
});
Ник
источник
Для тех, кто использует Ruby on Rails, с удаленным запросом, используя шаблоны __rjs.erb, это единственное решение, которое я нашел. Поскольку при использовании partials в js.erb он теряет привязку ($ ("# img" .on ("load") ... и для изображений НЕ возможно делегировать метод "on": $ ("body")). on ("load", "# img", function () ...
Альберт Катала
5

Решение, которое я нашел, https://bugs.chromium.org/p/chromium/issues/detail?id=7731#c12 (этот код взят непосредственно из комментария)

var photo = document.getElementById('image_id');
var img = new Image();
img.addEventListener('load', myFunction, false);
img.src = 'http://newimgsource.jpg';
photo.src = img.src;
AllisonC
источник
Это именно то, что я искал. Одно изображение приходит ко мне с сервера. Я хотел предварительно загрузить это и затем служить. Не выбирая все изображения, как многие ответы. Хороший.
Кай Цин
4

Модификация примера GUS:

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

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

Установите источник до и после загрузки.

Чак Конвей
источник
Как и ответ @ Gus, это не сработает для нескольких изображений ... и нет необходимости устанавливать srcперед тем, как присоединять onloadобработчик, а потом будет достаточно.
Ник Крейвер
3

Просто добавьте аргумент src в отдельную строку после определения изображения. Это обманом заставит IE запустить лад-событие. Это уродливо, но это самый простой обходной путь, который я нашел до сих пор.

jQuery('<img/>', {
    src: url,
    id: 'whatever'
})
.load(function() {
})
.appendTo('#someelement');
$('#whatever').attr('src', url); // trigger .load on IE
Бьёрн Т. Даль
источник
1

Я могу дать вам небольшой совет, если вы хотите сделать так:

<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>

Если вы делаете это, когда браузер кеширует изображения, то это не проблема, всегда показывается img, но загружается img под реальным изображением.

Иосиф Бесим
источник
1

У меня была эта проблема с IE, где e.target.width будет неопределенным. Событие load сработало бы, но я не смог получить размеры изображения в IE (сработал chrome + FF).

Оказывается, вам нужно искать e.currentTarget.naturalWidth & e.currentTarget.naturalHeight .

Еще раз, IE делает вещи своим собственным (более сложным) способом.

Али
источник
0

Вы можете решить вашу проблему, используя плагин JAIL, который также позволяет лениво загружать изображения (улучшая производительность страницы) и передавая обратный вызов в качестве параметра

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

HTML должен выглядеть так

<img name="/global/images/sample1.jpg" src="/global/images/blank.gif" width="width" height="height" />
sebarmeli
источник
0

Если вам нужно чистое решение CSS, этот прием работает очень хорошо - используйте объект transform. Это также работает с изображениями, когда они кэшированы или нет:

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

Пример Codepen

Codepen LESS пример

neoRiley
источник
Не могли бы вы привести пример того, как это работает с загрузкой изображений?
Хастиг Зусамменстеллен