Photo by Jess Bailey on Unsplash
How to Get the File Size of an Image imported by CSS Background URL Method in Javascript
The HTML and CSS
Here is the HTML
<div id="hello" class="container1"></div>
<div id="hello" class="container2"></div>
We have two <div>
elements with different classes applied to them. But they both share the same id hello
The CSS:
.container1 {
width: 1280px;
height: 720px;
background: url("./img/bunny.png") fit 0 0;
}
.container2 {
width: 240px;
height: 96px;
background: url("./img/rabbit.png") fit 0 0;
}
The Javascript Explained
I will explain the Javascript in incremental segments. If you want to grab the full code jump to the bottom.
Getting the computed CSS Styles
const divs = document.querySelectorAll('#hello')
Through the query selector method, we fetch all the NodeList
items that contain the id hello
.
divs.forEach( div => {
// ...
})
We can now iterate through individual div
elements exclusively using forEach()
and pass them as an argument through an arrow function for further operations.
divs.forEach( div => {
const styles = window.getComputedStyle(div)
const image = styles.backgroundImage
console.log(image)
})
The Window.getComputedStyle()
method returns an object containing the values of all CSS properties of an element, after applying active stylesheets and resolving any basic computation those values may contain.
Then we extract the CSS background-image property from styles
object.
It should output:
Output:
---
url("https://127.0.0.1/img/bunny.png")
---
Sanitising the URL using Regex
Now we need to clean up the URL. We will need to write a bit of regex.
const imgURL = image.replace(/url\((['"])?(.*?)\1\)/gi, '$2')
console.log(imgURL)
This should sanitize the URL values:
Output:
---
https://127.0.0.1/img/bunny.png
---
I'll skip over the RegEx explanation. You can see it here at regexpr.com. The replace()
method is pretty straightforward.
The regex breaks down the string into two groups. Group 1 is "
and group 2 is https://127.0.0.1/img/bunny.png
. The second group replaces the original string and puts it inside imgURL
.
We now have the URL Path to the image.
Retrieving the file size by using XMLHttpRequest()
const xhr = new XMLHttpRequest()
const method = 'HEAD'
const url = imgURL
xhr.open(method, url)
xhr.onreadystatechange = () => {
// ...
}
xhr.send(null)
This is the basic structure for XHR requests. You can find out more info about it on the MDN docs.
xhr.open()
initializes a newly created HTTP request with a request method and URL. Our request method here is the HTTP HEAD
which requests the headers that would be returned if the HEAD request's URL was instead requested with the HTTP GET
method. For example, if a URL might produce a large download, a HEAD
request could read its Content-Length
header to check the filesize without actually downloading the file.
We are going to find out how in the next segment. But, before that, we need to know that xhr.send()
sends the request to the server.
xhr.onreadystatechange()
fires when the readyState
of the XMLHttpRequest
client changes. The readyState
has five values enumerated from 0 to 4.
Value | State | Description |
0 | UNSENT | A client has been created. open() not called yet. |
1 | OPENED | open() has been called. |
2 | HEADERS_RECEIVED | send() has been called, and headers and the status are available. |
3 | LOADING | Downloading; responseText holds partial data. |
4 | DONE | The operation is complete. |
Find out more about it on MDN docs.
const xhr = new XMLHttpRequest()
const method = 'HEAD'
const url = imgURL
xhr.open(method, url)
xhr.onreadystatechange = () => {
if( xhr.readyState == 4) {
if( xhr.status == 200 ) {
var fileSize = xhr.getResponseHeader('Content-Length')
// ...
}
else {
console.info('XHR readyState: ' + xhr.readyState)
}
}
}
xhr.send(null)
We check whether the response from the server has been received successfully, i.e. readyState = 4
and if that is true then if the HTTP response status code is 200 OK
.
Then we proceed to retrieve the value of the response header Content-Length
. It will give us the file size in bytes.
Now we will convert the file size into kilobytes and put the value inside the <div>
element
xhr.onreadystatechange = () => {
if( xhr.readyState == 4) {
if( xhr.status == 200 ) {
var fileSize = xhr.getResponseHeader('Content-Length')
fileSizeInKB = (parseInt(fileSize) / 1024).toFixed(2)
div.innerHTML = "File Size: " + fileSizeInKB + "KB"
}
else {
console.info('XHR readyState: ' + xhr.readyState)
}
}
}
The Content-Length
gives us the file size as a string. We convert it to an integer via parseInt( fileSize )
and get the length in bits. Then we divide it by 1024 to convert it into kilobytes. We want our number to be rounded to two decimal places so we use the .toFixed( 2 )
method.
Voila! We have got the file size of our CSS background image.
Finally, we need to show it in our HTML. We do that via the div.innerHTML
property.
Full Code
const divs = document.querySelectorAll('#hello')
divs.forEach( div => {
const styles = window.getComputedStyle(div)
const image = styles.backgroundImage
// Clean up the Image URL using regex
const imgURL = image.replace(/url\((['"])?(.*?)\1\)/gi, '$2')
/*
* Send request to the server to retrieve the file size
* from Content-Length response header.
*/
const xhr = new XMLHttpRequest()
const method = 'HEAD'
const url = imgURL
xhr.open(method, url)
xhr.onreadystatechange = () => {
if( xhr.readyState == 4) {
if( xhr.status == 200 ) {
var fileSize = xhr.getResponseHeader('Content-Length')
fileSizeInKB = (parseInt(fileSize) / 1024).toFixed(2)
div.innerHTML = "File Size: " + fileSizeInKB + "KB"
} else {
console.info('XHR readyState: ' + xhr.readyState)
}
}
}
xhr.send(null)
})
If you somehow ended up here, thank you.