I have a button to pull some data from backend. But the length of the data is uncertain and the time to load is also uncertain.
This will generate high CLS penalty. Is there anyway to avoid this?
var stHandler = 0;
$('#render').click(function(){
clearTimeout(stHandler);
$('#result').html('loading...');
// [Simulate the server response time]
// CLS will not be penalized if the everything happens in 100ms,
// but most of the case, the server will return the data longer than that
stHandler = setTimeout(function(){
$('#result').html(fakeResultBuilder);
}, 500 + Math.random()*1000);
})
// [Simulate the real-world condition]
// Every time you load this content, the height will be different.
function fakeResultBuilder(){
var html = '';
for (var i=0; i<Math.random()*100; i++) {
html += '<div class="block"></div>';
}
return html;
}
button {cursor:pointer}
.block {height:10px; background:#f00; width:20px; margin:5px}
#result {border:1px solid #999}
footer {margin-top:1em}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
<button id="render">Render</button>
</div>
<h2>Result:</h2>
<div id="result"></div>
<footer>Page Footer - will have huge CLS sometimes</footer>
The key is to adjust as much as possible within the 500ms grace period (note not 100ms as given in your code comment). The 500ms grace period is set because clicking on things (like your Render button) is an expected shift. However, if it happens considerably later (and 500ms is set as the limit) then it's back to being an unexpected shift. The user may be in the middle of reading some of the footer while they wait.
While the full response may not be able to be returned within that 500ms, you can still reserve some space (perhaps using
min-height
?) to, at the very least, reduce the CLS. The default height of an empty div is 0px so ANYTHING you do here will be better than than that as surely it will be larger than 0ms?Additionally if it's enough to always knock the footer off-screen then adding a minimum height to push that footer off screen will mean any further moves for it are not considered CLS.
This may leave a large white space, which may not be ideal, so you may wish to consider some kind of "Loading..." message or a loading spinner. This will give an indication that something is happening to the user rather than them being unsure and perhaps rage-clicking the render button again and again.