Devolución de llamada jQuery al cargar la imagen (incluso cuando la imagen está en caché)


291

Quiero hacer:

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

Pero el evento de carga no se dispara cuando la imagen se carga desde la memoria caché. Los documentos de jQuery sugieren un complemento para solucionar esto, pero no funciona


12
Desde su pregunta, las cosas han cambiado. El complemento roto se movió a una esencia y luego a un repositorio con un pequeño complemento de trabajo jQuery.imagesLoaded . Arreglan todas las pequeñas peculiaridades del navegador .
Lode

La biblioteca JQuery mencionada anteriormente funcionó bien para mí. ¡Gracias!
plang

Gracias por el complemento, funcionó bien.
onimojo

Respuestas:


572

Si srcya está configurado, entonces el evento se activa en el caso en caché, incluso antes de que el controlador de eventos esté vinculado. Para solucionar esto, puede recorrer la comprobación y desencadenar el evento según .completeesto:

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

Tenga en cuenta el cambio de .bind()a .one()para que el controlador de eventos no se ejecute dos veces.


8
Su solución funcionó perfectamente para mí, pero quiero entender algo, ¿cuándo se ejecutará este código "if (this.complete)", después de que se cargue el contenido de la imagen o antes? porque, como puedo entender de esto. cada uno de ustedes está haciendo un bucle en todo $ ("img") y puede ser que el contenido de la imagen esté vacío y la carga no sucederá. hmmmm, creo que me falta algo, sería bueno si puedes describir eso para entenderlo mejor. Gracias.
Amr Elgarhy

33
Desde su respuesta, las cosas han cambiado. Ahora hay un pequeño complemento de trabajo jQuery.imagesLoaded . Arreglan todas las pequeñas peculiaridades del navegador .
Lode

3
Agregaría un setTimeout(function(){//do stuff},0)dentro de la función 'cargar' ... el 0 le da al sistema del navegador (DOM) una marca adicional para garantizar que todo se actualice correctamente.
dsdsdsdsd

14
@Lode: ¡es un plugin ENORME, no es pequeño en absoluto a cualquier escala!
vsync

55
dado que el método JQuery 3 .load()no activa el evento y tiene 1 argumento requerido, use .trigger("load")en su lugar
ForceUser

48

¿Puedo sugerirle que lo vuelva a cargar en un objeto de imagen que no sea DOM? Si está en caché, esto no tomará tiempo en absoluto, y la carga seguirá disparando. Si no está en caché, disparará la carga cuando se cargue la imagen, que debería ser al mismo tiempo que la versión DOM de la imagen termina de cargarse.

Javascript:

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

Actualizado (para manejar múltiples imágenes y con el archivo adjunto de carga correctamente ordenado):

$(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
Debería intentar adjuntar el loadcontrolador antes de que se agregue, también esto no funcionará, pero para una imagen, el código OP funciona para muchos :)
Nick Craver

Gracias, he agregado una versión actualizada que espero aborde estos dos problemas.
Gus

#imges una ID, no un selector de elementos :) También this.srcfunciona, no es necesario usar jQuery donde no se necesita :) Pero crear otra imagen parece exagerado en cualquier caso IMO.
Nick Craver

debería ser $ ('img'). pero la solución es gr8 en 2k15 también
Dimag Kharab

Además, para el caso de múltiples imágenes, si desea operar en el elemento DOM para cada imagen imageLoaded, usetmp.onload = imageLoaded.bind(this)
blisstdev el

38

Mi solución simple, no necesita ningún complemento externo y para casos comunes debería ser suficiente:

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

úsalo así:

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

por ejemplo, para desvanecerse en sus imágenes en carga puede hacer:

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

O como alternativa, si prefiere un enfoque tipo 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);

y úsalo de esta manera:

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

por ejemplo:

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

urm ... ¿y si mis imágenes tienen un tamaño establecido con css?
Simon_Weaver

Conjunto width, max-heighty la licencia height:auto, a menudo es necesario para establecer la anchura y la altura sólo si es necesario para estirar la imagen; para una solución más robusta, usaría un complemento como ImagesLoaded
guari

1
sí, fue un error de escritura, jsdoc estaba bien,
corrigí

1
Esta solución con $ .fn.imgLoad funciona en todos los navegadores. La solución será aún mejor: && / * para IE 10 - * / this.naturalHeight> 0 Esta línea no tendrá en cuenta el estilo CSS porque img puede bloquearse pero tener altura. Trabajo en IE10
Patryk Padus

1
Esto tiene una condición de carrera (pequeña). La imagen se puede cargar en un hilo diferente al script y, por lo tanto, se puede cargar entre la verificación de finalización y la conexión del evento de carga, en cuyo caso no sucederá nada. Normalmente JS está sincronizado con el renderizado, por lo que no tiene que preocuparse por las condiciones de carrera, pero esta es una excepción. html.spec.whatwg.org/multipage/…
Mog0

23

¿Realmente tienes que hacerlo con jQuery? También puede adjuntar el onloadevento directamente a su imagen;

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

Se disparará cada vez que la imagen se haya cargado, desde caché o no.


55
Es más limpio sin JavaScript en línea
avellana

1
Hola, Bjorn, ¿se activará el onloadcontrolador cuando la imagen se cargue por segunda vez desde el caché?
SexyBeast

1
@Cupidvogel no, no lo hará.
Anuncios

3
Amigo, me salvaste la vida, he probado al menos una docena de otras soluciones y la tuya es la única que realmente funcionó. Estoy pensando seriamente en crear 10 cuentas más para votar su respuesta 10 veces más. En serio gracias!
Carlo

1
Nunca he entendido la aversión de la gente a JS en línea. La carga de imágenes ocurre por separado y fuera de secuencia del resto del proceso de carga de la página. Por lo tanto, esta es la única solución válida para obtener comentarios inmediatos sobre la finalización de la carga de imágenes. Sin embargo, si alguien necesita esperar a que se cargue jQuery y eso sucede más tarde, entonces no tendrá que usar la solución simple aquí y usar una de las otras soluciones (Piotr es probablemente la mejor opción ya que no depende de pseudo- hacks pero la verificación de altura () de guari también puede ser importante). Una cola de procesamiento también podría ser útil.
CubicleSoft

16

También puede usar este código con soporte para errores de carga:

$("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
Este funcionó mejor para mí. Creo que la clave es configurar loadprimero el controlador y luego llamarlo más tarde en la eachfunción.
GreenRaccoon23

configurando la imagen usando el siguiente código '' var img = new Image (); img.src = sosImgURL; $ (img) .on ("load", function () {''
imdadhusen

13

Yo mismo tuve este problema, busqué en todas partes una solución que no implicara matar mi caché o descargar un complemento.

No vi este hilo inmediatamente, así que encontré algo más que es una solución interesante y (creo) digna de publicar aquí:

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

De hecho, obtuve esta idea de los comentarios aquí: http://www.witheringtree.com/2009/05/image-load-event-binding-with-ie-using-jquery/

No tengo idea de por qué funciona, pero lo he probado en IE7 y dónde se rompió antes de que ahora funcione.

Espero eso ayude,

Editar

La respuesta aceptada en realidad explica por qué:

Si el src ya está configurado, entonces el evento se dispara en la caja de la caché antes de que el controlador de eventos esté vinculado.


44
He probado que esto está en muchos navegadores y no he encontrado que falle en ningún lado. Solía $image.load(function(){ something(); }).attr('src', $image.attr('src'));restablecer la fuente original.
Maurice

He descubierto que este enfoque no funciona en iOS, al menos para Safari / 601.1 y Safari / 602.1
cronfy

Sería increíble saber por qué, ¿en qué navegador está?
Sammaye

5

Al usar jQuery para generar una nueva imagen con el src de la imagen, y asignar el método de carga directamente a eso, se llama con éxito al método de carga cuando jQuery termina de generar la nueva imagen. Esto funciona para mí en IE 8, 9 y 10

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

Para aquellos que usan Ruby on Rails, con solicitud remota, usando plantillas __rjs.erb, esta es la única solución que he encontrado. Debido al uso de parciales en js.erb, pierde el enlace ($ ("# img" .on ("load") ... y para las imágenes NO es posible delegar el método "on": $ ("body"). on ("load", "# img", function () ...
Albert Català

5

Una solución que encontré https://bugs.chromium.org/p/chromium/issues/detail?id=7731#c12 (Este código se tomó directamente del comentario)

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

Esto es exactamente lo que estaba buscando. Me llega una sola imagen del servidor. Quería precargarlo y luego servir. No selecciono todas las imágenes como hacen muchas respuestas. Bueno
Kai Qing

4

Una modificación al ejemplo de GUS:

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

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

Establezca la fuente antes y después de la carga.


Al igual que la respuesta de @ Gus, esto no funcionará para varias imágenes ... y no es necesario configurarlas srcantes de adjuntar el onloadcontrolador, una vez después será suficiente.
Nick Craver

3

Simplemente vuelva a agregar el argumento src en una línea separada después de definir el objeto img. Esto engañará a IE para que active el evento lad. Es feo, pero es la solución más simple que he encontrado hasta ahora.

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

1

Te puedo dar un pequeño consejo si quieres hacer esto:

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

Si hace eso cuando el navegador almacena imágenes en caché, no hay problema, siempre se muestra la imagen, pero se carga debajo de la imagen real.


1

Tuve este problema con IE donde e.target.width no estaría definido. El evento de carga se dispararía, pero no pude obtener las dimensiones de la imagen en IE (Chrome + FF funcionó).

Resulta que debes buscar e.currentTarget.naturalWidth & e.currentTarget.naturalHeight .

Una vez más, IE hace las cosas de manera propia (más complicada).


0

Puede resolver su problema utilizando el complemento JAIL que también le permite cargar imágenes de manera diferida (mejorando el rendimiento de la página) y pasando la devolución de llamada como parámetro

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

El HTML debería verse así

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

0

Si desea una solución CSS pura, este truco funciona muy bien: use el objeto de transformación. Esto también funciona con imágenes cuando están en caché o no:

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

Ejemplo de codepen

Codepen MENOS ejemplo


¿Podría dar un ejemplo de cómo funciona esto con la carga de imágenes?
Hastig Zusammenstellen
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.