Nehmen wir an, Sie möchten ein <img>-Element auf der Seite finden und herausfinden, wie breit es in JavaScript ist. Vielleicht müssen Sie basierend auf dieser Breite (oder Höhe oder beidem) Entscheidungen treffen. Das können Sie definitiv tun. Das DOM bietet sogar eine Vielzahl von Abmessungen zur Auswahl, je nachdem, was Sie benötigen. Es gibt jedoch einen Haken.
Hier ist das Bild
<img src="image.jpg" alt="an image">
Und hier wählen wir es aus, indem wir das erste <img>-Element finden, das JavaScript im DOM finden kann
var img = document.querySelector("img");
Lassen Sie uns einfach die Breite protokollieren
// 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
Problemwarnung! Sie werden damit wahrscheinlich seltsam inkonsistente Ergebnisse erzielen.
Das Problem liegt darin, *wann* das JavaScript ausgeführt wird. Wenn das JavaScript ausgeführt wird, *bevor* das Bild vollständig heruntergeladen und gerendert wurde, ist das Ergebnis 0. Wenn das JavaScript ausgeführt wird, nachdem das Bild heruntergeladen und gerendert wurde, ist das Ergebnis korrekt. In unserem Fall ist das Bild, mit dem wir arbeiten, 640 (Pixel breit).
Es ist zusätzlich verwirrend, weil Sie manchmal trotzdem das richtige Ergebnis erhalten, auch wenn Ihr Code dies nicht berücksichtigt. Das liegt wahrscheinlich daran, dass das Bild im Cache ist und so schnell auf die Seite geladen wird, dass zu dem Zeitpunkt, an dem Ihr JavaScript-Code ausgeführt wird, die richtige offsetWidth bekannt ist.
Das ist eine Race Condition, und das ist schlecht.
Der gute Weg
Das Mindeste, was Sie tun können, ist sicherzustellen, dass das Bild geladen wurde, bevor Sie es messen. Bilder lösen ein load-Ereignis aus, wenn sie fertig sind, und Sie könnten den Callback verwenden, um die Messungen dann durchzuführen.
img.addEventListener("load", function() {
console.log("image has loaded");
});
Wahrscheinlich möchten Sie dort auch eine Fehlerbehandlung durchführen, da Bilder auch ein error-Ereignis auslösen können.
Es besteht eine gute Chance, dass Sie dieses Konzept abstrahieren, sodass Sie auf eine beliebige Anzahl von Bildern achten müssten, und dann müssten Sie über sie schleifen und all das. Außerdem können Bilder auch Hintergrundbilder sein...
Es gibt hier genug zu tun, dass ich vorschlagen würde, einfach die imagesLoaded-Bibliothek von David DeSandro zu verwenden. Sie hat keine Abhängigkeiten, ist ziemlich klein und funktioniert mit jQuery, wenn Sie das verwenden.
Hier warten wir einfach darauf, dass alle Bilder auf der gesamten Seite geladen sind, bevor wir die Breiten testen
imagesLoaded(document.body, function() {
var img = document.querySelector("img");
console.log(img.width);
console.log(img.naturalWidth);
});
Warum?
Ein Anwendungsfall dafür, den ich kürzlich hatte, war etwas Ähnliches wie ein Lightbox. Ich wollte sicherstellen, dass, wenn ich ein Bild rendere, das kleiner ist als es tatsächlich war, der Benutzer die Möglichkeit hatte, darauf zu klicken, um es größer zu öffnen.
// 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");
})
}
}
Die Klasse enlarge macht die gesamte <figure> zu einem Vollbild-Overlay mit dem zentrierten Bild und der zentrierten figcaption darin. Aber all diese Dinge tat sie nur, wenn es Sinn machte, was das Bild mit korrekter Logik benötigte!
Ich habe an getComputedStyle( ) && getBoundingClientRect( ) in JS gedacht, als ich das sah.
Ressourcen
1. http://stackoverflow.com/questions/20412248/javascript-getting-the-size-of-an-image-using-getcomputedstyle
http://stackoverflow.com/questions/5834624/retrieve-width-height-of-a-css3-scaled-element
Diese sind sehr janky, da sie beim Zugriff darauf einen Reflow der Seite verursachen.
@Udi Talias
Ihre Antwort ist in zweierlei Hinsicht falsch.
img.naturalWidthgibt die physikalische Breite des Bildes zurück (Bitte beachten Sie: Es berücksichtigt auch diesrcset-Deskriptoren, falls Siesrcsetverwenden.). Dies wird mitimg.widthverglichen, das die Layoutbreite des Bildes zurückgibt und ähnlich wiegetComputedStyleodergetBoundingClientRectoderoffsetWidthfunktioniert. Es wurde also nicht verwendet, um einen weniger janky Code zu verwenden, da es bereits Code verwendet, der möglicherweise synchronen Stil/Layout erzwingt. Es wurde verwendet, um einfach die intrinsischen Dimensionen des Bildes mit den Layout-Dimensionen des Bildes zu vergleichen, um zu entscheiden, ob es ohne Qualitätsverlust vergrößert werden kann.Diese Methoden verursachen keine Re-Flows. Das Ändern des DOM oder CSSOM verursacht Reflows. (
classList.add,appendChildetc.). Und diese Methoden sind tatsächlich der Performance-Flaschenhals. WasgetComputedStyle,getBoundingClientRect,offsetWidthtun können, ist, ein synchrones Layout zu erzwingen, wenn DOM/CSSOM im selben Frame zuvor geändert wurde. Aber normalerweise sind diese Methoden super schnell. Was Sie nicht wollen, ist, mehrere erzwungene synchrone Layouts in einem Frame zu verursachen oder ein erzwungenes Layout zu verursachen und das DOM/CSSOM im selben Frame danach zu ändern.Es geht also nicht darum, diese Methoden nicht zu verwenden, sondern sie zur richtigen Zeit zu verwenden. Und der einfachste Ansatz ist zu sagen, dass Sie das Layout jederzeit lesen können, aber Sie sollten Layout-Änderungen auf
rAFbeschränken.Übrigens: Der obige Code erzwingt ein Layout, wenn Sie mehr als ein Bild auf der Seite haben.
Sie können dies leicht beheben, indem Sie diese Methoden anstelle der normalen jQuery-Methoden verwenden (
toggleClassRafanstelle vontoggleClass)