Предварительная загрузка изображений с помощью jQuery

689

Я ищу быстрый и простой способ предварительной загрузки изображений с помощью JavaScript. Я использую jQuery, если это важно.

Я видел это здесь ( http: //nettuts.com ... ):

function complexLoad(config, fileNames) {
  for (var x = 0; x < fileNames.length; x++) {
    $("<img>").attr({
      id: fileNames[x],
      src: config.imgDir + fileNames[x] + config.imgFormat,
      title: "The " + fileNames[x] + " nebula"
    }).appendTo("#" + config.imgContainer).css({ display: "none" });
  }
};

Но это выглядит немного чрезмерно для того, что я хочу!

Я знаю, что есть плагины jQuery, которые делают это, но все они кажутся немного большими (по размеру); Мне просто нужен быстрый, простой и короткий способ предварительной загрузки изображений!


источник
10
$.each(arguments,function(){(new Image).src=this});
Дэвид Хеллсинг

Ответы:

969

Быстро и просто:

function preload(arrayOfImages) {
    $(arrayOfImages).each(function(){
        $('<img/>')[0].src = this;
        // Alternatively you could use:
        // (new Image()).src = this;
    });
}

// Usage:

preload([
    'img/imageName.jpg',
    'img/anotherOne.jpg',
    'img/blahblahblah.jpg'
]);

Или, если вы хотите плагин jQuery:

$.fn.preload = function() {
    this.each(function(){
        $('<img/>')[0].src = this;
    });
}

// Usage:

$(['img1.jpg','img2.jpg','img3.jpg']).preload();
Джеймс
источник
25
Не нужно ли вставлять элемент изображения в DOM, чтобы браузер кэшировал его?
JoshNaro
8
Я считаю, $('<img />')что просто создает элемент изображения в памяти (см. Ссылку ). Похоже, '$('<img src="' + this + '" />')что на самом деле создаст элемент в скрытом DIV, потому что это более «сложный». Однако я не думаю, что это необходимо для большинства браузеров.
ДжошНаро
104
Это странный способ написания плагина jQuery. Почему нет $.preload(['img1.jpg', 'img2.jpg'])?
Алекс
12
Обязательно вызовите это внутри, $(window).load(function(){/*preload here*/});потому что таким образом все изображения в документе загружаются первыми, скорее всего, они нужны в первую очередь.
Джаспер Кеннис
12
@RegionalC - Может быть немного безопаснее установить loadсобытие перед установкой, на srcслучай, если изображение завершит загрузку до того, как будет установлено событие загрузки? $('<img/>').load(function() { /* it's loaded! */ }).attr('src', this);
незаполненные
102

Вот измененная версия первого ответа, которая фактически загружает изображения в DOM и скрывает его по умолчанию.

function preload(arrayOfImages) {
    $(arrayOfImages).each(function () {
        $('<img />').attr('src',this).appendTo('body').css('display','none');
    });
}
Деннис Ронго
источник
38
hide()более кратко чем css('display', 'none').
Алекс
6
В чем преимущество их вставки в DOM?
Алекс
Я также хотел бы знать преимущество вставки их в DOM.
Джедмао
11
Исходя из моего опыта, предварительная загрузка изображения в DOM позволяет браузеру узнать о его существовании и правильном кэшировании. В противном случае изображение существует только в памяти, которая работает только для одностраничных приложений.
Деннис Ронго
Деннис Ронго. При добавлении изображений в DOM исправлена ​​случайная повторная загрузка в Chrome. Спасибо!
Тормелл
52

Использовать объект JavaScript Image .

Эта функция позволяет вызвать обратный вызов при загрузке всех изображений. Однако обратите внимание, что он никогда не будет вызывать обратный вызов, если хотя бы один ресурс не загружен. Это может быть легко исправлено путем реализации onerrorобратного вызова и увеличения loadedзначения или обработки ошибки.

var preloadPictures = function(pictureUrls, callback) {
    var i,
        j,
        loaded = 0;

    for (i = 0, j = pictureUrls.length; i < j; i++) {
        (function (img, src) {
            img.onload = function () {                               
                if (++loaded == pictureUrls.length && callback) {
                    callback();
                }
            };

            // Use the following callback methods to debug
            // in case of an unexpected behavior.
            img.onerror = function () {};
            img.onabort = function () {};

            img.src = src;
        } (new Image(), pictureUrls[i]));
    }
};

preloadPictures(['http://foo/picture.bar', 'http://foo/picture.bar', 'http://foo/picture.bar', 'http://foo/picture.bar'], function(){
    console.log('a');
});

preloadPictures(['http://foo/picture.bar', 'http://foo/picture.bar', 'http://foo/picture.bar', 'http://foo/picture.bar'], function(){
    console.log('b');
});
Gajus
источник
4
Делайте правильно, используйте JavaScript Image object. Вы видели, как люди поступали неправильно в этих ответах?
Алекс
Блестящий код. Правильно ли я считаю, что это вызовет onloadсобытие, даже если изображение кэшируется? (Потому img.onloadчто объявляется первым). Это то, что показали мои тесты.
Startec
3
@alex потенциально, да. Если целью является предварительная загрузка (что предполагает порядок производительности), то я бы предпочел видеть необработанный параметр JS вместо зависимых от jQuery.
Чарли Шлиссер
Мне понравилось это решение, но я только что обнаружил, что оно не работает в моем Firefox. Это только у меня или у других такая же проблема?
Газиллион
@Gazillion дает больше подробностей. Как вы определяете "не работает"? Что такое версия FF?
Gajus
35

JP, После проверки вашего решения, у меня все еще были проблемы в Firefox, когда он не загружал изображения до перехода к загрузке страницы. Я обнаружил это, поместив некоторые sleep(5)в свой серверный скрипт. Я реализовал следующее решение на основе вашего, которое, кажется, решает эту проблему.

По сути, я добавил обратный вызов в ваш плагин предварительной загрузки jQuery, чтобы он вызывался после правильной загрузки всех изображений.

// Helper function, used below.
// Usage: ['img1.jpg','img2.jpg'].remove('img1.jpg');
Array.prototype.remove = function(element) {
  for (var i = 0; i < this.length; i++) {
    if (this[i] == element) { this.splice(i,1); }
  }
};

// Usage: $(['img1.jpg','img2.jpg']).preloadImages(function(){ ... });
// Callback function gets called after all images are preloaded
$.fn.preloadImages = function(callback) {
  checklist = this.toArray();
  this.each(function() {
    $('<img>').attr({ src: this }).load(function() {
      checklist.remove($(this).attr('src'));
      if (checklist.length == 0) { callback(); }
    });
  });
};

Из интереса, в моем контексте, я использую это следующим образом:

$.post('/submit_stuff', { id: 123 }, function(response) {
  $([response.imgsrc1, response.imgsrc2]).preloadImages(function(){
    // Update page with response data
  });
});

Надеюсь, это поможет тому, кто заходит на эту страницу из Google (как и я), в поисках решения для предварительной загрузки изображений при вызовах Ajax.

Дейв
источник
2
Для тех, кто не заинтересован в том, чтобы испортить прототип Array.pro, вместо этого вы можете сделать следующее: checklist = this.lengthИ в функции onload: checklist--Тогда:if (checklist == 0) callback();
MiniGod
3
Все было бы хорошо, однако добавление новых или удаление существующих методов для объекта, которым вы не владеете, является одной из худших практик в JavaScript.
Рихардс
Красиво и быстро, но этот код никогда не вызовет обратный вызов, если одно из изображений не работает или не может быть загружено.
SammyK
.attr({ src: this }).load(function() {...})В .load(function() {...}).attr({ src: this })противном случае у вас будут проблемы с кэшированием.
Кевин Б.
25

Этот однострочный код jQuery создает (и загружает) элемент DOM img, не показывая его:

$('<img src="img/1.jpg"/>');
rkcell
источник
4
@huzzah - Тебе может быть лучше использовать спрайты. Меньше http запросов. :)
Alex K
22
$.fn.preload = function (callback) {
  var length = this.length;
  var iterator = 0;

  return this.each(function () {
    var self = this;
    var tmp = new Image();

    if (callback) tmp.onload = function () {
      callback.call(self, 100 * ++iterator / length, iterator === length);
    };

    tmp.src = this.src;
  });
};

Использование довольно просто:

$('img').preload(function(perc, done) {
  console.log(this, perc, done);
});

http://jsfiddle.net/yckart/ACbTK/

yckart
источник
Это действительно хороший ответ, он действительно должен быть лучшим ответом imho.
сонный
1
Некоторые поясняющие слова были бы хорошими.
Робс
Хороший ответ, но я предлагаю использовать picsum.photos вместо lorempixel.com
Александр
19

У меня есть небольшой плагин, который обрабатывает это.

Он называется waitForImages и может обрабатывать imgэлементы или любые элементы со ссылкой на изображение в CSS, например div { background: url(img.png) }.

Если вы просто хотите загрузить все изображения, включая те, на которые есть ссылки в CSS, вот как вы это сделаете :)

$('body').waitForImages({
    waitForAll: true,
    finished: function() {
       // All images have loaded.
    }  
});
Алекс
источник
Этот плагин вызывает загрузки изображений, которые еще не появились на странице, или только прикрепляет события к изображениям, которые уже были загружены?
Дейв Кэмерон
@DaveCameron Не имеет значения, являются ли изображения видимыми или нет. Вы можете легко его раскошелиться и внести это изменение - просто добавьте :visibleв пользовательский селектор.
Алекс
Этот плагин выглядит очень интересно. Тем не менее, документация JQuery подчеркивает, что loadсобытие, когда применяется к изображениям: «не работает согласованно и надежно кросс-браузер». Как плагину удалось обойти это?
EleventyOne
@EleventyOne Проверьте источник относительно пользовательского селектора.
Алекс
@alex Как этот плагин загружает изображения, которые установлены в css on: hover элемента? Это не работает для меня
Филипп Хофманн
13

