Skip to content

fix(render): four state-tracking fixes in the vertical virtual renderer - #4931

Draft
lukecotter wants to merge 5 commits into
tabulator-tables:masterfrom
lukecotter:fix/render-vdom-scrollheight-init
Draft

fix(render): four state-tracking fixes in the vertical virtual renderer#4931
lukecotter wants to merge 5 commits into
tabulator-tables:masterfrom
lukecotter:fix/render-vdom-scrollheight-init

Conversation

@lukecotter

@lukecotter lukecotter commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

What this is

Four state-tracking fixes in VirtualDomVertical, five commits, 29 added lines in one file
plus tests. They were four separate drafts (#4931#4934); one review beats four tabs on the
same file. Happy to split them back out.

  1. Initialize vDomScrollHeight (commit 1) — scrollRows read it before the first fill
    set it, so an early scroll worked from undefined.
  2. Correct the rerenderRows fallback index (commits 2 and 4) — with no anchor row it
    fell back to an index other than the one just computed. Commit 4 then separates "no anchor
    row found" from "window indices past the new row count", which had been conflated and so
    discarded the fallback.
  3. Keep content flush after a filter rerender (commit 3) — filtering a long list down to
    a short one left a blank strip.
  4. Clamp vDomTopPad to zero when removing top rows (commit 5) — one line; it could go
    negative.

Verification

npm run test:unit 471 pass, npm run test:e2e 22/22. New coverage:
VirtualDomVertical.scrollHeight.spec.js, VirtualDomVertical.rerender.spec.js, and an e2e
case in rerender-filter.spec.ts.

Performance-neutral: mutation churn is byte-identical to master at 500k rows.

Part of #4938.

vDomScrollHeight was only assigned inside _virtualRenderFill's full-fill
branch, never initialized. scrollRows reads it on the first scroll (before
any full fill), where it was undefined and produced a NaN comparison that
took the wrong add/remove branch. Initialize to 0 in the constructor and
reset it in clearRows.
The rerenderRows anchor-scan fallback used `this.rows.length - 1`, but
`this.rows` is the method (arity 0), so the expression was always -1. When
the scan found no anchor row (stale or out-of-range rendered window), the
renderer filled from position -1 and left vDomTop negative. Use the last
display-row index instead.
rerenderRows scanned the pre-filter vDomTop..vDomBottom window for an anchor
row, then filled against the post-filter rows. When that window pointed past
the new (smaller) row count, the stale topOffset inflated vDomTopPad into a
blank strip across the top. Fall back to a fresh fill (which resets
vDomTopPad) when the pre-filter window is invalid or no anchor was found.

Also derive vDomBottomPad in the position branch of _virtualRenderFill from
the current row count (mirroring the full-fill branch) instead of the cached
vDomScrollHeight, which goes stale when the row count shrinks and leaves an
inflated blank strip below the last row; refresh vDomScrollHeight too.

Adds a Playwright regression test filtering 2000 rows down to ~50 and
asserting no top/bottom blank strip.
In _removeTopRow, accumulated actual row heights can push vDomTopPad slightly negative, which sets a negative paddingTop and lets rendered content drift above the viewport. Clamp to zero after the adjustment.
@lukecotter lukecotter changed the title fix(render): initialize vDomScrollHeight fix(render): four state-tracking fixes in the vertical virtual renderer Aug 4, 2026
@rathboma

Copy link
Copy Markdown
Collaborator

@lukecotter love all these PRs, but it's hard for me to evaluate the impact of them all, or to know if you've had any hand in them, or tested them, beyond your AI agent.

Any way you could add a little video to each one showing what each PR does, or add a Luke-written comment with context for the fix and how you've tested it? For example - was this just a fun activity for you? Are you using this in production?

@lukecotter

lukecotter commented Aug 26, 2026

Copy link
Copy Markdown
Contributor Author

@rathboma
Of course I can do all that and I'll tone down letting ai comment.

Yes I have manually tested them all and run them through a chrome dev tools perf profile.

They are actually all fixes I applied to a custom version of the renderer I wrote to fix some bugs. One example was no rows being shown when the scroll is dragged down then quickly scrolled up.

That new renderer also improved performance quite a bit but it had gaps in behaviour and is obviously a sync and maintenance annoyance not to mention a potential upgrade nightmare so I thought it better to port the fixes so I can switch back to stock. I also opened some prs which improve performance and I have some more improvements that could be migrated from the custom version but I was hoping get some of these in first to avoid bombarding you with too much and make sure they other changes are stable first. Plus I want to re benchmark once these all land.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants