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

Tom Lehman
fuente
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.

Nick Craver
fuente
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') ;
    }) ;
}) ;
Gus
fuente
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);
});
guari
fuente
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.

Björn
fuente
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();
    }
});
Piotr Stępniewski
fuente
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.

Sammaye
fuente
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
});
mella
fuente
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;
AllisonC
fuente
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.

Chuck Conway
fuente
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
Bjørn T. Dahl
fuente
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.

Josephy Besim
fuente
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).

Ali
fuente
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" />
sebarmeli
fuente
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

neoRiley
fuente
¿Podría dar un ejemplo de cómo funciona esto con la carga de imágenes?
Hastig Zusammenstellen