Pré-carregando imagens com o jQuery

689

Estou procurando uma maneira rápida e fácil de pré-carregar imagens com JavaScript. Estou usando jQuery se isso é importante.

Eu vi isso aqui ( 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" });
  }
};

Mas, parece um pouco exagerado para o que eu quero!

Eu sei que existem plugins jQuery por aí que fazem isso, mas todos parecem um pouco grandes (em tamanho); Eu só preciso de uma maneira rápida, fácil e curta de pré-carregar imagens!


fonte
10
$.each(arguments,function(){(new Image).src=this});
David Hellsing

Respostas:

969

Rápido e fácil:

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'
]);

Ou, se você quiser um plugin jQuery:

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

// Usage:

$(['img1.jpg','img2.jpg','img3.jpg']).preload();
James
fonte
25
O elemento de imagem não precisa ser inserido no DOM para garantir que o navegador o armazene em cache?
JoshNaro
8
Eu acredito que $('<img />')apenas cria um elemento de imagem na memória (veja o link ). Parece '$('<img src="' + this + '" />')que realmente criaria o elemento dentro de uma DIV oculta, porque é mais "complicado". No entanto, não acho que isso seja necessário para a maioria dos navegadores.
21711 JoshNaro
104
Essa é uma maneira estranha de escrever um plugin jQuery. Por que não $.preload(['img1.jpg', 'img2.jpg'])?
alex
12
Lembre-se de chamar isso $(window).load(function(){/*preload here*/});por dentro, pois dessa maneira todas as imagens no documento são carregadas primeiro, é provável que sejam necessárias primeiro.
Jasper Kennis
12
@RegionalC - Pode ser um pouco mais seguro definir o loadevento antes de definir o srccaso de a imagem terminar de carregar antes de o evento de carregamento ser definido? $('<img/>').load(function() { /* it's loaded! */ }).attr('src', this);
Sparebytes
102

Aqui está uma versão aprimorada da primeira resposta que realmente carrega as imagens no DOM e as oculta por padrão.

function preload(arrayOfImages) {
    $(arrayOfImages).each(function () {
        $('<img />').attr('src',this).appendTo('body').css('display','none');
    });
}
Dennis Rongo
fonte
38
hide()é mais conciso que css('display', 'none').
22411 alex
6
Qual é a vantagem de inseri-los no DOM?
alex
Eu também gostaria de saber a vantagem de inseri-los no DOM.
jedmao
11
Pela minha experiência, o pré-carregamento de uma imagem no DOM torna o navegador ciente de sua existência e para que seja armazenado em cache corretamente. Caso contrário, a imagem existe apenas na memória, que funciona apenas para aplicativos de página única.
Dennis Rongo 04/07
Dennis Rongo. A adição de imagens ao DOM corrigiu o carregamento aleatório no Chrome. Obrigado!
tmorell
52

Use o objeto JavaScript Image .

Esta função permite ativar um retorno de chamada ao carregar todas as imagens. No entanto, observe que ele nunca acionará um retorno de chamada se pelo menos um recurso não estiver carregado. Isso pode ser facilmente corrigido implementando onerrorretorno de chamada e incrementando loadedvalor ou manipulando o erro.

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
fonte
4
Faça a coisa certa, use o objeto JavaScript Image. você observou pessoas fazendo a coisa errada nessas respostas?
alex
Código brilhante. Estou correto ao pensar que isso disparará o onloadevento mesmo se a imagem estiver em cache? (Porque img.onloadé declarado primeiro). Isto é o que meus testes mostraram.
Startec
3
@alex potencialmente, sim. Se o objetivo é pré-carregar (o que sugere uma ordem de desempenho), eu preferiria ver uma opção JS bruta em vez de opções dependentes de jQuery.
Charlie Schliesser
Adorei esta solução, mas descobri que ela não funciona no meu Firefox. Sou apenas eu ou outras pessoas estão tendo o mesmo problema?
Gazillion
@Gazillion dá mais detalhes. Como você define "não está funcionando"? Qual é a versão FF?
Gajus 20/08/14
35

JP, Depois de verificar sua solução, eu ainda estava tendo problemas no Firefox, onde ele não carregava as imagens antes de prosseguir com o carregamento da página. Eu descobri isso colocando alguns sleep(5)no meu script do lado do servidor. Eu implementei a seguinte solução com base na sua, que parece resolver isso.

Basicamente, adicionei um retorno de chamada ao seu plugin de pré-carregamento do jQuery, para que ele seja chamado depois que todas as imagens forem carregadas corretamente.

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

Fora de interesse, no meu contexto, estou usando isso da seguinte maneira:

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

Espero que isso ajude alguém que chega a esta página do Google (como eu) procurando uma solução para pré-carregar imagens nas chamadas do Ajax.

Dave
fonte
2
Para aqueles que não estão interessados em bagunçar o Array.prototype, em vez disso, você pode fazer: checklist = this.lengthE na função onload: checklist--Então:if (checklist == 0) callback();
MiniGod
3
Tudo ficaria bem, no entanto, adicionar novos ou remover métodos existentes a um objeto que você não possui é uma das piores práticas em JavaScript.
21116 Rihards
Bom e rápido, mas esse código nunca acionará o retorno de chamada se uma das imagens estiver quebrada ou impossibilitada de carregar.
SammyK 30/10/12
.attr({ src: this }).load(function() {...})caso .load(function() {...}).attr({ src: this })contrário, você terá problemas de cache.
Kevin B
25

Esse código jQuery de uma linha cria (e carrega) um elemento DOM img sem mostrá-lo:

$('<img src="img/1.jpg"/>');
rkcell
fonte
4
@huzzah - Você pode estar melhor usando apenas sprites. Menos solicitações de 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;
  });
};

O uso é bastante simples:

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

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

yckart
fonte
Esta é uma resposta muito boa, realmente deve ser a resposta mais votada.
sleepycal
1
Algumas palavras explicativas teriam sido legais.
robsch
Boa resposta, mas eu sugiro usar o picsum.photos em vez de lorempixel.com #
Aleksandar
19

Eu tenho um pequeno plugin que lida com isso.

É chamado waitForImages e pode manipular imgelementos ou qualquer elemento com referência a uma imagem no CSS, por exemplo div { background: url(img.png) }.

Se você simplesmente deseja carregar todas as imagens, incluindo as referenciadas no CSS, eis como você faria isso :)

$('body').waitForImages({
    waitForAll: true,
    finished: function() {
       // All images have loaded.
    }  
});
alex
fonte
Esse plug-in faz com que as imagens que ainda não apareceram na página ainda sejam carregadas ou apenas anexa eventos às imagens que já estavam sendo carregadas?
Dave Cameron
@DaveCameron Não respeita se as imagens são visíveis ou não. Você pode facilmente bifurcar e fazer essa alteração - basta adicionar :visibleao seletor personalizado.
alex
Este plugin parece muito interessante. No entanto, a documentação do jquery destaca que o loadevento, quando aplicado às imagens: "não funciona de maneira consistente nem confiável no navegador cruzado". Como o plugin conseguiu contornar isso?
EleventyOne 28/10
@EleventyOne Verifique a fonte em relação ao seletor personalizado.
28283 alex
@alex Como esse plug-in carrega imagens definidas no CSS em: pairar um elemento? Não funciona para mim
Philipp Hofmann
13

você pode carregar imagens em seu html em algum lugar usando a display:none;regra css e depois mostrá-las quando quiser com js ou jquery

não use funções js ou jquery para pré-carregar é apenas uma regra css versus muitas linhas de js a serem executadas

exemplo: 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();

O pré-carregamento de imagens por jquery / javascript não é bom, pois as imagens levam alguns milissegundos para carregar na página + você tem milissegundos para o script ser analisado e executado, especialmente se forem imagens grandes, por isso é melhor também ocultá-las em hml para desempenho, porque a imagem é realmente pré-carregada sem ser visível, até você mostrar isso!

sou eu
fonte
2
Mais informações sobre essa abordagem podem ser encontradas aqui: perishablepress.com/…
gdelfino
3
Mas você precisa estar ciente de que esta técnica tem uma grande desvantagem: sua página não será completamente carregada até que todas as imagens sejam carregadas. Dependendo do número de imagens a serem pré-carregadas e de seu tamanho, isso pode levar algum tempo. Pior ainda, se a tag <img> não especificar uma altura e largura, alguns navegadores poderão esperar até que a imagem seja buscada antes de renderizar o restante da página.
@ Alex, de qualquer forma, você precisa carregar o img, você pode escolher se deseja carregá-los com html e evitar qualquer tipo de imagem piscante e meio carregada, ou se você quiser obter mais velocidade para uma solução não estável
itsme
Também eu realmente acho que a criação de tags de html com javascript está ilegível em todos os apenas meus 2 centavos
itsme
1
Eu precisava de uma solução muito rápida para um projeto muito complexo e era isso.
Germstorm
13

este plugin jquery imageLoader tem apenas 1,39kb

uso:

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

também existem outras opções, como se você deseja carregar as imagens de maneira síncrona ou assíncrona e um evento completo para cada imagem individual.

alzclarke
fonte
@AamirAfridi Por que isso?
Ian
1
@Ian porque o documento já está pronto no momento em que o retorno de chamada é acionado. $ (document) .ready é usado para garantir que o seu DOM esteja carregado. Quando se trata de retorno de chamada, isso significa que todas as imagens são carregadas, o que significa que o DOM está carregado, portanto, não há necessidade de document.ready dentro de um retorno de chamada.
Aamir Afridi
1
@AamirAfridi Não há garantia de que o documento esteja pronto no retorno de chamada ... de onde você está deduzindo isso? Não há nada na página do plug-in que diga que o allcompleteretorno de chamada é executado após o DOM estar pronto. Há uma boa chance de as imagens terminarem de carregar depois que o DOM estiver pronto, mas não há motivo para supor. Não sei por que você acha que os retornos de chamada são mágicos e são executados após o DOM estar pronto ... você pode explicar de onde está obtendo isso? Só porque as imagens são carregadas não significa que o DOM está pronto
Ian
@AamirAfridi E a documentação do plugin ainda diz: NB to use this as an image preloader simply put your $(document).ready(function(){}); into your 'allcomplete' event : > simples!... recomenda diretamente o que esta resposta mostra.
Ian
5
@AamirAfridi Isso não faz sentido para esta situação. O plugin é um pré- carregador. Você deseja executá-lo o mais rápido possível. E há muitas coisas que não dependem do DOM que você deseja acionar o mais rápido possível e , quando o DOM estiver pronto, faça alguma coisa.
Ian
9

Uma maneira rápida e sem plug-ins de pré-carregar imagens no jQuery e obter uma função de retorno de chamada é criar várias imgtags ao mesmo tempo e contar as respostas, por exemplo

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

Observe que se você deseja oferecer suporte ao IE7, precisará usar esta versão um pouco menos bonita (que também funciona em outros navegadores):

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

Obrigado por isso! Eu gostaria de acrescentar um pouco da resposta do JP - não sei se isso vai ajudar alguém, mas dessa forma você não precisa criar uma série de imagens e pode pré-carregar todas as imagens grandes, se quiser. nomeie seus polegares corretamente. Isso é útil porque eu tenho alguém que está escrevendo todas as páginas em html, e isso garante menos uma etapa para elas - eliminando a necessidade de criar a matriz de imagens e outra etapa em que as coisas podem estragar tudo.

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

Basicamente, para cada imagem na página, ele pega o src de cada imagem; se corresponder a determinados critérios (é um polegar ou imagem da página inicial), ele altera o nome (uma sequência básica substituída na imagem src) e carrega as imagens. .

No meu caso, a página estava cheia de imagens em miniatura, todas nomeadas como image_th.jpg, e todas as imagens grandes correspondentes são nomeadas image_lg.jpg. O polegar para grande substitui o _th.jpg por _lg.jpg e pré-carrega todas as imagens grandes.

Espero que isso ajude alguém.

