Retorno de chamada do jQuery no carregamento da imagem (mesmo quando a imagem é armazenada em cache)

291

Eu quero fazer:

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

Mas o evento load não dispara quando a imagem é carregada do cache. Os documentos do jQuery sugerem um plugin para corrigir isso, mas não funciona

Tom Lehman
fonte
12
Desde a sua pergunta, as coisas mudaram. O plugin quebrado foi movido para uma essência e, posteriormente, para um repositório com um pequeno plugin de trabalho jQuery.imagesLoaded . Eles corrigem todas as pequenas peculiaridades do navegador .
Lode
A biblioteca JQuery mencionada acima funcionou bem para mim. Obrigado!
plang
Obrigado pelo plugin, funcionou bem.
Onimojo

Respostas:

572

Se o srcjá estiver definido, o evento será disparado no caso em cache, antes mesmo de você vincular o manipulador de eventos. Para corrigir isso, você pode percorrer a verificação e o acionamento do evento com base .complete, assim:

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

Observe a alteração de .bind()para .one()para que o manipulador de eventos não seja executado duas vezes.

Nick Craver
fonte
8
Sua solução funcionou perfeitamente para mim, mas quero entender alguma coisa, quando esse código "if (this.complete)" será executado, após o carregamento do conteúdo da imagem ou antes? porque, pelo que entendi, cada. que você está repetindo todos os $ ("img") e pode ser que o conteúdo da imagem esteja vazio e a carga não aconteça. hmmmm, acho que falta algo, será bom se você descrever o que está acontecendo para entender melhor. obrigado.
Amr Elgarhy
33
Desde a sua resposta, as coisas mudaram. Agora existe um pequeno plugin de trabalho jQuery.imagesLoaded . Eles corrigem todas as pequenas peculiaridades do navegador .
Lode
3
Eu adicionaria um setTimeout(function(){//do stuff},0)dentro da função 'carregar' ... o 0 dá ao sistema do navegador (DOM) uma marca extra para garantir que tudo seja atualizado corretamente.
dsdsdsdsd
14
@Ode - é um plugin enorme, não é pequeno em tudo por qualquer escala!
vsync 5/05
5
desde 3 JQuery método .load()não faz evento de disparo e tem um argumento necessário, utilizar .trigger("load")em vez disso
ForceUser
48

Posso sugerir que você o recarregue em um objeto de imagem que não seja do DOM? Se estiver armazenado em cache, isso não levará tempo e a carga ainda será acionada. Se não estiver armazenado em cache, disparará a carga quando a imagem for carregada, que deve ser a mesma hora que a versão DOM da imagem termina o carregamento.

Javascript:

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

Atualizado (para lidar com várias imagens e com o anexo onload ordenado corretamente):

$(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
fonte
1
Você deve tentar anexar o loadmanipulador antes de adicioná-lo, mas isso não funcionará, mas para uma imagem, o código de OP funciona para muitos :)
Nick Craver
Obrigado, adicionei uma versão atualizada que, espero, aborda esses dois problemas.
Gus
#imgé um ID, não um seletor de elementos :) Também this.srcfunciona, não é necessário usar o jQuery onde não é necessário :) Mas criar outra imagem parece exagerar nos dois casos IMO.
Nick Craver
deve ser $ ('img'). mas é gr8 solução em 2K15 também
Dimag Kharab
Além disso, para o caso de várias imagens, se você deseja operar o elemento DOM para cada imagem imageLoaded, usetmp.onload = imageLoaded.bind(this)
blisstdev
38

Minha solução simples, ele não precisa de nenhum plugin externo e, para casos comuns, deve 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);
            });
        }
    });
};

use-o assim:

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

por exemplo, para desbotar suas imagens no carregamento, você pode:

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

Ou, como alternativa, se você preferir uma abordagem semelhante ao plugin 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);

e use-o desta maneira:

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

por exemplo:

$('img').hide().imgLoad(function(){
    $(this).fadeIn(700);
});
guari
fonte
urm .. e se minhas imagens tiverem um tamanho definido com css?
Simon_Weaver
Set width, max-heighte licença height:auto, muitas vezes é necessário para definir a largura e altura apenas se você precisa para esticar a imagem; para uma solução mais robusta eu iria usar um plugin como ImagesLoaded
Guari
1
sim, era um mistyping, JSDoc foi ok, eu corrigi o exemplo da linha
Guari
1
Esta solução com $ .fn.imgLoad funciona entre navegadores. A correção será ainda melhor: && / * para IE 10 - * / this.naturalHeight> 0 Esta linha não levará em consideração o estilo css porque o img pode ser bloqueado, mas tem altura. Trabalho no IE10
Patryk Padus
1
Isso tem uma (pequena) condição de corrida. A imagem pode estar carregando em um encadeamento diferente do script e também pode carregar entre a verificação de concluir e anexar o evento onload, caso em que nada acontecerá. Normalmente, o JS é sincronizado com a renderização e, portanto, você não precisa se preocupar com as condições da corrida, mas isso é uma exceção. html.spec.whatwg.org/multipage/…
Mog0
23

Você realmente tem que fazer isso com o jQuery? Você também pode anexar o onloadevento diretamente à sua imagem;

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

Ele dispara toda vez que a imagem é carregada, do cache ou não.

