Precarga de imágenes con jQuery

689

Estoy buscando una manera rápida y fácil de precargar imágenes con JavaScript. Estoy usando jQuery si eso es importante.

Vi esto aquí ( 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" });
  }
};

Pero, ¡parece un poco exagerado lo que quiero!

Sé que hay complementos de jQuery que hacen esto, pero todos parecen un poco grandes (en tamaño); ¡Solo necesito una forma rápida, fácil y corta de precargar imágenes!


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

Respuestas:

969

Rápido y 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'
]);

O, si quieres un complemento jQuery:

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

// Usage:

$(['img1.jpg','img2.jpg','img3.jpg']).preload();
James
fuente
25
¿No es necesario insertar el elemento de imagen en el DOM para garantizar que el navegador lo almacena en caché?
JoshNaro
8
Creo que $('<img />')solo crea un elemento de imagen en la memoria (ver enlace ). Parece '$('<img src="' + this + '" />')que en realidad crearía el elemento dentro de un DIV oculto, porque es más "complicado". Sin embargo, no creo que esto sea necesario para la mayoría de los navegadores.
JoshNaro
104
Esa es una forma extraña de escribir un complemento jQuery. ¿Por qué no $.preload(['img1.jpg', 'img2.jpg'])?
alex
12
Asegúrese de llamar a esto adentro $(window).load(function(){/*preload here*/});porque de esa manera todas las imágenes del documento se cargan primero, es probable que se necesiten primero.
Jasper Kennis
12
@RegionalC: ¿puede ser un poco más seguro configurar el loadevento antes de configurarlo srcen caso de que la imagen termine de cargarse antes de configurar el evento de carga? $('<img/>').load(function() { /* it's loaded! */ }).attr('src', this);
sparebytes
102

Aquí hay una versión modificada de la primera respuesta que realmente carga las imágenes en DOM y las oculta de forma predeterminada.

function preload(arrayOfImages) {
    $(arrayOfImages).each(function () {
        $('<img />').attr('src',this).appendTo('body').css('display','none');
    });
}
Dennis Rongo
fuente
38
hide()es más concisa de css('display', 'none').
alex
66
¿Cuál es la ventaja de insertarlos en el DOM?
alex
También me gustaría saber la ventaja de insertarlos en el DOM.
jedmao
11
Desde mi experiencia, la precarga de una imagen en el DOM hace que el navegador sea consciente de su existencia y que se almacene en caché correctamente. De lo contrario, la imagen solo existe en la memoria, que solo funciona para aplicaciones de una sola página.
Dennis Rongo
Dennis Rongo. Agregar imágenes al DOM arregló la recarga aleatoria en Chrome. ¡Gracias!
tmorell el
52

Usar objeto de imagen de JavaScript .

Esta función le permite activar una devolución de llamada al cargar todas las imágenes. Sin embargo, tenga en cuenta que nunca activará una devolución de llamada si no se carga al menos un recurso. Esto se puede solucionar fácilmente implementando la onerrordevolución de llamada e incrementando el loadedvalor o manejando el error.

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
fuente
44
Haga lo correcto, use el objeto de imagen de JavaScript. ¿Has observado que las personas hacen algo incorrecto en estas respuestas?
alex
Código brillante ¿Estoy en lo cierto al pensar que esto activará el onloadevento incluso si la imagen está en caché? (Porque img.onloadse declara primero). Esto es lo que mostraron mis pruebas.
Startec
3
@alex potencialmente, sí. Si el objetivo es precargar (lo que sugiere un orden de rendimiento), preferiría ver una opción JS sin formato en lugar de opciones dependientes de jQuery.
Charlie Schliesser
Me encantó esta solución, pero acabo de descubrir que no funciona en mi Firefox. ¿Soy solo yo u otros tienen el mismo problema?
Gazillion
@Gazillion da más detalles. ¿Cómo define "no funciona"? ¿Cuál es la versión FF?
Gajus
35