dgig
fonte
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
fonte
7
Bem-vindo ao Stack Overflow! Em vez de publicar apenas um bloco de código, explique por que esse código resolve o problema apresentado. Sem uma explicação, isso não é uma resposta.
Martijn Pieters
3

Eu uso o seguinte código:

$("#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
fonte
Ligue primeiro ao evento de carregamento e, em seguida, defina o src.
Kevin B
1

Eu usaria um arquivo de manifesto para informar aos navegadores da web (modernos) que também carregassem todas as imagens relevantes e as armazenassem em cache. Use Grunt e grunt-manifest para fazer isso automaticamente e nunca mais se preocupe com scripts de pré-carregamento, invalidadores de cache, CDN etc.

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

Christian Landgren
fonte
0

Isso funciona para mim, mesmo no IE9:

$('<img src="' + imgURL + '"/>').on('load', function(){ doOnLoadStuff(); });
Zymotik
fonte
1
Eventualmente, isso falhará devido ao armazenamento em cache; sempre vincule o evento load antes de definir o atributo src.
Kevin B
0

Eu queria fazer isso com uma sobreposição personalizada da API do Google Maps. Seu código de amostra simplesmente usa JS para inserir elementos IMG e a caixa de espaço reservado para a imagem é exibida até a imagem ser carregada. Encontrei uma resposta aqui que funcionou para mim: https://stackoverflow.com/a/10863680/2095698 .

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

Isso pré-carrega uma imagem como sugerido anteriormente e, em seguida, usa o manipulador para anexar o objeto img após o carregamento do URL do img. A documentação do jQuery alerta que as imagens armazenadas em cache não funcionam bem com esse código de manipulador de eventos, mas está funcionando para mim no FireFox e Chrome, e não preciso me preocupar com o IE.

bwinchester
fonte
Isso não funcionará bem com imagens em cache, como você descobriu. a solução alternativa é ligar o evento load primeiro e, em seguida, defina o atributo src.
Kevin B
-1
function preload(imgs) {
    $(imgs).each(function(index, value) {
        $('<img />').attr('src', value).appendTo('body').css('display', 'none');
    });
}

.attr('src',value) não .attr('src',this)

apenas para apontar :)

Whisher
fonte
O escopo thisdentro do retorno de chamada passado $.eaché atribuído ao valor que está sendo iterado.
Oybek
? $ (['img1.jpg', 'img2.jpg', 'img3.jpg']). each (function (index, value) {console.log (value); // img1.jpg console.log (este) ; // String {0 = "i", 1 = "m", 2 = "g", mais ...} $ ('<img />'). Attr ('src', this) .appendTo (' corpo '). css (' display ',' none ');});
Whisher
Hum. Eu acho que você está bem aqui. Por exemplo, $("div").each(function(i, el){ console.log(el == this);});gera todos os trues; A iteração sobre a matriz parece se comportar de maneira diferente.
Oybek
-2

5 linhas em 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
Guy Morita
fonte
2
Você pode expandir como a solução funciona para resolver a questão no OP? E, possivelmente, comente seu código para que outras pessoas possam entendê-lo com mais facilidade?
Ro Yo Mi
-4

Para quem conhece um pouco do actionscript, é possível verificar o flash player, com o mínimo esforço, e criar um pré-carregador de flash, que também pode ser exportado para html5 / Javascript / Jquery. Para usar se o flash player não for detectado, verifique exemplos de como fazer isso com a função do youtube de volta ao html5 player :) E crie o seu. Eu não tenho os detalhes, porque ainda não comecei, se eu não me esquecer, publicarei mais tarde e tentarei algum código Jquery padrão para o meu.

Peter Gruppelaar
fonte
Esta não é uma solução. O navegador não suporta o Flash player por padrão. Pode ser facilmente alcançado com JavaScript, que é o idioma nativo do navegador.
Usman Ahmed
Lembro-me dos inimigos do Flash de 2012 ... não era normal ... em todos os lugares que eu fui, fui atacado quando até usei a palavra Flash.
Peter Gruppelaar