- I'm using CDK virtual scrolling to display a list of items on a page. Worst case scenario for number of items is around 2K.
- I'm using the default fixed size scrolling strategy.
- The source for the data is a DataSource with paging. There is some delay when fetching subsequent pages.
- The list elements can have a varying height based on the data they contain.
- There is some non-virtualized content before the list elements. I'm using a separate viewport and scrolling element
The problem I want to solve is to be able to programmatically scroll to an item using it's index. A sample API would be:
async scrollToIndex(viewPort: CdkVirtualScrollViewport, index: number): Promise<boolean>
Here is the high level overview of the approach I'm using:
- User initiates scrolling to index N
- Make a guess for the offset of index N using the formula
offset + (N * itemSize)
. This guess is expected to be inaccurate as there are different itemSizes. - Scroll to this offset and get the rendered range using
const range = viewport.getRenderedRange()
- There are 3 possibilities
N < range.start
- the guess was too high, correct offset and go to 2N >= range.end
- the guess was too low, correct offset and go to 2range.start <= N < range.end
- the guess is good enough to bring N into the viewport, go to 5
- Final scrolling - use scrollIntoView() to scroll the element with index N to the top
This approach is working however there are some issues with it:
- There needs to be a delay between the operations in Step 3
guess = this.getGuess(index, offset, itemHeight);
viewPort.scrollToOffset(guess);
await delayFor(400); // ARBITRARY DELAY
const renderedRange = viewPort.getRenderedRange();
- Sometimes the scoll in step 5 doesn't bring the element to the top, it will be a couple of pixels off
I need this to work on different mobile devices, which have varying performance characteristics, so I can't use an arbitrary delay, it could work on my PC/device but for some other device the delay could be not enough. I'm looking for a better solution without a hardcoded delay. For example wait for some event signifying that the range has changed after the scrollToOffset()
call.
Regarding issue 2 I'm not sure why it's happening and therefore don't know how to fix it. I'm more interested in solving the first issue but any help is appreciated. On the example scrolling to index 2 gives me incorrect results.
I created a minimal example here - in this example every 3rd list element is taller than the other elements.
To solve your problems, try this approach without unnecessary delays and handle scrolling more efficiently. Also, for the issue of the element not scrolling to the top, use the scrolledIndexChange event to track when the viewport has finished scrolling and then scroll the element into view.