How to Get the File Size of an Image imported by CSS Background URL Method in Javascript

bot5ambot5am
5 min read

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.

ValueStateDescription
0UNSENTA client has been created. open() not called yet.
1OPENEDopen() has been called.
2HEADERS_RECEIVEDsend() has been called, and headers and the status are available.
3LOADINGDownloading; responseText holds partial data.
4DONEThe 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.

0
Subscribe to my newsletter

Read articles from bot5am directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

bot5am
bot5am

I am trying to mix fun with coding. So far, it has burned up on my face. I have voices in my head feeding me fear, wholesale. Coding is the only antidote to my fear of powerlessness. That's why I can't give up.