Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Tooltip] Tooltip is opened on active buttons when the browser tab is switched #1800

Open
gdorsi opened this issue Nov 23, 2022 · 6 comments

Comments

@gdorsi
Copy link

gdorsi commented Nov 23, 2022

Bug report

Current Behavior

Steps:

This happens also when the browser window is minimized/maximized.

Expected behavior

The tooltip shouldn't be open when the tab becomes active again

Reproducible example

https://www.radix-ui.com/docs/primitives/components/tooltip

Suggested solution

The issue is triggered by the onFocus handler because the browser triggers a focus event when the page becomes visible.

I've applied this workaround on my tooltips:

  const prevActiveElement = useRef<EventTarget | null>(null);

  useEffect(() => {
    const handleFocus = (evt: Event) => {
      const { target } = evt;

      prevActiveElement.current = target;
    };

    window.addEventListener('focusin', handleFocus);

    return () => {
      window.removeEventListener('focusin', handleFocus);
    };
  }, []);

  return (
    <Tooltip.Root>
      <Tooltip.Trigger
        onFocus={(evt) => {
          // Blocks the tooltip open during the page visibility change
          // based on the assumption that we want to show the tooltip only when the focus
          // is triggered on a new element
          if (prevActiveElement.current === evt.target) {
            evt.preventDefault();
          }
        }}

To make the example small I've placed everything in the component, but it's better to track the previous active element inside a provider.

I'm migrating from tippy.js and while they handle focus they don't have the same issue.
I would also check there to find a possible solution.

Your environment

Software Name(s) Version
Radix Package(s) @radix-ui/react-tooltip 1.0.2
React n/a 18.2
Browser Chrome 105
Assistive tech n/a
Node n/a
npm/yarn
Operating System Linux & MacOS
@itsmingjie
Copy link

Reproducing and experiencing the same issue.

@ctaylr13
Copy link

ctaylr13 commented Jan 3, 2023

are you getting a memory leak warning for this?

@sfrieson
Copy link

sfrieson commented Jan 3, 2023

There are some other similar cases where this experience happens.

From looking at the code, it's anytime the trigger regains focus but the pointer is not down.

Another example case would be closing a dialog or other modal experience with the escape button. This returns focus to the trigger, but the pointer is not pressed. Alternatively, a click event only fires after the pointer up, so any button click in a dialog that closes the dialog and returns the focus will likely have this same issue.

@gdorsi
Copy link
Author

gdorsi commented Jan 10, 2023

are you getting a memory leak warning for this?

Nope

@gdorsi
Copy link
Author

gdorsi commented Jan 10, 2023

There are some other similar cases where this experience happens.

From looking at the code, it's anytime the trigger regains focus but the pointer is not down.

Another example case would be closing a dialog or other modal experience with the escape button. This returns focus to the trigger, but the pointer is not pressed. Alternatively, a click event only fires after the pointer up, so any button click in a dialog that closes the dialog and returns the focus will likely have this same issue.

Seems a different issue to me.

The one I've reported is caused by the browsers behavior of triggering a focus event when the tab/window goes back to the active state.

@sanbornhilland
Copy link

sanbornhilland commented Jun 22, 2023

There are some other similar cases where this experience happens.

From looking at the code, it's anytime the trigger regains focus but the pointer is not down.

Another example case would be closing a dialog or other modal experience with the escape button. This returns focus to the trigger, but the pointer is not pressed. Alternatively, a click event only fires after the pointer up, so any button click in a dialog that closes the dialog and returns the focus will likely have this same issue.

We've experienced this behaviour as well. We commonly have Icon Buttons with Tooltips that activate Dialogs. Once the Dialog is closed, the Tooltip is shown because focus is returned back to the Icon Button. It strikes me that this is not ideal behaviour. It's a bit confusing and annoying as a user, but I will also admit I'm not intimately familiar with all the accessibility guidelines for Tooltips. Perhaps this is intended behaviour?

If it's not intended behaviour it would be useful to get a fix for it because I expect it's not uncommon to use these components together so it seems like a common issue users will run into.

As I currently understand it, it seems plausible that the issues @gdorsi and @sfrieson have described are the same root cause.

What if there was an extra check added here that checked if the target of the tooltip has focus-visible rather than focus? I'm not actually sure if there's a direct way to query focus-visible for element in javascript. I don't believe there is a focus-visible (reference: https://w3c.github.io/uievents/#event-type-focus) but you could perhaps do something along the lines of:

onFocus={composeEventHandlers(props.onFocus, (event) => {
    if (!isPointerDownRef.current && document.querySelector("*:focus-visible") === event.currentTarget) context.onOpen();
})}

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

No branches or pull requests

6 participants