Вы можете загрузить изображения в ваш HTML где-нибудь с помощью display:none;правила CSS , а затем показать их, когда вы хотите с JS или JQuery

не используйте функции js или jquery для предварительной загрузки - это просто правило css против множества строк j, которые нужно выполнить

пример: HTML

<img src="someimg.png" class="hide" alt=""/>

Css:

.hide{
display:none;
}

JQuery:

//if want to show img 
$('.hide').show();
//if want to hide
$('.hide').hide();

Предварительная загрузка изображений с помощью jquery / javascript не годится, поскольку загрузка изображений на странице занимает несколько миллисекунд + у вас есть миллисекунды для синтаксического анализа и выполнения сценария, особенно если это большие изображения, поэтому скрытие их в hml лучше также для производительности, потому что изображение действительно предварительно загружено, не будучи видимым, пока вы не покажете это!

это я
источник
2
Более подробную информацию об этом подходе можно найти здесь: perishablepress.com/...
gdelfino
3
Но вы должны знать, что этот метод имеет существенный недостаток: ваша страница не будет полностью загружена, пока не будут загружены все изображения. В зависимости от количества загружаемых изображений и их размера это может занять некоторое время. Еще хуже, если тег <img> не определяет высоту и ширину, некоторые браузеры могут подождать, пока изображение будет извлечено, прежде чем отображать остальную часть страницы.
@ Алекс, в любом случае, вам нужно загрузить IMG, вы можете выбрать, загружать ли его с помощью html и избегать каких-либо миганий и наполовину загруженных изображений, или если вы хотите получить более высокую скорость при
поиске
Кроме того, я действительно думаю, что создание HTML-тегов с помощью javascript не читается вообще, только мои 2 цента
его
1
Мне нужно было очень быстрое решение для очень сложного проекта, и на этом все.
Germstorm
13

этот jquery imageLoader плагин всего 1.39kb

Применение:

$({}).imageLoader({
    images: [src1,src2,src3...],
    allcomplete:function(e,ui){
        //images are ready here
        //your code - site.fadeIn() or something like that
    }
});

Существуют также другие варианты, например, хотите ли вы загружать изображения синхронно или асинхронно, а также полное событие для каждого отдельного изображения.

