About
A layout shift occurs when the rendering of the web page shifts / move element from their current position.
Causes
Image
For instance:
- without the knowledge of the target ratio (via the height and width attribute of an image, the browser can't reserve space for the image
- it download the image calculate the space needed for the image
- recalculate the layout
- and shifts element around to be able to put the image
That's what's called a layout shift
Solution:
- Set the correct image ratio on the image tag with the height and width attribute
Style
When you change a layout css property after page load (the first rendering), you will also create a layout shift.
Solution: If you want to change two layout property at the same time, you should use the window.requestAnimationFrame function to avoid a layout shift.
Example with a dynamic correction of a fixed top header such as the Bootstrap navbar where:
- we add the fixed-top class that set the position to fixed
- and correct it with a padding on the body.
window.requestAnimationFrame(function() {
let fixedNavbar = document.querySelector(".navbar[data-type=\"fixed-top\"]")
fixedNavbar.classList.add("fixed-top");
let offsetHeight = fixedNavbar.offsetHeight;
document.body.style.setProperty("padding-top",offsetHeight+"px");
});
Measurement
Occurences: Devtool
In the devtool, layout shift can be seen
- in the performance tab:
- click the start and reload page (Ctrl+Shift+E)
- stop it when the page has finish loaded
- check the experience row.
- in the rendering tool where you can see the shifted region and and the web core vitals.
Cumulative layout shift
The Cumulative layout shift or CLS is a web core vital metrics that groups all layout shift during page load.
- The devtool can also add an overlay. See the web core vital for more information.
- A library can report back the cumulative layout shift (or CLS) measurement. See the example below
Calculation Example with Perfume
Example with perfume
- The element that we are going to shift
<button>Click to see the cumulative value</button>
- Simulate a layout shift
setTimeout(function(){
let button = document.querySelector("button");
button.style.display = "block";
button.style.marginLeft= "auto";
button.style.marginRight= "auto";
button.style.width= "fit-content";
}
,2000);
- The engine
new Perfume({
analyticsTracker: options => {
const { metricName, data, eventProperties, navigatorInformation, vitalsScore, } = options;
switch (metricName) {
case 'cls':
console.log('cumulativeLayoutShift value: '+data);
break;
case 'clsFinal':
console.log('cumulativeLayoutShiftFinal: '+data);
break;
}
},
});
- Output: