Short version of the question
Is it possible to find only elements on a page that have a background-image
or background: url
set (including in stylesheets) without looping through every element on the page and using getComputedStyle(el);
.
If not is it possible to optimise the elements I look through to reduce JS execution time?
Longer version of the question
As part of this related question I am trying to find a solution to gathering the size of all elements above the fold that may impact the "visually complete" state of the page.
The related question covers checking all CSS etc. is loaded so I am left with images (including background images) to check.
I am looking to make the following functions as performant as possible (as I may have to call it multiple times if I am unable to solve the main problem in the other question).
The main function is getRects()
. I have included the checkRectangle
function for completeness but the main concern is the way I am gathering candidates for the checkRectangle
function (having to loop through every element on the page).
var doc = window.document;
var browserWidth = window.innerWidth || doc.documentElement.clientWidth;
var browserHeight = window.innerHeight || doc.documentElement.clientHeight;
function checkRectangle(el){
var rtrn = false;
if (el.getBoundingClientRect) {
var rect = el.getBoundingClientRect();
//check if the bottom is above the top to ensure the element has height, same for width.
//Then the last 4 checks are to see if the element is in the above the fold viewport.
if (rect.bottom <= rect.top || rect.right <= rect.left || rect.right < 0 || rect.left > browserWidth || rect.bottom < 0 || rect.top > browserHeight) {
rtrn = false;
}else{
rtrn = {};
rtrn.bot = rect.bottom;
rtrn.top = rect.top;
rtrn.left = rect.left;
rtrn.right = rect.right;
}
}
return rtrn;
}
//function to get the rectangles above the fold (I do other things to check fonts are loaded etc. so images are the only thing left to check)
function getRects(){
var rects = [];
var elements = doc.getElementsByTagName('*');
var re = /url\(.*(http.*)\)/ig;
for (var i = 0; i < elements.length; i++) {
var el = elements[i];
var style = getComputedStyle(el);
if(el.tagName == "IMG"){
var rect = checkRectangle(el);
if(rect){
//The URL is stored here for later processing where I match performance timings to the element, it is not relevant other than to show why I convert the `getBoundingClientRect()` to a simple object.
rect.url = el.src;
rects.push(rect);
}
}
//I also need to check for background images set in either CSS or with inline styles.
if (style['background-image']) {
var rect = checkRectangle(el);
if(rect){
var matches = re.exec(style['background-image']);
if (matches && matches.length > 1){
rect.url = matches[1].replace('"', '');
rects.push(rect);
}
}
}
}
Concerns / things that I can't work out
- I see no way of not looping through all elements on the page and using
getComputedStyle(el)
to check if they have abackground-image
set. If I can reduce the candidates sufficiently that would solve my problems. - At the moment (due to having to call the function multiple times) I am not doing a check for
background: url
but that needs adding in as an efficient way as possible. - Is there a way of discarding some elements on the page that I can guarantee are not "above the fold" that wouldn't carry a massive performance penalty (bearing in mind anything could be
position: fixed
at the top of the page?).
Things I know I can do
- If I can find a better way of checking for
background
andbackground-image
then I know images become easier as I can usequerySelectorAll
and limit that list.
Additional information / thoughts
I am already tracking every network request using PerformanceObserver
.
Is there perhaps a way I could look at every request
, grab the file name if it is an image and then use the filename to work out where that image is displayed on the page, even if it is a background-image
or background: url
set in external CSS?
Alternative way of phrasing the question.
How could I possibly limit a list of elements that can make a network call for an image and how can I then check if they are above the fold as efficiently as possible?