Como posso determinar se uma imagem foi carregada, usando Javascript / jQuery?

84

Estou escrevendo algum Javascript para redimensionar a imagem grande para caber na janela do navegador do usuário. (Não controlo o tamanho das imagens de origem, infelizmente.)

Portanto, algo assim estaria no HTML:

<img id="photo"
     src="a_really_big_file.jpg"
     alt="this is some alt text"
     title="this is some title text" />

Existe uma maneira de determinar se a srcimagem em uma imgtag foi baixada?

Eu preciso disso porque estou tendo um problema se $(document).ready()for executado antes que o navegador carregue a imagem. $("#photo").width()e $("#photo").height()retornará o tamanho do espaço reservado (o texto alternativo). No meu caso, é algo como 134 x 20.

No momento, estou apenas verificando se a altura da foto é inferior a 150 e presumindo que, se for, é apenas texto alternativo. Mas isso é um grande hack, e seria quebrado se uma foto tivesse menos de 150 pixels de altura (provavelmente não no meu caso em particular) ou se o texto alternativo tivesse mais de 150 pixels de altura (possivelmente poderia acontecer em uma pequena janela do navegador) .


Edit: Para quem deseja ver o código:

$(function()
{
  var REAL_WIDTH = $("#photo").width();
  var REAL_HEIGHT = $("#photo").height();

  $(window).resize(adjust_photo_size);
  adjust_photo_size();

  function adjust_photo_size()
  {
    if(REAL_HEIGHT < 150)
    {
      REAL_WIDTH = $("#photo").width();
      REAL_HEIGHT = $("#photo").height();
      if(REAL_HEIGHT < 150)
      {
        //image not loaded.. try again in a quarter-second
        setTimeout(adjust_photo_size, 250);
        return;
      }
    }

    var new_width = . . . ;
    var new_height = . . . ;

    $("#photo").width(Math.round(new_width));
    $("#photo").height(Math.round(new_height));
  }

});

Atualização : Obrigado pelas sugestões. Há o risco de o evento não ser disparado se eu definir um retorno de chamada para o $("#photo").loadevento, portanto, defini um evento onLoad diretamente na tag da imagem. Para registro, aqui está o código que acabei usando:

<img id="photo"
     onload="photoLoaded();"
     src="a_really_big_file.jpg"
     alt="this is some alt text"
     title="this is some title text" />

Depois, em Javascript:

//This must be outside $() because it may get called first
var isPhotoLoaded = false;
function photoLoaded()
{
  isPhotoLoaded = true;
}

$(function()
{
  //Hides scrollbars, so we can resize properly.  Set with JS instead of
  //  CSS so that page doesn't break with JS disabled.
  $("body").css("overflow", "hidden");

  var REAL_WIDTH = -1;
  var REAL_HEIGHT = -1;

  $(window).resize(adjust_photo_size);
  adjust_photo_size();

  function adjust_photo_size()
  {
    if(!isPhotoLoaded)
    {
      //image not loaded.. try again in a quarter-second
      setTimeout(adjust_photo_size, 250);
      return;
    }
    else if(REAL_WIDTH < 0)
    {
      //first time in this function since photo loaded
      REAL_WIDTH = $("#photo").width();
      REAL_HEIGHT = $("#photo").height();
    }

    var new_width = . . . ;
    var new_height = . . . ;

    $("#photo").width(Math.round(new_width));
    $("#photo").height(Math.round(new_height));
  }

});
Kip
fonte
1
Isso é tão antigo e você já aceitou uma resposta, então vou apenas comentar aqui. Por que você não pode usar o plugin jQuery 'onImagesLoad'? E também, jQuery ou não, o que há de errado em configurar max-widthe max-heightestilizar as imagens com Javascript? Em seguida, você evita TODO o código que precisa escrever, definindo a largura / altura máxima para o tamanho da largura / altura da janela de visualização.
@ jon.wd7: Não estou familiarizado com esse plug-in e pode não ter existido em '08. quanto a max-width e max-height, duas coisas: 1) eles não são suportados no IE (bem, não tenho certeza sobre o IE 8); 2) se a janela de visualização mudar de tamanho (a janela é redimensionada etc.), eu ainda precisaria de javascript para alterar a largura máxima / altura máxima (embora não possa se usar "100%" em vez de uma medição de pixel)
Kip
É definitivamente compatível com o IE7 e IE8, de acordo com quirksmode.org. Portanto, não tenho certeza do seu problema. Eu pessoalmente não suporte o IE6 para coisas pequenas como essa, então o que eles verão é a mesma coisa que um usuário sem JS habilitado veria. E, claro, você precisaria reiniciar max-widthe max-heightredimensionar. Mas isso é uma tarefa bastante fácil, um uso de uma linha, .resize()na verdade.
3
A propriedade complete fornece a maneira obrigatória de saber se a imagem foi carregada.
BorisOkunskiy
1
@Adrien, Mozilla agora mostra completo como suportado por todos os principais navegadores, exceto para Andoid (desconhecido).
Oliver Bock

