5

Looks like I've discovered a chrome bug.

Working demo is here. If you try to scroll a list you will eventually see blank spaces instead of list items. The issue is not observed in Safari (except for broken padding at the list end).

enter image description here

To reproduce the issue create <div/> with overflow: scroll and flex-direction: row-reverse or column-reverse as a container and fill it with any type of items. Full code is here

Does anyone know how to workaround the issue?

  • A [mcve] should be included in the [question itself](//meta.stackoverflow.com/q/254428/90527), both so the question is self-contained and in case the linked page goes down, goes away or gets edited. Please read the [help] articles, especially "[ask]". – outis Dec 22 '22 at 21:26

4 Answers4

5

I found two workarounds.

Workaround 1

The simplest workaround is to add content-visibility: auto; to the item class:

.item {
  /* other styles */
  content-visibility: auto;
}

This article covers this property in details and has a great video demo in the example section.

This workaround solved the issue but resulted in really janky scrolling behavior. You might solve it with contain-intrinsic-size property. The same article covers it in this section. Here's a quote:

In order to realize the potential benefits of content-visibility, the browser needs to apply size containment to ensure that the rendering results of contents do not affect the size of the element in any way. This means that the element will lay out as if it was empty. If the element does not have a height specified in a regular block layout, then it will be of 0 height.

This might not be ideal, since the size of the scrollbar will shift, being reliant on each story having a non-zero height.

Thankfully, CSS provides another property, contain-intrinsic-size, which effectively specifies the natural size of the element if the element is affected by size containment.

Workaround 2

I ended up replacing the column-reverse behavior with just column and then scrolling to the bottom of the list with element.scrollIntoView.

Here's the code I used for that (React): https://stackoverflow.com/a/52266212/4745802

2

This issue is related to Chromium Issue ID 1380100 and has been fixed in version 109.0.5414.25 of Chrome.

Once the issue occurs any action which causes a re-painting of the component will fix it temporarily but it will reoccur if you scroll back down and up.
I tested the following approach as a temporary fix successfully.

Add a p element with ID 'scroller' on the page (this could easily be hidden by making the color the same as the background color for example). Updating this p element will trigger the re-painting of the flex container.

<p id="output">scrollTop: 0</p>

Then add an event targeting the scroll event to the flex container (you will notice that I added ID scroller1 to the flex container div.)

const output = document.querySelector("#output")
const scroller = document.querySelector("#scroller1")
scroller.addEventListener("scroll", (event) => {
    output.textContent = `scrollTop: ${scroller.scrollTop}`
})

Finally seeing that this is a Chrome bug you can apply this only if executed by Chrome as follows.

var is_chrome = navigator.userAgent.toLowerCase().indexOf('chrome') > -1;
if(is_chrome)
{
...`enter code here`
}

So a simplified example index.html file with the fix in looks like this.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        html, body {
            padding: 0;
            margin: 0;
            font-family: sans-serif;
            background: #525252;
        }
        body {
            display: flex;
            align-items: center;
            justify-content: space-evenly;
            height: 100vh;
        }
        .item {
            background: #ececec;
            padding: 20px;
            display: grid;
            place-items: center;
            border-radius: 8px;
        }
        .list {
            display: flex;
            flex-direction: column-reverse;
            width: 600px;
            height: 600px;
            overflow-y: scroll;
            gap: 16px;
            background: #2B2B2B;
            padding: 16px;
        }
        #output {
            color: cyan;
        }
    </style>
</head>
<body>
    <div class="list" id="scroller1"></div>
    <div id="output">scrollTop: 0</div>
<script>
    // fix - comment out to see issue
    var is_chrome = navigator.userAgent.toLowerCase().indexOf('chrome') > -1
    if(is_chrome)
    {
        const output = document.querySelector("#output")
        const scroller = document.querySelector("#scroller1")
        scroller.addEventListener("scroll", (event) => {
            output.textContent = `scrollTop: ${scroller.scrollTop}`
        })
    }
    // end of fix
    const list = document.querySelector('.list')
    for (let i = 0; i <= 400; i++) {
        let item = document.createElement('div')
        item.setAttribute('class', 'item')
        item.textContent= 'Item '+ i
        list.appendChild(item)
    }
</script>
</body>
</html>

If you are using a framework which controls element updates then you might have to use a method other than a simple p element update to trigger a repainting of the component but the Event Handler approach should stay the same.

  • If developers use the snippet in this answer as-is, that is_chrome check of navigator.userAgent will also cause all future users with Chrome 109+ to be served the workaround code, forever. So, the code in this answer could be improved by future-proofing it with some check that's not just looking for Chrome in the User-Agent string (if possible) ー so that Chrome 109+ users don't get served the workaround forever. – sideshowbarker Dec 08 '22 at 11:20
  • @sideshowbarker - thank you that suggestion makes sense. Just for the record I see Chrome 109 was pushed into production this week and the issue has been fixed on my system. – Caspianstrider Jan 12 '23 at 18:57
1

This is a silly, but working workaround suggested in the Chromium bug thread (Chromium Issue ID 1380100).

Simply set the background of the parent container to:

background: linear-gradient(transparent,transparent) fixed;

This forces Chrome to render the container in a way, that does not mess up the rendering of the contained elements, even though the container has 'flex-direction: column-reverse' or 'flex-direction: row-reverse'.

Sune S.-T.
  • 235
  • 2
  • 15
0

To work around the problem make the parent of the flex container also a flex container with flex-direction: column.

The problem seems to go away when the primary flex container is also a flex item.

Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701
  • Thanks for your answer. Sadly, it didn't help. Also, in the demo both two lists are already inside the body with ```display: flex``` – Max P. Urban Nov 29 '22 at 18:04
  • That's strange. I just had it working with `flex-direction: column`. Okay. Will take another shot. – Michael Benjamin Nov 29 '22 at 18:23
  • Yeah, some kind of weird bug. I just got it to work by adding a border around the containers. But after a refresh, it didn't work anymore. – Michael Benjamin Nov 29 '22 at 18:27
  • So... playing around with multiple properties (in the dev tools) gets the layout to work. But nothing is stable. – Michael Benjamin Nov 29 '22 at 18:33
  • 2
    I think you make it work just by forcing chrome to rerender the layout. You can also make it work by resizing the chrome window as well as by changing the layout properties on the fly – Max P. Urban Nov 30 '22 at 11:51