Digamos que quieres encontrar un <img>
en la página y averigüe qué ancho tiene en JavaScript. Tal vez necesite tomar algunas decisiones en función de ese ancho (o alto, o ambos). Definitivamente puede hacerlo. El DOM incluso le dará una variedad de dimensiones para elegir según lo que necesite. Sin embargo, definitivamente hay una trampa.
Aquí está la imagen:
<img src="https://css-tricks.com/measuring-image-widths-javascript-carefully/image.jpg" alt="an image">
Y aquí estamos seleccionándolo, encontrando el primero <img>
JavaScript puede encontrar en el DOM:
var img = document.querySelector("img");
Vamos a registrar el ancho:
// How wide the image is rendered at (which could be affected by styles)
console.log(img.width);
// How wide the image itself actually is
console.log(img.naturalWidth);
// There is also `offsetWidth` (width with border and padding) and `clientWidth` (width with just padding)
// Those aren't usually too useful with images
¡Alerta de problema! Probablemente obtendrá resultados extrañamente inconsistentes con eso.
El problema se origina cuando se ejecuta JavaScript. Si JavaScript se ejecuta antes de que la imagen se haya descargado y renderizado por completo, el resultado será 0
. Si JavaScript se ejecuta después de que la imagen se haya descargado y renderizado, el resultado será correcto. En nuestro caso, la imagen con la que estamos trabajando es 640
(píxeles de ancho).
Es muy confuso, porque a veces, incluso si su código no tiene esto en cuenta, a veces obtendrá el resultado correcto. Eso se debe a que, probablemente, la imagen está en caché y se coloca en la página tan rápido que, cuando se ejecuta el fragmento de JavaScript, conoce la imagen adecuada. offsetWidth
.
Esta es una condición de carrera, y eso es algo malo.
el buen camino
Lo mínimo que puede hacer es asegurarse de que la imagen se haya cargado antes de medirla. Las imágenes emiten un load
evento cuando hayan terminado, y podría usar la devolución de llamada para hacer las mediciones en ese momento.
img.addEventListener("load", function() {
console.log("image has loaded");
});
Probablemente también querrá hacer un manejo de errores allí, ya que las imágenes también pueden emitir un error
evento.
Es muy probable que estés abstrayendo este concepto, por lo que estarías buscando cualquier cantidad de imágenes, en las que tendrías que recorrerlas y todo eso. Además, las imágenes también pueden ser imágenes de fondo…
Están sucediendo suficientes cosas aquí que sugeriría simplemente usar la biblioteca imagesLoaded de David DeSandro. No tiene dependencias, es bastante pequeño y funciona con jQuery si lo está usando.
Aquí solo estamos observando que todas las imágenes en la página completa se carguen antes de probar los anchos:
imagesLoaded(document.body, function() {
var img = document.querySelector("img");
console.log(img.width);
console.log(img.naturalWidth);
});
¿Por qué?
Un caso de uso para esto que tuve recientemente fue una cosa similar a una caja de luz. Quería asegurarme de que si estaba renderizando una imagen más pequeña de lo que realmente era, el usuario pudiera hacer clic en ella para abrirla más grande.
// Only do this on large screens
if (window.outerWidth > 800) {
// Wait for all images to load inside the article
$("article").imagesLoaded(function() {
$("figure > img").each(function(el) {
// Only do this is shown image is smaller than actual image
if (this.naturalWidth > this.width) {
$(this)
.closest("figure")
.addClass("can-be-enlarged")
.end()
// When the image is clicked, toggle a class to enlarge it
.on("click", function(e) {
e.stopPropagation();
$(this)
.closest("figure")
.toggleClass("enlarge");
})
}
});
});
// When the enlarged image is clicked again, remove the class, shrinking it back down
$("body").on("click", "figure.can-be-enlarged.enlarge", function() {
$(this).removeClass("enlarge");
})
}
}
El enlarge
la clase hace todo <figure>
en una superposición de pantalla completa con la imagen y el pie de foto centrados en él. ¡Pero solo hizo todo esto si tenía sentido hacerlo, lo que necesitaba que la imagen con lógica fuera correcta!