alzclarke
источник
@AamirAfridi Почему это?
Ян
1
@Ian, потому что документ уже готов к тому времени, когда сработал обратный вызов. $ (document) .ready используется, чтобы убедиться, что ваш DOM загружен. Когда дело доходит до обратного вызова, это означает, что все изображения загружены, что означает, что ваш DOM загружен, поэтому нет необходимости в document.ready внутри обратного вызова.
Аамир Африди
1
@AamirAfridi Нет никаких гарантий, что документ будет готов к обратному вызову ... откуда вы это делаете? На странице плагина нет ничего, что говорило бы, что allcompleteобратный вызов выполняется после того, как DOM будет готов. Есть довольно хороший шанс, что изображения завершат загрузку после того, как DOM будет готов, но нет никаких оснований предполагать. Я не знаю, почему вы думаете, что обратные вызовы магические и выполняются после того, как DOM будет готов ... вы можете объяснить, откуда вы это взяли? То, что изображения загружены, не означает, что DOM готов
Ян
@AamirAfridi И в документации к плагину даже сказано: NB to use this as an image preloader simply put your $(document).ready(function(){}); into your 'allcomplete' event : > simples!... он прямо рекомендует то, что показывает этот ответ.
Ян
5
@AamirAfridi Это не имеет смысла для этой ситуации. Плагин является предварительным загрузчиком. Вы хотите выполнить это как можно скорее. И есть множество вещей, которые не зависят от DOM, который вы хотите запустить как можно скорее, а затем, когда DOM будет готов, сделайте что-нибудь.
Ян
9

Быстрый, без плагинов способ предварительно загрузить изображения в jQuery и получить функцию обратного вызова состоит в том, чтобы создавать несколько imgтегов одновременно и подсчитывать ответы, например

function preload(files, cb) {
    var len = files.length;
    $(files.map(function(f) {
        return '<img src="'+f+'" />';
    }).join('')).load(function () {
        if(--len===0) {
            cb();
        }
    });
}

preload(["one.jpg", "two.png", "three.png"], function() {
    /* Code here is called once all files are loaded. */
});
    

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

function preload(files, cb) {
    var len = files.length;
    $($.map(files, function(f) {
        return '<img src="'+f+'" />';
    }).join('')).load(function () {
        if(--len===0) {
            cb();
        }
    });
}
IZB
источник
7

Спасибо за это! Я хотел бы добавить небольшой риф к ответу JP - я не знаю, поможет ли это кому-нибудь, но таким образом вам не нужно создавать массив изображений, и вы можете предварительно загрузить все свои большие изображения, если вы правильно назовите большие пальцы. Это удобно, потому что у меня есть кто-то, кто пишет все страницы в html, и это гарантирует им на один шаг меньше - устранение необходимости создавать массив изображений и еще один шаг, когда все может быть испорчено.

$("img").each(function(){
    var imgsrc = $(this).attr('src');
    if(imgsrc.match('_th.jpg') || imgsrc.match('_home.jpg')){
      imgsrc = thumbToLarge(imgsrc);
      (new Image()).src = imgsrc;   
    }
});

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

В моем случае страница была полна изображений большого пальца, все названы как image_th.jpg, а все соответствующие большие изображения названы image_lg.jpg. Большой палец просто заменяет _th.jpg на _lg.jpg, а затем предварительно загружает все большие изображения.

Надеюсь, это кому-нибудь поможет.

dgig
источник
3
    jQuery.preloadImage=function(src,onSuccess,onError)
    {
        var img = new Image()
        img.src=src;
        var error=false;
        img.onerror=function(){
            error=true;
            if(onError)onError.call(img);
        }
        if(error==false)    
        setTimeout(function(){
            if(img.height>0&&img.width>0){ 
                if(onSuccess)onSuccess.call(img);
                return img;
            }   else {
                setTimeout(arguments.callee,5);
            }   
        },0);
        return img; 
    }

    jQuery.preloadImages=function(arrayOfImages){
        jQuery.each(arrayOfImages,function(){
            jQuery.preloadImage(this);
        })
    }
 // example   
    jQuery.preloadImage(
        'img/someimage.jpg',
        function(){
            /*complete
            this.width!=0 == true
            */
        },
        function(){
            /*error*/
        }
    )
Alex
источник
7
Добро пожаловать в переполнение стека! Вместо того, чтобы только публиковать блок кода, пожалуйста, объясните, почему этот код решает поставленную проблему. Без объяснения это не ответ.
Мартин Питерс
3

