Skip to content

[popup] Keep highlight when pointer moves onto nested item content - #6199

Open
mdm317 wants to merge 1 commit into
mui:masterfrom
mdm317:perf/list-navigation-pointer-leave
Open

mdm317 wants to merge 1 commit into
mui:masterfrom
mdm317:perf/list-navigation-pointer-leave

Conversation

@mdm317

@mdm317 mdm317 commented Nov 7, 2026 •

Copy link
Copy Markdown
Contributor

Problem

When the pointer moves from one item to another, pointerleave's relatedTarget is the element it lands on:

<div role="option">   <!-- Item 1 -->
  <span>Apple</span>
</div>
<div role="option">   <!-- Item 2: landing on its padding works -->
  <span>Banana</span> <!-- landing here directly broke the check -->
</div>
  • Landing on Item 2's own box (for example its padding): relatedTarget is Item 2, so the highlight was kept.
  • Landing directly on a child of Item 2: relatedTarget is <span>Banana</span>.

onPointerLeave only checked listRef.current.includes(relatedTarget), so in the second case it treated the move as leaving the list and cleared the highlight. The next mousemove set it again, causing an extra state update and React commit.

Changes

Check whether relatedTarget is inside any item (with contains) instead of matching only the item elements, so child elements of items count too.

Side effect: consistent highlight while scrolling

Hovering an item and then scrolling the list without moving the pointer:

  • Before: the highlight was sometimes cleared and sometimes kept.
  • After: the highlight is always kept.

Performance

Measured in headless Chromium with a production React 19 build and 4x CPU throttling, as medians of 30 interleaved runs. Each item's text fills the item, so the pointer enters the next item's text directly. Each run parks the pointer on Item 0, then moves it with real mouse input one item at a time to Item 20 (20 boundary crossings).

Select

Scenario (20 crossings) Items Before After Delta
React commits 21 40 20 −50%
Script time 21 73.2 ms 55.9 ms −24%
Main-thread task time 21 111.5 ms 90.3 ms −19%
React commits 100 40 20 −50%
Script time 100 85.3 ms 58.7 ms −31%
Main-thread task time 100 128.8 ms 98.5 ms −24%

Combobox (100 items)

Scenario (20 crossings) Before After Delta
React commits 80 40 −50%
Script time 92.1 ms 63.5 ms −31%
Main-thread task time 128.5 ms 95.3 ms −26%

Tests

Added to popupListConformanceTests

@pkg-pr-new

pkg-pr-new Bot commented Nov 7, 2026

Copy link
Copy Markdown

commit: 9d1a650

@netlify

netlify Bot commented Nov 7, 2026

Copy link
Copy Markdown

✅ Deploy Preview for base-ui ready!

Built without sensitive environment variables

Name Link
🔨 Latest commit 9d1a650
🔍 Latest deploy log https://app.netlify.com/projects/base-ui/deploys/6aeeee523edb6c9b9700c1f5
😎 Deploy Preview https://deploy-preview-6199--base-ui.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@code-infra-dashboard

code-infra-dashboard Bot commented Nov 7, 2026 •

Copy link
Copy Markdown

Bundle size

Bundle Parsed size Gzip size
@base-ui/react 🔺+32B(+0.01%) 🔺+3B(0.00%)

Details of bundle changes

Performance

No regressions in 36 benchmarks · ran 4m 11s · details


Check out the code infra dashboard for more information about this PR.

@mdm317
mdm317 marked this pull request as ready for review November 7, 2026 17:05

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant