Skip to content

Click events not firing when DOM manipulation occurs in useButton hook's onPress handler, even with capture phase #6454

Closed
@steveoh-rose

Description

@steveoh-rose

Provide a general summary of the issue here

If DOM manipulation occurs during the onPress handler in the useButton hook, click events fail to fire, even when event listeners are set to the capture phase.

Screen.Recording.2024-05-27.at.4.24.24.pm.mov

Event listener used

  window.addEventListener("click", () => console.log("clicked"), true);

🤔 Expected Behavior?

Click events should fire reliably when a button is clicked, even if DOM manipulation occurs during the onPress handler. Event listeners, especially those set in the capture phase, should detect these events without issue.

😯 Current Behavior

When DOM manipulation occurs within the onPress handler in the useButton hook, click events fail to fire. This issue persists even when event listeners are set to the capture phase. Here is a link to the code sandbox

We first recognised this issue in Google Tag Manager (GTM), where we observed that events on buttons removed from the DOM by the onPress handler were not being picked up. This led to inconsistencies in our event tracking and analytics

Error: No specific error message, but click events are not registered by Google Tag Manager (GTM) or Console Logs. The event loop isn't complete and therefore all listeners don't detect click. This appears to only be an issue with "click" events.

💁 Possible Solution

The problem can be mitigated by deferring the execution of the onPress handler. This can be done using setTimeout with a delay of 0 or using Promise.resolve().then(). This ensures that the event loop processes before DOM manipulation interferes with event handling.

Code Example:

onPress: (e) => {
  e.continuePropagation();
  setTimeout(() => {
    if (onPress) {
      onPress(e);
    }
  }, 0);
}

However, this is a hack. I believe this should be fixed within Adobe Spectrum.

🔦 Context

This issue impacts the reliability of event tracking using GTM on our buttons. When a button triggers a DOM manipulation that removes it, the associated click event is not captured, leading to inconsistencies in our analytics and tracking.

Screen.Recording.2024-05-27.at.4.24.24.pm.mov

🖥️ Steps to Reproduce

Code Sandbox Link

Code Sandbox Link

  1. Create a button using the useButton hook with an onPress handler that manipulates the DOM (e.g., removing the button).
  2. Add a click event listener in the capture phase to the window.
  3. Click the button and observe that the click event is not fired.
const ButtonTest = (args) => {
  const [hasButton, setHasButton] = React.useState(true);

  window.addEventListener('click', () => console.log('clicked'), true);

  const removeButton = () => {
    setHasButton((prev) => !prev);
  };

  return (
    hasButton && (
      <Button {...args} onPress={removeButton}>
        {args.children}
      </Button>
    )
  );
};

For this Sandbox, open the console and see that the button with no DOM manipulation has a console.log fired, but the one with doesn't.

Version

"react-aria": "^3.28.0",

What browsers are you seeing the problem on?

Chrome, Safari, Microsoft Edge

If other, please specify.

No response

What operating system are you using?

MacOS

🧢 Your Company/Team

No response

🕷 Tracking Issue

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions