To visualize how this works, let’s take a list with a 1000 rows where each row has a height of 35 pixels. Thus, the total height of that list equals the total item count multiplied by the height of the item, i.e. 1000 × 35px = 35000px. Based on this height, the virtualized list creates an empty DOM element that matches the height and the width dimension of its parent visible window/element. In the UI, we’d like to display the scrollable list with a height of 150 pixels.
This empty placeholder DOM element allows to emulate the behavior of a non-virtualized list by taking the position of the scrollbar within the list: the scroll position basically determines which index we’re at in the list. Combined with the height per item as well as the overall maximum height of the list, the visible/relevant items of the list can be determined and rendered. All other items are skipped and not rendered. Rendered items receive the style position: absolute
and top
property calculated based on the item’s index and height.
<div style="position: relative; width: 300px; height: 150px; overflow: auto;">
<div style="width: 100%; height: 35000px;">
<div style="position: absolute; left: 0px; top: 3395px; width: 100%; height: 35px;">Item 97</div>
<div style="position: absolute; left: 0px; top: 3430px; width: 100%; height: 35px;">Item 98</div>
<div style="position: absolute; left: 0px; top: 3465px; width: 100%; height: 35px;">Item 99</div>
<div style="position: absolute; left: 0px; top: 3500px; width: 100%; height: 35px;">Item 100</div>
<div style="position: absolute; left: 0px; top: 3535px; width: 100%; height: 35px;">Item 101</div>
<div style="position: absolute; left: 0px; top: 3570px; width: 100%; height: 35px;">Item 102</div>
<div style="position: absolute; left: 0px; top: 3605px; width: 100%; height: 35px;">Item 103</div>
<div style="position: absolute; left: 0px; top: 3640px; width: 100%; height: 35px;">Item 104</div>
<div style="position: absolute; left: 0px; top: 3675px; width: 100%; height: 35px;">Item 105</div>
</div>
</div>
In the example above, the scrollTop
property of the root/list element is 3477
. That means the first visible item would be 3477 ÷ 35 = ~99.34 which means we’re starting somewhere between item #99 and item #100 in the list (i.e. ~⅔ of item #99 is visible).
Now, as the maximum height of the list itself is 150 pixels, 150 ÷ 35 = ~4.29 tells us the amount of visible items. As you can see in the DOM example above, a small amount of non-visible items above and below the visible range (i.e. items #97, #98, #104, and #105) are rendered in addition so that it appears like other items above/below the visible items are existing when scrolling, resulting in a smooth scroll experience (compared to simply adding items into the view once they enter the visible window).
This safety-net approach is called »overscan«, a term borrowed from television.
Virtualized list also work as horizontal lists. In this case, the logic is simply switched for width
/height
and top
/left
. A combination of both horizontally and vertically scrollable lists would be possible as well, of course.
As you may guess, the big challenge of virtualized lists is items with varying/variable heights based on their content, as the basic concept of calculating/reserving the space with a placeholder DOM element does not work as-is anymore.