JP, después de verificar su solución, todavía tenía problemas en Firefox donde no precargaba las imágenes antes de continuar con la carga de la página. Descubrí esto poniendo algunos sleep(5)en mi script del lado del servidor. Implementé la siguiente solución basada en la suya que parece resolver esto.

Básicamente agregué una devolución de llamada a su complemento de precarga de jQuery, para que se llame después de que todas las imágenes se carguen correctamente.

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

Por interés, en mi contexto, estoy usando esto de la siguiente manera:

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

Esperemos que esto ayude a alguien que viene a esta página desde Google (como yo) buscando una solución para precargar imágenes en llamadas Ajax.

Dave
fuente
2
Para aquellos que no están interesados ​​en estropear el prototipo Array.En su lugar, pueden hacer: checklist = this.lengthY en la función de checklist--if (checklist == 0) callback();
carga
3
Todo estaría bien, sin embargo, agregar nuevos métodos o eliminar métodos existentes a un objeto que no es de su propiedad es una de las peores prácticas en JavaScript.
Rihards
Agradable y rápido, pero este código nunca activará la devolución de llamada si una de las imágenes está rota o no se puede cargar.
SammyK
.attr({ src: this }).load(function() {...})De lo .load(function() {...}).attr({ src: this })contrario, tendrá problemas de almacenamiento en caché.
Kevin B
25

Este código jQuery de una línea crea (y carga) un elemento DOM img sin mostrarlo:

$('<img src="img/1.jpg"/>');
rkcell
fuente
44
@huzzah: es mejor que solo uses sprites. Menos solicitudes 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;
  });
};

El uso es bastante simple:

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

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

yckart
fuente
Esta es una muy buena respuesta, realmente debería ser la respuesta más votada en mi humilde opinión.
sleepycal
1
Algunas palabras explicativas habrían sido agradables.
robsch
Buena respuesta, pero sugiero usar picsum.photos en lugar de lorempixel.com
Aleksandar
19

Tengo un pequeño complemento que maneja esto.

Se llama waitForImages y puede manejar imgelementos o cualquier elemento con una referencia a una imagen en el CSS, por ejemplo div { background: url(img.png) }.

Si simplemente desea cargar todas las imágenes, incluidas las referenciadas en el CSS, así es como lo haría :)

$('body').waitForImages({
    waitForAll: true,
    finished: function() {
       // All images have loaded.
    }  
});
alex
fuente
¿Este complemento hace que las imágenes que aún no aparecieron en la página se carguen o solo adjunta eventos a las imágenes que ya se iban a cargar?
Dave Cameron
@DaveCameron No respeta si las imágenes son visibles o no. Puede bifurcarlo fácilmente y hacer ese cambio, solo agregue :visibleal selector personalizado.
alex
Este complemento se ve muy interesante. Sin embargo, la documentación de jquery destaca que el loadevento, cuando se aplica a las imágenes: "no funciona de manera consistente ni confiable entre navegadores". ¿Cómo se las arregló el plugin para solucionarlo?
EleventyOne
@EleventyOne Verifique la fuente con respecto al selector personalizado.
alex
@alex ¿Cómo carga este complemento las imágenes que se establecen en el CSS en: desplazamiento de un elemento? No funciona para mí
Philipp Hofmann
13

puedes cargar imágenes en tu html en algún lugar usando la display:none;regla css , luego mostrarlas cuando quieras con js o jquery

no use las funciones js o jquery para precargar es solo una regla css Vs muchas líneas de js para ejecutar

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

La precarga de imágenes por jquery / javascript no es buena porque las imágenes tardan unos pocos milisegundos en cargar en la página + usted tiene milisegundos para que el script se analice y ejecute, especialmente si son imágenes grandes, por lo que ocultarlas en hml es mejor también para el rendimiento, ¡porque la imagen está realmente precargada sin ser visible en absoluto, hasta que lo demuestres!

