Most of the time HTML images can take care of themselves. By default, an <img>
element will load an image file and display it at its natural width on the page. If the file is 200px wide and 400px tall, that's how big it will be when it loads.
If we need to adjust the size of the image, or how it is cropped or stretched or otherwise positioned, we can use CSS.
However, there are still times when we need to use JavaScript to manually work with an image, and often we need the image's natural dimensions to do so. For example, we may need to paint the image on an HTML canvas.
Getting this information is easy, as the HTMLImageElement API includes a width
and height
property that expose the natural dimensions of the image file. Let's see those properties in action in JavaScript:
const myImage = document.querySelector('img.my-image')
const { width, height } = myImage
console.log(`The image is ${width}px wide and ${height}px tall.`)
Determine any image file's size
There are cases when we need to know the size of an image that hasn't been loaded yet. When we first load a web page, the HTML is available before any image data has loaded. The HTML is parsed into the DOM, and the page is rendered. Many times we see the blink where the page has rendered with no image and the image finishes loading later.
This delay can cause problems if we need to use JavaScript to determine the size or behavior of elements around the image. Before the image has loaded, its naturalHeight
and naturalWidth
are both zero. If our code tries to lay out a web page based on the size and aspect ratio of the image before it loads, it will treat the image as if it had no size at all.
To get around this, we have to wait for the image data to be available in the browser. Once the image is loaded we can execute our JavaScript to resize and reposition elements based on the image.
Here's an example:
// Returns the original dimensions of an image file via callback
function getImgSize(imgSrc, callback) {
const newImg = new Image();
newImg.onload = function() {
const height = newImg.height;
const width = newImg.width;
callback({ width, height });
};
newImg.src = imgSrc; // this must be done AFTER setting onload
}
This function takes the URL of an image (imgSrc
) and waits until the image has loaded. At that point, the callback
function is called with the image's natural width and height.
To accomplish this, we first create an HTMLImageElement. This is the same element that is created in the DOM by an HTML <img>
tag.
HTMLImageElement
s have a width
and height
property. If a width
and height
attribute exist on an <img>
element in HTML, those values will be returned. That means they are not the natural width and height of the image data being displayed. However, if an image does not have a width
and height
attribute manually set, it will return the natural width and height of the image data.
It is important that we wait for the image data to load in the browser. We do this by listening for the onload
event. This event fires as soon as the image data is available in the browser. We have to be careful here – the image data might already be cached in the browser from a previous request, meaning the onload
event will fire as soon as the image has an src
. To ensure our onload
callback is actually called, we have to add it before we give the image an src
, which triggers the image data to load (from the browser cache or if necessary from a new request).
Other Applications
Another way we can take advantage of the onload
event listener is to preload multiple images in sequence without overwhelming the connection to the server. For example:
// This is a Promise-based async version
const loadImage = (imageSrc) => new Promise(resolve => {
const image = new Image();
image.onload = () => {
const height = image.height;
const width = image.width;
resolve({ image, width, height });
};
image.src = imageSrc;
});
const imageUrls = ['image1.png', 'image2.png', 'image3.png'];
const run = async () => {
for (const imageUrl of imageUrls) {
const { image, width, height } = await loadImage(imageUrl);
// do something with `image`, `width`, and `height`
}
};
run();