Skip to content

Commit

Permalink
Closes #6530 #6531 #6532 #6534 #6535 #6552: Preload images markup (#6559
Browse files Browse the repository at this point in the history
)
  • Loading branch information
Miraeld authored Apr 24, 2024
2 parents 44e5116 + c6a7c1e commit 41f5192
Show file tree
Hide file tree
Showing 20 changed files with 808 additions and 94 deletions.
202 changes: 127 additions & 75 deletions assets/js/lcp-beacon.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,116 +7,168 @@ function isIntersecting(rect) {

function LCPCandidates(count) {
const potentialCandidates = document.querySelectorAll(
"img, video, p, main, div"
"img, video, picture, p, main, div"
);
const topCandidates = [];

potentialCandidates.forEach((element) => {
potentialCandidates.forEach(( element ) => {
const rect = element.getBoundingClientRect();
if (
rect.width > 0 &&
rect.height > 0 &&
isIntersecting(rect)
isIntersecting( rect )
) {
const visibleWidth = Math.min(rect.width, (window.innerWidth || document.documentElement.clientWidth) - rect.left);
const visibleHeight = Math.min(rect.height, (window.innerHeight || document.documentElement.clientHeight) - rect.top);
const area = visibleWidth * visibleHeight;
const imageURL = getImageUrlFromElement(element);
if (imageURL !== null) {
const elementInfo = getElementInfo(element);
if (elementInfo !== null) {
// Insert element into topCandidates in descending order of area
for (let i = 0; i < topCandidates.length; i++) {
if (area > topCandidates[i].area) {
topCandidates.splice(i, 0, { element, area, imageURL });
topCandidates.splice(i, 0, {element, area, elementInfo});
topCandidates.length = Math.min(
count,
topCandidates.length
);
); // Keep only specified number of elements
break;
}
}
// If topCandidates is not full, append
if (topCandidates.length < count) {
topCandidates.push({ element, area, imageURL });
topCandidates.push({element, area, elementInfo});
}
}
}
});

return topCandidates.map((candidate) => ({
element: candidate.element,
imageURL: candidate.imageURL,
elementInfo: getElementInfo(candidate.element),
}));

}

function getImageUrlFromElement(element) {
// Check if the element is an <img> element
if (element.tagName === "IMG") {
return element.src;
}

// Check if the element has a background image using computed style
const backgroundImage = window
.getComputedStyle(element)
.getPropertyValue("background-image");

// Check if the background image property is not 'none'
if (backgroundImage && backgroundImage !== "none") {
// Extract the URL from the 'url("...")' format
const imageUrl = backgroundImage.replace(/^url\(['"](.+)['"]\)$/, "$1");
return imageUrl;
}

// If no image is found
return null;
function getElementInfo(element) {
const nodeName = element.nodeName.toLowerCase();
const element_info = {
type: "",
src: "",
srcset: "",
sizes: "",
sources: [],
bg_set: [],
current_src: ""
};

const css_bg_url_rgx = /url\(\s*?['"]?\s*?(\S+?)\s*?["']?\s*?\)\s*?([a-zA-Z0-9\s]*[x|dpcm|dpi|dppx]?)/ig;

if (nodeName === "img" && element.srcset) {
element_info.type = "img-srcset";
element_info.src = element.src;
element_info.srcset = element.srcset; // capture srcset
element_info.sizes = element.sizes; // capture sizes
element_info.current_src = element.currentSrc;
} else if (nodeName === "img") {
element_info.type = "img";
element_info.src = element.src;
element_info.current_src = element.currentSrc;
} else if (nodeName === "video" && element.poster) {
element_info.type = "img";
element_info.src = element.poster;
element_info.current_src = element.poster;
} else if (nodeName === "picture") {
element_info.type = "picture";
element_info.src = element.querySelector('img').src;
element_info.sources = Array.from(element.querySelectorAll('source')).map(source => {
return {
srcset: source.srcset,
media: source.media
};
});
} else {
const computed_style = window.getComputedStyle(element, null);
const bg_props = [
computed_style.getPropertyValue("background-image"),
getComputedStyle(element, ":after").getPropertyValue("background-image"),
getComputedStyle(element, ":before").getPropertyValue("background-image")
];

const full_bg_prop = bg_props.filter(prop => prop !== "none").join("");
element_info.type = "bg-img";
if (full_bg_prop.includes("image-set(")) {
element_info.type = "bg-img-set";
}
if (!full_bg_prop || full_bg_prop === "") {
return null;
}

const matches = [...full_bg_prop.matchAll(css_bg_url_rgx)];
element_info.bg_set = matches.map(m => m[1] ? {src: m[1].trim() + (m[2] ? " " + m[2].trim() : "")} : {});
// Check if bg_set array is populated with empty objects
if (element_info.bg_set.every(item => item.src === "")) {
// If bg_set array is populated with empty objects, populate it with the URLs from the matches array
element_info.bg_set = matches.map(m => m[1] ? {src: m[1].trim()} : {});
}

if (element_info.bg_set.length > 0) {
element_info.src = element_info.bg_set[0].src;
if (element_info.type === "bg-img-set") {
element_info.src = element_info.bg_set;
}
}
}

return element_info;
}

let performance_images = [];

async function main() {
// Filter the array based on the condition imageURL is not null
const filteredArray = LCPCandidates(1)
if (filteredArray.length !== 0) {
console.log("Estimated LCP element:", filteredArray);
performance_images = filteredArray.map((item) => ({
src: item.imageURL,
label: "lcp",
}));
} else {
console.log("No LCP candidate found.");
}

var above_the_fold_images = document.querySelectorAll("img");

for (var i = 0; i < above_the_fold_images.length; i++) {
var image = above_the_fold_images[i];
var rect = image.getBoundingClientRect();
var intersecting = isIntersecting(rect);
if (intersecting) {
var parent = image.parentNode;
while (parent !== document) {
var displayStyle = window.getComputedStyle(parent).display;
var visibilityStyle =
window.getComputedStyle(parent).visibility;
if (displayStyle === "none" || visibilityStyle === "hidden") {
break;
}
parent = parent.parentNode;
}
const isDuplicate = performance_images.some(
(item) => item.src === image.src
);

// If it's not a duplicate, push the new element
if (!isDuplicate && parent === document) {
performance_images.push({
src: image.src,
label: "above-the-fold",
});
}
}
}
console.log(performance_images);
var performance_images_json = JSON.stringify(performance_images);
window.performance_images_json = performance_images_json;
console.log(performance_images_json);
const filteredArray = LCPCandidates(1);
if (filteredArray.length !== 0) {
console.log("Estimated LCP element:", filteredArray);
performance_images = filteredArray.map((item) => ({
...item.elementInfo,
label: "lcp",
}));
} else {
console.log("No LCP candidate found.");
}

var above_the_fold_images = document.querySelectorAll("img");

for (var i = 0; i < above_the_fold_images.length; i++) {
var image = above_the_fold_images[i];
var rect = image.getBoundingClientRect();
var intersecting = isIntersecting(rect);
if (intersecting) {
var parent = image.parentNode;
while (parent !== document) {
var displayStyle = window.getComputedStyle(parent).display;
var visibilityStyle =
window.getComputedStyle(parent).visibility;
if (displayStyle === "none" || visibilityStyle === "hidden") {
break;
}
parent = parent.parentNode;
}
const isDuplicate = performance_images.some(
(item) => item.src === image.src
);

// If it's not a duplicate, push the new element
if (!isDuplicate && parent === document) {
performance_images.push({
src: image.src,
label: "above-the-fold",
});
}
}
}
console.log(performance_images);
var performance_images_json = JSON.stringify(performance_images);
window.performance_images_json = performance_images_json;

const data = new FormData();

Expand Down
2 changes: 1 addition & 1 deletion assets/js/lcp-beacon.js.min.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 41f5192

Please sign in to comment.