soy yo
fuente
2
Puede encontrar más información sobre este enfoque aquí: perishablepress.com/…
gdelfino
3
Pero debe tener en cuenta que esta técnica tiene un gran inconveniente: su página no se cargará por completo hasta que se carguen todas las imágenes. Dependiendo de la cantidad de imágenes a precargar y su tamaño, esto podría llevar algún tiempo. Peor aún, si la etiqueta <img> no especifica una altura y un ancho, algunos navegadores pueden esperar hasta que se obtenga la imagen antes de mostrar el resto de la página.
@Alex, de todos modos, necesita cargar el img, puede elegir si cargarlos con html y evitar cualquier tipo de imágenes parpadeantes y medio cargadas, o si desea obtener más velocidad para una solución no estable
es
También creo que la creación de etiquetas html con javascript es ilegible en absoluto, solo mis 2 centavos
itsme
1
Necesitaba una solución muy rápida para un proyecto muy complejo y esto fue todo.
Tormenta germinal
13

este complemento jquery imageLoader es solo 1.39kb

uso:

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

También hay otras opciones, como si desea cargar las imágenes de forma síncrona o asíncrona y un evento completo para cada imagen individual.

alzclarke
fuente
@AamirAfridi ¿Por qué es eso?
Ian
1
@Ian porque el documento ya está listo cuando se activa la devolución de llamada. $ (document) .ready se usa para asegurarse de que su DOM esté cargado. Cuando se trata de la devolución de llamada, eso significa que todas las imágenes están cargadas, lo que significa que su DOM está cargado, por lo que no es necesario document.ready dentro de una devolución de llamada.
Aamir Afridi
1
@AamirAfridi No hay garantía de que el documento esté listo en la devolución de llamada ... ¿de dónde deduce eso? No hay nada en la página del complemento que diga que la allcompletedevolución de llamada se ejecuta después de que el DOM esté listo. Existe una buena posibilidad de que las imágenes terminen de cargarse después de que el DOM esté listo, pero no hay razón para suponer. No sé por qué piensas que las devoluciones de llamada son mágicas y se ejecutan después de que el DOM está listo ... ¿puedes explicar de dónde lo sacas? El hecho de que las imágenes estén cargadas no significa que el DOM esté listo
Ian
@AamirAfridi Y la documentación del complemento incluso dice: NB to use this as an image preloader simply put your $(document).ready(function(){}); into your 'allcomplete' event : > simples!... recomienda directamente lo que muestra esta respuesta.
Ian
55
@AamirAfridi Eso no tiene sentido para esta situación. El complemento es un precargador . Desea ejecutarlo lo antes posible. Y hay muchas cosas que no dependen del DOM que desea disparar lo antes posible, y luego, cuando el DOM esté listo, haga algo.
Ian
9

Una forma rápida y sin complementos de precargar imágenes en jQuery y obtener una función de devolución de llamada es crear varias imgetiquetas a la vez y contar las respuestas, por ejemplo

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

Tenga en cuenta que si desea admitir IE7, deberá usar esta versión un poco menos bonita (que también funciona en otros 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
fuente
7

¡Gracias por esto! Me gustaría agregar un pequeño riff en la respuesta del JP: no sé si esto ayudará a alguien, pero de esta manera no tiene que crear una matriz de imágenes, y puede precargar todas sus imágenes grandes si nombra tus pulgares correctamente. Esto es útil porque tengo a alguien que está escribiendo todas las páginas en html, y garantiza un paso menos para ellos, eliminando la necesidad de crear la matriz de imágenes y otro paso en el que las cosas podrían arruinarse.

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

Básicamente, para cada imagen en la página, toma el src de cada imagen, si coincide con ciertos criterios (es un pulgar o imagen de la página de inicio) cambia el nombre (una cadena básica reemplaza en el src de la imagen), luego carga las imágenes .

En mi caso, la página estaba llena de imágenes de pulgar, todas denominadas algo así como image_th.jpg, y todas las imágenes grandes correspondientes se denominan image_lg.jpg. El pulgar a grande simplemente reemplaza el _th.jpg con _lg.jpg y luego precarga todas las imágenes grandes.

Espero que esto ayude a alguien.

dgig
fuente
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
fuente
77
¡Bienvenido a Stack Overflow! En lugar de solo publicar un bloque de código, explique por qué este código resuelve el problema planteado. Sin una explicación, esto no es una respuesta.
Martijn Pieters
3

Yo uso el siguiente 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
fuente
Enlaza primero al evento de carga, luego configura el src.
Kevin B
1

Usaría un archivo de manifiesto para indicarle a los navegadores web (modernos) que también carguen todas las imágenes relevantes y las almacenen en caché. Utilice Grunt y grunt-manifest para hacer esto automáticamente y nunca más se preocupe por los scripts de precarga, los invalidadores de caché, CDN, etc.

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

Christian Landgren
fuente
0

Esto funciona para mí incluso en IE9:

$('<img src="' + imgURL + '"/>').on('load', function(){ doOnLoadStuff(); });
Zymotik
fuente
1
Esto fallará eventualmente debido al almacenamiento en caché, siempre enlace el evento de carga antes de configurar el atributo src.
Kevin B
0

Quería hacer esto con una superposición personalizada de la API de Google Maps. Su código de muestra simplemente usa JS para insertar elementos IMG y el cuadro de marcador de posición de la imagen se muestra hasta que se carga la imagen. Encontré una respuesta aquí que funcionó para mí: https://stackoverflow.com/a/10863680/2095698 .

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

Esto precarga una imagen como se sugirió anteriormente, y luego usa el controlador para agregar el objeto img después de cargar la URL img. La documentación de jQuery advierte que las imágenes en caché no funcionan bien con este código de evento / controlador, pero me funciona en Firefox y Chrome, y no tengo que preocuparme por IE.

bwinchester
fuente
Esto no funcionará bien con las imágenes en caché, como ya lo descubrió. la solución es vincular primero el evento de carga y luego establecer el 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) no .attr('src',this)

solo para señalarlo :)

Whisher
fuente
El alcance thisdentro de la devolución de llamada que se pasa $.eachse asigna al valor que se está iterando.
Oybek
? $ (['' img1.jpg ',' img2.jpg ',' img3.jpg ']). each (function (index, value) {console.log (value); // img1.jpg console.log (this) ; // Cadena {0 = "i", 1 = "m", 2 = "g", más ...} $ ('<img />'). Attr ('src', this) .appendTo (' body '). css (' display ',' none ');});
Whisher
Hm. Supongo que estás aquí. Por ejemplo, $("div").each(function(i, el){ console.log(el == this);});genera todos los trues; La iteración sobre la matriz parece comportarse de manera diferente.
Oybek
-2

5 líneas en 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
fuente
2
¿Puede ampliar cómo funciona la solución para resolver la pregunta en el OP? ¿Y posiblemente comentar su código para que otros puedan entenderlo más fácilmente?
Ro Yo Mi
-4

Para aquellos que conocen un poco de actionscript, puede verificar flash player, con un mínimo esfuerzo, y hacer un precargador flash, que también puede exportar a html5 / Javascript / Jquery. Para usar si no se detecta el reproductor flash, consulte los ejemplos sobre cómo hacer esto con el rol de youtube de vuelta al reproductor html5 :) Y cree el suyo propio. No tengo los detalles, porque aún no he comenzado, si no lo olvido, lo publicaré más tarde y probaré un código Jquery estándar para el mío.

Peter Gruppelaar
fuente
Esta no es una solución. El navegador no admite Flash player de forma predeterminada. Se puede lograr fácilmente con JavaScript, que es el idioma nativo del navegador.
Usman Ahmed
Recuerdo que los enemigos de Flash de 2012 ... no era normal ... donde quiera que fui fui atacado cuando incluso usé la palabra Flash.
Peter Gruppelaar