Respostas:

33

Adicione um ouvinte de evento ou faça com que a imagem se anuncie com onload. Em seguida, descubra as dimensões a partir daí.

<img id="photo"
     onload='loaded(this.id)'
     src="a_really_big_file.jpg"
     alt="this is some alt text"
     title="this is some title text" />
Diodeus - James MacFarlane
fonte
De acordo com as especificações, onload é apenas um evento para os elementos body e frameset; Dito isso, acho que funciona no IE, mas não sei se funciona em outros navegadores ou se é algo que você possa presumir ... Eu estava investigando isso há algumas semanas ...
Jason Bunting
Eu testei no IE6, IE7, FF3, Opera9, Safair (Windows Beta) e Chrome (Windows Beta).
Kip
9
O comportamento e o conteúdo de bagunça são desencorajados. Os eventos devem ser aplicados usando Javascript, não em HTML.
dionyziz
10
O comentário dele É relevante já que não estamos parados em 2008. As pessoas ainda lêem isso, e mesmo que não tenha sido considerado uma má prática em 08, você não quer que as pessoas pensem que é uma boa prática agora.
Spoeken
3
document.querySelector ("img"). addEventListener ("carregar", função () {alert ('onload!');});
Frank Schwieterman
14

Usando o armazenamento de dados jquery, você pode definir um estado 'carregado'.

<img id="myimage" onload="$(this).data('loaded', 'loaded');" src="lolcats.jpg" />

Então, em outro lugar, você pode fazer:

if ($('#myimage').data('loaded')) {
    // loaded, so do stuff
}
Mike Fogel
fonte
2
Use em jQuery(this)vez de $(this)para melhor compatibilidade com outras bibliotecas (jquery não possui o $), especialmente quando você tem JavaScript em html.
John Magnolia
11

A resposta certa é usar event.special.load

É possível que o evento de carregamento não seja disparado se a imagem for carregada do cache do navegador. Para dar conta dessa possibilidade, podemos usar um evento de carregamento especial que dispara imediatamente se a imagem estiver pronta. event.special.load está atualmente disponível como um plugin.

De acordo com a documentação em .load ()

Evan Carroll
fonte
2
Eu adicionaria uma resposta com uma sugestão como esta, feliz por ter visto essa resposta antes de codificá-la. Basicamente, seus manipuladores precisam primeiro verificar se a imagem está carregada antes de definir o manipulador. Se já estiver carregado, dispare o retorno de chamada imediatamente. É o que $.readyfaz. Eu ia sugerir apenas verificar a largura, mas isso tem seus problemas, eu gosto muito dessa solução.
Juan Mendes
5

Você quer fazer o que Allain disse, no entanto, esteja ciente de que às vezes a imagem carrega antes do dom estar pronto, o que significa que seu gerenciador de carga não disparará. A melhor maneira é fazer o que Allain diz, mas defina o src da imagem com javascript após anexar o gerenciador de carregamento. Assim você garante que ele dispara.

Em termos de acessibilidade, seu site ainda funcionará para pessoas sem javascript? Você pode querer dar à tag img o src correto, anexar seu manipulador de dom ready para executar seu js: limpe o src da imagem (dê a ele um valor fixo com e altura com css para evitar a oscilação da página), em seguida, defina seu manipulador de carregamento de img, em seguida, reconfigure o src para o arquivo correto. Desta forma, você cobre todas as bases :)

Andrew Bullock
fonte
O site ainda funciona para quem não tem Javascript, basta rolar para ver toda a imagem.
Kip
4

De acordo com um dos comentários recentes à sua pergunta original

