skip to Main Content

I am having a problem with high CLS (Content Layout Shift) while using Bootstrap (4.5) grid for two column layout with column order change.

CLS is a Core Web Vital metric. Basically Google sees a problem when webpage’s parts are moving when the page is loading. Supposedly this metric is to affect SEO.

On high resolutions my layout consists of two columns. Main content on the right and sidebar on the left. On lower resolutions sidebar content is pushed down below main content. So HTML looks like this:

<div class="container-fluid">
    <div class="row">
        <div class="col-lg-8 order-lg-2">
        </div>
        <div class="col-lg-4 order-lg-1">
        </div> 
    </div> 
</div>

The problem is that for brief moment while the page renders on desktops, the main content appears on the left side, then milliseconds later it shifts to the right place on the right. With simple pages (with simple DOM or no external resources) the shift is not detectable. 

I’ve prepared an example of such page. (The source code is on github). To measure CLS I am using Lighthouse in Chrome. In my case when I refresh the page I can see columns moving and Lighthouse informs me of CLS value of 0.326. The result might depend on rendering resources so you might get something different. But it seems Google Page Insight gives similar result:

enter image description here

Anyway, is there a way to avoid such shift while the page renders? 

2

Answers


  1. Chosen as BEST ANSWER

    It seems the problem is more Chrome related then flexbox or bootstrap. It turned out that the problem is caused by premature renderation. Chrome's parser "yields" (so it triggers rendering): 

    • after reading 65536 bytes of data, 
    • after encountering <script> tag after reading 50 "tokens" (which I think are basically html tags).

    The example I provided shows the first case (but actually my real website experiences CLS because the second one). Both of those cases have "bugs" related to them submitted: 1130290 and 1041006

    So the answer to the problem is hoping that the "bugs" will get resolved. In the meantime depending on actual cause you can limit file size or remove <script> tags.     


  2. Short Answer

    Minimise the HTML and the problem seems to go away.

    Longer Answer

    I did a bit of digging, this isn’t a complete "this is exactly what happens" answer but I got enough of an idea to come up with the above solution and to roughly explain my reasoning. I am hoping someone can expand upon the gaps in my knowledge.

    So what made me come up with the above solution?

    After profiling the page load I notice that there were 2 HTML parse tasks being created.

    One dealt with lines 1-770 of the HTML and the other dealt with lines 771 to the end of the document.

    Because of this the page appears to render the first 770 lines and then recalculates the layout on the second HTML parse task because you have swapped the order (and the .col-lg-4 column is in the second parsing pass HTML).

    You won’t see this on a "normal" page as if the page is rendered in DOM order layouts will be correct anyway and the second HTML parsing pass will just add more detail to the layout.

    As it appeared to be pretty consistent where it cut the page off I removed all line breaks and white space. My theory being that whatever algorithm is deciding where to split the HTML up is using line number as part of that calculation.

    By reducing the effective lines to 15 or so I was hoping to make that algorithm only parse the HTML in one pass.

    It actually still does it in 2 but the last pass is the closing </html> tag only so doesn’t matter. The result of this is when the parsed HTML is combined with the CSSOM it can calculate the layout correctly.

    a bit of a hack but it should work up to certain page depths.

    please note – if I doubled the DOM node count this workaround did not work again. If I changed the length of each list item (i.e. put lorem ipsum in) but didn’t change the structure it did not make a difference. So it appears to be some combination of number of DOM elements and line number that decides when the HTML parser should stop its first pass.

    A possible solution

    Go back to old layout models. If you use float:left and float:right it should work. I think this specific issue is a combination of page complexity (number of DOM nodes) and using flexbox.

    With flexbox being slightly slower than old layout models and sometimes having to use multiple passes (old layout models are single pass) I would imagine this issue would not persist with the above recommendation.

    Where I found out about multiple layout passes in certain scenarios

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search