Skip to content

Conversation

@rubencarvalho
Copy link
Contributor

@rubencarvalho rubencarvalho commented Jan 16, 2026

Description

This PR optimizes event handling performance in the overlay system by optimizing backdrop click detection. I added an early return check to skip backdrop detection when no modal/page overlays are open, so we don't need to iterate through the event's composedPath on every pointerup event. I also changed instanceof HTMLElement to instanceof HTMLDivElement for faster filtering when checking for backdrop elements, since backdrops are always <div> elements.

I took the opportunity and also eliminated redundant stopPropagation() calls that were placed after stopImmediatePropagation(), since stopImmediatePropagation() already prevents all further event propagation.

Motivation and context

Some code cleanup feels nice?

Author's checklist

  • I have read the CONTRIBUTING and PULL_REQUESTS documents.
  • I have reviewed at the Accessibility Practices for this feature, see: Aria Practices
  • I have added automated tests to cover my changes.
  • I have included a well-written changeset if my change needs to be published.
  • I have included updated documentation if my change required it.

Reviewer's checklist

  • Includes a Github Issue with appropriate flag or Jira ticket number without a link
  • Includes thoughtfully written changeset if changes suggested include patch, minor, or major features
  • Automated tests cover all use cases and follow best practices for writing
  • Validated on all supported browsers
  • All VRTs are approved before the author can update Golden Hash

@changeset-bot
Copy link

changeset-bot bot commented Jan 16, 2026

⚠️ No Changeset found

Latest commit: b86dd8d

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@rubencarvalho rubencarvalho added the Status: Ready for review PR ready for review or re-review. label Jan 16, 2026
@rubencarvalho rubencarvalho marked this pull request as ready for review January 16, 2026 11:47
@rubencarvalho rubencarvalho requested a review from a team as a code owner January 16, 2026 11:47
@github-actions
Copy link
Contributor

github-actions bot commented Jan 16, 2026

📚 Branch Preview Links

🔍 First Generation Visual Regression Test Results

When a visual regression test fails (or has previously failed while working on this branch), its results can be found in the following URLs:

Deployed to Azure Blob Storage: pr-5967

If the changes are expected, update the current_golden_images_cache hash in the circleci config to accept the new images. Instructions are included in that file.
If the changes are unexpected, you can investigate the cause of the differences and update the code accordingly.

if (topOverlay?.type === 'modal') {
this.closeOverlay(topOverlay);
if (hasModalOrPageOverlay) {
const clickedBackdrop = composedPath.some(
Copy link
Contributor

@Rajdeepc Rajdeepc Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const composedPath = event.composedPath() is still executed at the top of the handler, the optimisation is effectively lost. Can we move it inside hasModalOrPageOverlay to get a performance win?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

event.composedPath() is already executed in handlePointerdown (line 168) and cached in this.pointerdownPath. So here it's just a property read, not a function call. I did do the optimization about avoiding iterating through the path array for backdrop detection.

Copy link
Contributor

@Rajdeepc Rajdeepc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice cleanup!
Non blocking: The HTMLDivElement check is fine for now but it would be great if you can add a short comment asserting that the backdrops are guaranteed to be <div>s and also if you can lock a test with this that would be great. Otherwise you can add a more semantic element + class check for future proofing this.

if (hasModalOrPageOverlay) {
const clickedBackdrop = composedPath.some(
(el) =>
el instanceof HTMLDivElement &&
Copy link
Contributor

@Rajdeepc Rajdeepc Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a little concern here: We are assuming that the backdrop will always be div and nothing else. This might not hold long term and will break the backdrop detection in future. Can you use a element + class check just to make sure we are detecting how its implemented rather than if its a backdrop or not?

 const clickedBackdrop = composedPath.some(
        (el) =>
            el &&
            (el as Element).nodeType === Node.ELEMENT_NODE &&
            (el as Element).classList?.contains('modal-backdrop')
    );

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I nerded out a little bit on this, ran some performance benchmarks and and here are my results:

Testing with backdrop at index 5 in a path of 12 elements
Running 100,000 iterations per approach

instanceof Element + hasAttribute       : 16.18ms
nodeType + hasAttribute                 : 19.15ms
Optional chaining                       : 10.02ms
instanceof Element + matches            : 15.50ms
instanceof HTMLDivElement + classList   : 9.41ms
tagName + hasAttribute                  : 15.39ms

--- Results (fastest to slowest) ---
1. instanceof HTMLDivElement + classList   : 9.41ms (100.0% of fastest)
2. Optional chaining                       : 10.02ms (93.9% of fastest)
3. tagName + hasAttribute                  : 15.39ms (61.1% of fastest)
4. instanceof Element + matches            : 15.50ms (60.7% of fastest)
5. instanceof Element + hasAttribute       : 16.18ms (58.2% of fastest)
6. nodeType + hasAttribute                 : 19.15ms (49.1% of fastest)

--- Testing worst case (backdrop NOT in path) ---
1. instanceof HTMLDivElement + classList   : 17.01ms (100.0% of fastest)
2. instanceof Element + hasAttribute       : 23.15ms (73.5% of fastest)
3. Optional chaining                       : 26.70ms (63.7% of fastest)
4. instanceof Element + matches            : 34.63ms (49.1% of fastest)
5. nodeType + hasAttribute                 : 38.58ms (44.1% of fastest)
6. tagName + hasAttribute                  : 42.94ms (39.6% of fastest)

So let's go for instanceof HTMLDivElement + classList! If the backdrop ever becomes other element other than a DIV, at least 6 tests fail, so we're covered on that front 😄

Copy link
Contributor

@Rajdeepc Rajdeepc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Thanks for bringing this in.

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

Labels

Status: Ready for review PR ready for review or re-review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants