¿Cómo puedo determinar si una imagen se ha cargado usando Javascript / jQuery?

85

Estoy escribiendo algo de Javascript para cambiar el tamaño de la imagen grande para que quepa en la ventana del navegador del usuario. (Desafortunadamente, no controlo el tamaño de las imágenes de origen).

Entonces, algo como esto estaría en el HTML:

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

¿Hay alguna forma de determinar si la srcimagen de una imgetiqueta se ha descargado?

Necesito esto porque tengo un problema si $(document).ready()se ejecuta antes de que el navegador haya cargado la imagen. $("#photo").width()y $("#photo").height()devolverá el tamaño del marcador de posición (el texto alternativo). En mi caso, esto es algo así como 134 x 20.

En este momento, solo estoy comprobando si la altura de la foto es inferior a 150, y suponiendo que, si es así, es solo texto alternativo. Pero esto es todo un truco, y se rompería si una foto tiene menos de 150 píxeles de alto (no es probable en mi caso particular), o si el texto alternativo tiene más de 150 píxeles de alto (posiblemente podría ocurrir en una ventana pequeña del navegador) .


Editar: para cualquiera que quiera ver el 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));
  }

});

Actualización : Gracias por las sugerencias. Existe el riesgo de que el evento no se active si configuro una devolución de llamada para el $("#photo").loadevento, por lo que he definido un evento onLoad directamente en la etiqueta de la imagen. Para que conste, aquí está el código con el que terminé yendo:

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

Luego en 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));
  }

});
Dormir
fuente
1
Esto es tan antiguo y ya has aceptado una respuesta, así que solo comentaré aquí. ¿Por qué no puede utilizar el complemento jQuery 'onImagesLoad'? Y también, jQuery o no, ¿qué hay de malo en la configuración max-widthy max-heighten el estilo de la imagen con Javascript? Luego, evita TODO el código que ha necesitado escribir estableciendo el ancho / alto máximo en el tamaño del ancho / alto de la ventana gráfica.
@ jon.wd7: no estoy familiarizado con ese complemento, y es posible que no haya existido en el '08. en cuanto a max-width y max-height, dos cosas: 1) no son compatibles con IE (bueno, no estoy seguro de IE 8); 2) si la ventana gráfica cambia de tamaño (la ventana cambia de tamaño, etc.), entonces todavía necesitaría javascript para cambiar el ancho máximo / alto máximo (aunque es posible que no lo haga si usé "100%" en lugar de una medición de píxeles)
Kip
Definitivamente es compatible con IE7 e IE8, según quirksmode.org. Así que no estoy seguro de tu problema. Personalmente, no soy compatible con IE6 para cosas pequeñas como esta, así que lo que verán es lo mismo que vería un usuario sin JS habilitado. Y, por supuesto, necesitaría restablecer max-widthy max-heightcambiar el tamaño. Pero esa es una tarea bastante fácil, de hecho, una sola línea .resize().
3
La propiedad completa proporciona la forma imperativa de saber si la imagen se ha cargado.
BorisOkunskiy
1
@Adrien, Mozilla ahora se muestra completo como es compatible con todos los navegadores principales, excepto Andoid (desconocido).
Oliver Bock

Respuestas:

33

Agregue un detector de eventos o haga que la imagen se anuncie con onload. Luego, calcula las dimensiones a partir de ahí.

<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
fuente
Según la especificación, onload es solo un evento para los elementos body y frameset; Dicho esto, creo que esto funciona en IE, pero no sé si funciona en otros navegadores o si es algo que puedes asumir ... Estaba investigando esto hace un par de semanas ...
Jason Bunting
Lo he probado en IE6, IE7, FF3, Opera9, Safair (Windows Beta) y Chrome (Windows Beta).
Kip
9
Se desaconseja el comportamiento y el contenido enredados. Los eventos deben aplicarse utilizando Javascript, no HTML.
dionyziz
10
Su comentario ES relevante ya que no estamos estancados en 2008. La gente todavía lee esto, e incluso aunque no se consideró una mala práctica en 08, no quieres que la gente piense que es una buena práctica ahora.
Hablado el
3
document.querySelector ("img"). addEventListener ("load", function () {alert ('onload!');});
Frank Schwieterman
14

Usando el almacén de datos jquery puede definir un estado 'cargado'.

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

Luego, en otro lugar puedes hacer:

if ($('#myimage').data('loaded')) {
    // loaded, so do stuff
}
Mike Fogel
fuente
2
Úselo en jQuery(this)lugar de $(this)para una mejor compatibilidad con otras bibliotecas (jquery no es propietario de $), especialmente cuando tiene JavaScript en html.
John Magnolia
11

La respuesta correcta es usar event.special.load

Es posible que el evento de carga no se active si la imagen se carga desde la caché del navegador. Para tener en cuenta esta posibilidad, podemos utilizar un evento de carga especial que se activa inmediatamente si la imagen está lista. event.special.load está disponible actualmente como complemento.

Según los documentos en .load ()

Evan Carroll
fuente
2
Iba a agregar una respuesta con una sugerencia como esta, me alegro de haber visto esta respuesta antes de codificarla. Básicamente, los controladores deben verificar primero si la imagen está cargada antes de configurar el controlador. Si ya está cargado, active la devolución de llamada inmediatamente. Es lo que $.readyhace. Iba a sugerir simplemente verificar el ancho, pero eso tiene sus problemas, realmente me gusta esa solución.
Juan Mendes
5

Desea hacer lo que dijo Allain, sin embargo, tenga en cuenta que a veces la imagen se carga antes de que dom esté listo, lo que significa que su controlador de carga no se activará. La mejor manera es hacer lo que dice Allain, pero establecer el src de la imagen con javascript después de adjuntar el controlador de carga. De esta forma puede garantizar que se dispara.

En términos de accesibilidad, ¿su sitio seguirá funcionando para personas sin javascript? Es posible que desee darle a la etiqueta img el src correcto, adjunte su controlador dom ready para ejecutar su js: borre la imagen src (déle un valor fijo con y altura con css para evitar que la página parpadee), luego configure su controlador de carga img, luego restablezca el src al archivo correcto. De esta manera cubrirá todas las bases :)

Andrew Bullock
fuente
El sitio todavía funciona para personas sin Javascript, solo tienen que desplazarse para ver toda la imagen.
Kip
4

Según uno de los comentarios recientes a su pregunta 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 {
      ... 
    }
});

Advertencia Esta respuesta podría causar un bucle grave en ie8 y versiones inferiores, porque img.complete no siempre está configurado correctamente por el navegador. Si debe admitir ie8, use una bandera para recordar que la imagen está cargada.

commonpike
fuente
Sostenlo. Esta respuesta podría causar un bucle grave en ie8 y versiones inferiores, porque img.complete no siempre está configurado correctamente por el navegador. Si debe admitir ie8, use una bandera para recordar que la imagen está cargada.
Commonpike
2

Prueba algo como:

$("#photo").load(function() {
    alert("Hello from Image");
});
Allain Lalonde
fuente
6
Gracias. Sin embargo, existe la posibilidad de que la imagen ya se haya cargado antes de que esto suceda, en cuyo caso el evento de carga no se activará.
Kip
¿Qué tal una etiqueta de secuencia de comandos que establezca este evento justo después de la etiqueta de imagen? La razón para usar ready es asegurarse de que esté cargado todo el documento, lo cual es innecesario en este caso, ¿verdad?
Jason Goemaat
2

Hay un complemento de jQuery llamado "imagesLoaded" que proporciona un método compatible con varios navegadores para verificar si se han cargado las imágenes de un elemento.

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

Uso de un contenedor que tiene muchas imágenes en su interior:

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

El complemento es genial:

  1. funciona para comprobar un contenedor con muchas imágenes dentro
  2. funciona para comprobar una imagen para ver si se ha cargado
arrendajo
fuente
parece un poco pesado 5KB minificado solo para verificar img cargado. Debería ser un método más ligero más fácil disponible ...
cdalxndr
1

¿Algún comentario 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 en todas partes ...

Arrendajo
fuente
es una mala práctica usarlosetTimeout
cdalxndr
1

Acabo de crear una función jQuery para cargar una imagen usando jQuerys Deferred Object que hace que sea muy fácil reaccionar ante un evento de carga / error:

$.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');
});

Véalo funcionando en: http://jsfiddle.net/roberkules/AdWZj/