$(function() {

  $(window).resize(adjust_photo_size);
  adjust_photo_size();

  function adjust_photo_size()  {
    if (!$("#photo").get(0).complete) {
       $("#photo").load(function() {
          adjust_photo_size();
       });
    } else {
      ... 
    }
});

Aviso Esta resposta pode causar um loop sério no ie8 e inferior, porque img.complete nem sempre é configurado corretamente pelo navegador. Se você deve suportar o ie8, use um sinalizador para lembrar que a imagem foi carregada.

pique comum
fonte
Segure. Esta resposta pode causar um loop sério no ie8 e inferior, porque img.complete nem sempre é configurado corretamente pelo navegador. Se você deve suportar o ie8, use um sinalizador para lembrar que a imagem foi carregada.
commonpike
2

Experimente algo como:

$("#photo").load(function() {
    alert("Hello from Image");
});
Allain Lalonde
fonte
6
Obrigado. No entanto, existe a possibilidade de que a imagem já tenha sido carregada antes que isso aconteça, caso em que o evento load não seria disparado.
Kip
Que tal uma tag de script definindo este evento logo após a tag da imagem? O motivo de usar pronto é ter certeza de que todo o documento está carregado, o que é desnecessário nesse caso, certo?
Jason Goemaat
2

Há um plugin jQuery chamado "imagesLoaded" que fornece um método compatível com vários navegadores para verificar se as imagens de um elemento foram carregadas.

Local: https://github.com/desandro/imagesloaded/

Uso para um contêiner que contém muitas imagens:

$('container').imagesLoaded(function(){
 console.log("I loaded!");
})

O plugin é ótimo:

  1. funciona para verificar um contêiner com muitas imagens dentro
  2. funciona para verificar um img para ver se ele foi carregado
gaio
fonte
parece meio pesado 5KB minificado apenas para verificar img carregado. Deve haver um método mais fácil disponível ...
cdalxndr
1

Algum comentário sobre este?

...

doShow = function(){
  if($('#img_id').attr('complete')){
    alert('Image is loaded!');
  } else {
    window.setTimeout('doShow()',100);
  }
};

$('#img_id').attr('src','image.jpg');

doShow();

...

Parece que funciona em qualquer lugar ...

Jay
fonte
é uma má prática de usarsetTimeout
cdalxndr
1

Acabei de criar uma função jQuery para carregar uma imagem usando o objeto jQuerys Deferred, o que torna muito fácil reagir no evento de carregamento / erro:

$.fn.extend({
    loadImg: function(url, timeout) {
        // init deferred object
        var defer = $.Deferred(),
            $img = this,
            img = $img.get(0),
            timer = null;

        // define load and error events BEFORE setting the src
        // otherwise IE might fire the event before listening to it
        $img.load(function(e) {
            var that = this;
            // defer this check in order to let IE catch the right image size
            window.setTimeout(function() {
                // make sure the width and height are > 0
                ((that.width > 0 && that.height > 0) ? 
                    defer.resolveWith : 
                    defer.rejectWith)($img);
            }, 1);
        }).error(function(e) {
            defer.rejectWith($img);
        });

        // start loading the image
        img.src = url;

        // check if it's already in the cache
        if (img.complete) {
            defer.resolveWith($img);
        } else if (0 !== timeout) {
            // add a timeout, by default 15 seconds
            timer = window.setTimeout(function() {
                defer.rejectWith($img);
            }, timeout || 15000);
        }

        // return the promise of the deferred object
        return defer.promise().always(function() {
            // stop the timeout timer
            window.clearTimeout(timer);
            timer = null;
            // unbind the load and error event
            this.off("load error");
        });
    }
});

Uso:

var image = $('<img />').loadImg('http://www.google.com/intl/en_com/images/srpr/logo3w.png')
.done(function() {
    alert('image loaded');
    $('body').append(this);
}).fail(function(){
    alert('image failed');
});

Veja-o funcionando em: http://jsfiddle.net/roberkules/AdWZj/

Roberkules
fonte
Isso soa maravilhoso. Você pode dar um exemplo exato de quando seu código é preferível a img.onload e img.onerror? Seu código serve apenas para lidar com as falhas aparentes do tratamento de erros do jQuery sobre o qual acabei de ser informado: esse erro não é acionado no IE se não houver extensão de arquivo?
mplungjan
ideia interessante. então saber quando a imagem está pronta é fácil. Mas, e se a imagem não pudesse ser carregada. Estava procurando uma maneira de saber se o carregamento da imagem falhou. e você me deu a ideia de tempo limite ao carregá-lo. +1 para isso
carvalho
1
@oak o tempo limite deve ser apenas um substituto. a maioria dos navegadores deve disparar um errorevento que é controlado por .error(function(e) { defer.rejectWith($img); }). Se você olhar para o jsFiddle, verá que o http://www.google.com/abc.png not foundtexto é mostrado imediatamente no painel de resultados (sem esperar pelo tempo limite, porque o evento de erro começou)
roberkules
note que para jquery levantar .error duas coisas devem ser 1. o identificador, mas ser definido antes do erro ser gerado. 2. .error lida apenas com o protocolo http e não com o arquivo: // então você não receberá erros lá ..
oak
em meu exemplo de código, o manipulador de sucesso / erro é definido antes de atribuir o atributo src exatamente por esse motivo. sobre a file://restrição - nunca precisei vincular imagens usando o protocolo de arquivo e não acho que haja muita necessidade.
roberkules de
1

Esta função verifica se uma imagem é carregada com base nas dimensões mensuráveis. Essa técnica é útil se o seu script estiver sendo executado depois que algumas das imagens já tiverem sido carregadas.

imageLoaded = function(node) {
    var w = 'undefined' != typeof node.clientWidth ? node.clientWidth : node.offsetWidth;
    var h = 'undefined' != typeof node.clientHeight ? node.clientHeight : node.offsetHeight;
    return w+h > 0 ? true : false;
};
Ralph Ritoch
fonte
Descobri que essa solução pode exigir que não haja margem ou preenchimento na imagem e que o texto alternativo de uma string vazia seja fornecido. Na maioria dos casos, porém, funciona como planejado. Ele só falhará se o tamanho da imagem descarregada for maior que zero.
Ralph Ritoch de
1

Eu achei que funcionou para mim

document.querySelector("img").addEventListener("load", function() { alert('onload!'); });

O crédito vai totalmente para Frank Schwieterman, que comentou sobre a resposta aceita. Tive que colocar isso aqui, é muito valioso ...

Armand
fonte
0

Desenvolvemos uma página onde carregava uma série de imagens e realizava outras funções somente após o carregamento da imagem. Era um site movimentado que gerava muito tráfego. Parece que o seguinte script simples funcionou em praticamente todos os navegadores:

$(elem).onload = function() {
    doSomething();
}

MAS ESTA É UMA QUESTÃO POTENCIAL PARA O IE9!

O ÚNICO navegador em que relatamos problemas é o IE9. Não estamos surpresos? Parece que a melhor maneira de resolver o problema é não atribuir um src à imagem até DEPOIS que a função onload tenha sido definida, assim:

$(elem).onload = function() {
    doSomething();
}
$(elem).attr('src','theimage.png');

Parece que o IE 9 às vezes não joga o onload evento por qualquer motivo. Outras soluções nesta página (como a de Evan Carroll, por exemplo) ainda não funcionaram. Logicamente, isso verificou se o estado de carregamento já foi bem-sucedido e acionou a função e, se não foi, defina o manipulador onload, mas mesmo quando você fez isso, demonstramos no teste que a imagem poderia carregar entre essas duas linhas de js. aparecendo não carregado na primeira linha e depois carregando antes que o manipulador onload seja definido.

Descobrimos que a melhor maneira de obter o que deseja é não definir o src da imagem até que você defina o onloadacionador de evento.

Recentemente, paramos de oferecer suporte ao IE8, então não posso falar em versões anteriores ao IE9, caso contrário, de todos os outros navegadores que foram usados ​​no site - IE10 e 11, bem como Firefox, Chrome, Opera, Safari e outros o navegador móvel que as pessoas estavam usando - definir o srcantes de atribuir o onloadmanipulador nem mesmo era um problema.

Polvo
fonte
0

Posso sugerir uma solução totalmente CSS pura?

Basta ter um Div no qual deseja mostrar a imagem. Defina a imagem como plano de fundo. Então tenha o imóvel background-size: coverou background-size: containdependendo de como você quiser.

covercortará a imagem até que os lados menores cubram a caixa. containirá manter a imagem inteira dentro da div, deixando você com espaços nas laterais.

Verifique o snippet abaixo.

div {
  height: 300px;
  width: 300px;
  border: 3px dashed grey;
  background-position: center;
  background-repeat: no-repeat;
}

.cover-image {
  background-size: cover;
}

.contain-image {
  background-size: contain;
}
<div class="cover-image" style="background-image:url(https://assets1.ignimgs.com/2019/04/25/avengers-endgame-1280y-1556226255823_1280w.jpg)">
</div>
<br/>
<div class="contain-image" style="background-image:url(https://assets1.ignimgs.com/2019/04/25/avengers-endgame-1280y-1556226255823_1280w.jpg)">
</div>

arunwithasmile
fonte
0

Acho que esta solução simples funciona melhor para mim:

        function setEqualHeight(a, b) {
            if (!$(a).height()) {
                return window.setTimeout(function(){ setEqualHeight(a, b); }, 1000);
            }
            $(b).height($(a).height());
        }

        $(document).ready(function() {
            setEqualHeight('#image', '#description');
            $(window).resize(function(){setEqualHeight('#image', '#description')});
        });
    </script>
Martin Francis
fonte