Я использую следующий код:

$("#myImage").attr("src","img/spinner.gif");

var img = new Image();
$(img).load(function() {
    $("#myImage").attr("src",img.src);
});
img.src = "http://example.com/imageToPreload.jpg";
Cristan
источник
Сначала привяжите к событию load, затем установите src.
Кевин Б
1

Я бы использовал файл Manifest, чтобы сообщить (современным) веб-браузерам, чтобы они также загружали все соответствующие изображения и кэшировали их. Используйте Grunt и grunt-manifest, чтобы сделать это автоматически, и больше никогда не беспокоиться о скриптах предварительной загрузки, обесценивателях кэша, CDN и т. Д.

https://github.com/gunta/grunt-manifest

Кристиан Ландгрен
источник
0

Это работает для меня даже в IE9:

$('<img src="' + imgURL + '"/>').on('load', function(){ doOnLoadStuff(); });
Zymotik
источник
1
Это в конечном итоге не удастся из-за кэширования, всегда связывайте событие загрузки перед установкой атрибута src.
Кевин Б.
0

Я хотел сделать это с помощью пользовательского оверлея Google Maps API. Их пример кода просто использует JS для вставки элементов IMG, и поле заполнителя изображения отображается до тех пор, пока изображение не будет загружено. Я нашел ответ, который работал для меня: https://stackoverflow.com/a/10863680/2095698 .

$('<img src="'+ imgPaht +'">').load(function() {
$(this).width(some).height(some).appendTo('#some_target');
});

Это предварительно загружает изображение, как предлагалось ранее, а затем использует обработчик для добавления объекта img после загрузки URL-адреса img. Документация jQuery предупреждает, что кэшированные изображения плохо работают с этим кодом события / обработчика, но он работает для меня в FireFox и Chrome, и мне не нужно беспокоиться об IE.

bwinchester
источник
Это не будет хорошо работать с кэшированными изображениями, как вы узнали. Обходной путь должен сначала связать событие загрузки, а затем установить атрибут src.
Кевин Б
-1
function preload(imgs) {
    $(imgs).each(function(index, value) {
        $('<img />').attr('src', value).appendTo('body').css('display', 'none');
    });
}

.attr('src',value) не .attr('src',this)

просто чтобы указать на это :)

Whisher
источник
Область thisвнутри обратного вызова, которому передается, $.eachприсваивается значению, которое повторяется.
Ойбек
? $ (['img1.jpg', 'img2.jpg', 'img3.jpg']). each (function (index, value) {console.log (value); // img1.jpg console.log (this) ; // String {0 = "i", 1 = "m", 2 = "g", more ...} $ ('<img />'). Attr ('src', this) .appendTo (' body '). css (' display ',' none ');});
Whisher
Гектометр Я думаю, ты прямо здесь. Например $("div").each(function(i, el){ console.log(el == this);});генерирует все trues; Итерация по массиву, кажется, ведет себя по-другому.
Ойбек
-2

5 строк в coffeescript

array = ['/img/movie1.png','/img/movie2.png','/img/movie3.png']

$(document).ready ->
  for index, image of array
    img[index] = new Image()
    img[index].src = image
Гай Морита
источник
2
Можете ли вы рассказать о том, как работает решение для решения вопроса в ОП? И, возможно, прокомментируете ваш код, чтобы другие могли его легче понять?
Ro Yo Mi
-4

Для тех, кто немного знает ActionScript, вы можете с минимальными усилиями проверить flash player и сделать предварительный загрузчик flash, который вы также можете экспортировать в html5 / Javascript / Jquery. Чтобы использовать, если flash player не обнаружен, посмотрите примеры того, как сделать это с ролью youtube обратно в html5 player :) И создайте свой собственный. У меня нет подробностей, потому что я еще не начал, если я не забыл, я опубликую его позже и попробую какой-то странный код Jquery для моего.

Питер Группелаар
источник
Это не решение. Браузер не поддерживает Flash player по умолчанию. Этого легко добиться с помощью JavaScript, который является родным языком браузера.
Усман Ахмед
Я помню Flash ненавистников 2012 года ... это было ненормально ... везде, где бы я ни был, на меня нападали, когда я даже использовал слово Flash.
Питер Группелаар