Björn
fonte
5
É mais limpo sem javascript embutido
hazelnut
1
Olá, Bjorn, o onloadmanipulador será acionado quando a imagem for carregada uma segunda vez do cache?
Sexy Beast
1
@Cupidvogel não, não vai.
Anúncios
3
Cara, você salvou minha vida, tentei pelo menos uma dúzia de outras soluções e a sua é a única que realmente funcionou. Estou pensando seriamente em criar mais 10 contas para votar em sua resposta mais 10 vezes. Sério obrigado!
Carlo
1
Eu nunca entendi a aversão das pessoas ao JS embutido. O carregamento da imagem ocorre separadamente e fora de sequência do restante do processo de carregamento da página. Portanto, esta é a única solução válida para obter feedback imediato sobre a conclusão do carregamento de imagens. No entanto, se alguém precisar esperar o jQuery carregar e isso acontecer mais tarde, ele terá que não usar a solução simples aqui e usar uma das outras soluções (o Piotr é provavelmente a melhor opção, pois não depende de pseudo- hacks, mas a verificação de altura () do guari também pode ser importante). Uma fila de processamento também pode ser útil.
CubicleSoft
16

Você também pode usar este código com suporte para erro de carregamento:

$("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
fonte
1
Este funcionou melhor para mim. Eu acho que a chave é definir o loadmanipulador primeiro e depois chamá-lo mais tarde na eachfunção.
GreenRaccoon23
configurando a imagem usando o código abaixo '' var img = new Image (); img.src = sosImgURL; $ (img) .on ("load", function () {''
imdadhusen em 28/04
13

Eu mesmo tive esse problema, procurei em todos os lugares uma solução que não envolvesse matar meu cache ou baixar um plugin.

Eu não vi esse tópico imediatamente, então encontrei outra coisa que é uma correção interessante e (acho) digna de ser postada aqui:

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

Na verdade, recebi essa idéia dos comentários aqui: http://www.witheringtree.com/2009/05/image-load-event-binding-with-ie-using-jquery/

Eu não tenho idéia do por que ele funciona, mas eu testei isso no IE7 e onde ele quebrou antes de agora funcionar.

Espero que ajude,

Editar

A resposta aceita realmente explica o porquê:

Se o src já estiver definido, o evento será disparado no cache fornecido antes de você vincular o manipulador de eventos.

Sammaye
fonte
4
Eu testei isso em muitos navegadores e não o encontrei em nenhum lugar. Eu costumava $image.load(function(){ something(); }).attr('src', $image.attr('src'));redefinir a fonte original.
Maurice
Descobri que essa abordagem não funciona em iOS, pelo menos para Safari / 601.1 e Safari / 602.1
cronfy
Seria incrível descobrir por que, em que navegador ele está?
Sammaye
5

Usando o jQuery para gerar uma nova imagem com o src da imagem e atribuindo o método de carregamento diretamente a ele, o método de carregamento é chamado com êxito quando o jQuery termina de gerar a nova imagem. Isso está funcionando para mim no IE 8, 9 e 10

$('<img />', {
    "src": $("#img").attr("src")
}).load(function(){
    // Do something
});
usuario
fonte
Para aqueles que usam Ruby on Rails, com solicitação remota, usando modelos __rjs.erb, esta é a única solução que encontrei. Como o uso de parciais no js.erb, ele perde a ligação ($ ("# img" .on ("load")) ... e para imagens NÃO é possível delegar o método "on": $ ("body"). on ("load", "# img", function () ...
Albert Català
5

Uma solução que encontrei https://bugs.chromium.org/p/chromium/issues/detail?id=7731#c12 (este código foi extraído diretamente do comentário)

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
fonte
Era exatamente isso que eu estava procurando. Uma única imagem chega do servidor. Eu queria pré-carregá-lo e depois servi-lo. Não é possível selecionar todas as imagens, como fazem muitas respostas. Um bom.
Kai Qing
4

Uma modificação no exemplo da GUS:

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

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

Defina a fonte antes e depois da carga.

Chuck Conway
fonte
Como a resposta de @ Gus, isso não funcionará para várias imagens ... e não há necessidade de definir o srcantes de anexar o onloadmanipulador, uma vez que depois será suficiente.
Nick Craver
3

Apenas adicione novamente o argumento src em uma linha separada após a definição do objeto. Isso levará o IE a acionar o evento lad. É feio, mas é a solução mais simples que encontrei até agora.

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

Posso dar uma pequena dica, se você quiser fazer o seguinte:

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

Se você fizer isso quando o navegador armazena em cache as imagens, não há problema em sempre mostrar o img, mas carregar o img na imagem real.

Josephy Besim
fonte
1

Eu tive esse problema com o IE, onde a largura de e.target.windth seria indefinida. O evento de carregamento seria acionado, mas não consegui obter as dimensões da imagem no IE (chrome + FF funcionou).

Acontece que você precisa procurar por e.currentTarget.naturalWidth e e.currentTarget.naturalHeight .

Mais uma vez, o IE faz as coisas de maneira própria (mais complicada).

Todos
fonte
0

Você pode resolver seu problema usando o plug-in JAIL, que também permite carregar imagens com preguiça (melhorando o desempenho da página) e passando o retorno de chamada como parâmetro

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

O HTML deve se parecer com

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

Se você deseja uma solução CSS pura, esse truque funciona muito bem - use o objeto de transformação. Isso também funciona com imagens quando elas são armazenadas em cache ou não:

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

Exemplo de Codepen

Exemplo de Codepen LESS

neoRiley
fonte
Você poderia dar um exemplo de como isso funciona com o carregamento de imagens?
Hastig Zusammenstellen 18/11/16