roberkules
fuente
Eso suena maravilloso. ¿Puede dar un ejemplo exacto de cuándo su código es preferible a img.onload e img.onerror? ¿Su código solo está destinado a manejar las aparentes deficiencias del manejo de errores de jQuery que me acaban de informar: ese error no se activa en IE si no hay una extensión de archivo?
mplungjan
idea interesante. así que saber cuándo se hace la imagen es fácil. Pero, ¿qué pasa si no se puede cargar la imagen? Estaba buscando una forma de saber si la carga de la imagen ha fallado. y me diste la idea de un tiempo de espera para cargarlo. +1 para eso
roble
1
@oak, el tiempo de espera debe ser solo una alternativa. la mayoría de los navegadores deberían disparar un errorevento manejado por .error(function(e) { defer.rejectWith($img); }). Si observa jsFiddle, verá que el http://www.google.com/abc.png not foundtexto se muestra inmediatamente en el panel de resultados (sin esperar un tiempo de espera, porque el evento de error se
activó
tenga en cuenta que para que jquery genere .error, dos cosas deben ser 1. el identificador, pero debe establecerse antes de que se produzca el error. 2. Los mangos .ERROR única protocolo HTTP y no file: // para que no obtendrá el error allí ..
roble
en mi ejemplo de código, el controlador de éxito / error se establece antes de asignar el atributo src exactamente por esa razón. sobre la file://restricción: nunca necesité vincular imágenes usando el protocolo de archivo y no creo que sea muy necesario.
roberkules
1

Esta función comprueba si una imagen se carga basándose en tener dimensiones medibles. Esta técnica es útil si su script se está ejecutando después de que algunas de las imágenes ya se hayan cargado.

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
fuente
Descubrí que esta solución puede requerir que no haya márgenes ni relleno en la imagen, y que se proporcione texto alternativo de una cadena vacía. Sin embargo, en la mayoría de los casos funciona según lo previsto. Solo fallará si el tamaño de la imagen descargada es mayor que cero.
Ralph Ritoch
1

Encontré que esto funcionó para mí

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

El crédito va totalmente a Frank Schwieterman, quien comentó la respuesta aceptada. Tuve que poner esto aquí, es demasiado valioso ...

Armand
fuente
0

Desarrollamos una página donde cargaba una serie de imágenes y luego realizaba otras funciones solo después de que se cargaba la imagen. Era un sitio muy concurrido que generaba mucho tráfico. Parece que el siguiente script simple funcionó en prácticamente todos los navegadores:

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

¡PERO ESTE ES UN PROBLEMA POTENCIAL PARA IE9!

El ÚNICO navegador sobre el que informamos problemas es IE9. ¿No nos sorprende? Parece que la mejor manera de resolver el problema es no asignar un src a la imagen hasta DESPUÉS de que se haya definido la función de carga, así:

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

Parece que IE 9 a veces no arroja el onload evento por cualquier motivo. Otras soluciones en esta página (como la de Evan Carroll, por ejemplo) aún no funcionaban. Lógicamente, eso verificó si el estado de carga ya fue exitoso y activó la función y, si no lo fue, luego configure el controlador de carga, pero incluso cuando lo haga, demostramos en la prueba que la imagen podría cargarse entre esas dos líneas de js, aparece no cargado en la primera línea y luego se carga antes de que se establezca el controlador de carga.

Descubrimos que la mejor manera de obtener lo que desea es no definir el src de la imagen hasta que haya configurado el activador del onloadevento.

Recientemente dejamos de admitir IE8, por lo que no puedo hablar de versiones anteriores a IE9, de lo contrario, de todos los demás navegadores que se usaron en el sitio: IE10 y 11, así como Firefox, Chrome, Opera, Safari y lo que sea. Los navegadores móviles que usaban las personas: configurar el srcantes de asignar el onloadcontrolador ni siquiera era un problema.

Pulpo
fuente
0

¿Puedo sugerir una solución CSS pura por completo?

Solo tenga un Div en el que desee mostrar la imagen. Establezca la imagen como fondo. Entonces tienes la propiedad background-size: covero background-size: containsegún como la quieras.

coverrecortará la imagen hasta que los lados más pequeños cubran la caja. containmantendrá la imagen completa dentro del div, dejándote con espacios en los lados.

Consulte el fragmento a continuación.

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

Encuentro que esta sencilla solución funciona mejor para mí:

        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
fuente