Repository navigation
[FEAT REQ] Open on focus only when the user moved focus there (:focus-visible), not when a script or the browser restores it #1287
Description
Problem to solve
The default openEvents include focus, so the tooltip opens whenever its anchor receives focus. That is right for keyboard users, but two other kinds of focus open it as well:
- Focus a script returns to the trigger. A menu or dialog that hands focus back to its trigger when it closes, as the WAI-ARIA menu button pattern asks, reopens the trigger's tooltip right after a mouse user picks an item. This is [BUG]: tooltip show again after closing the modal #1096, closed as not planned in 2023, where the advice was to stop opening on focus.
- Focus the browser restores on window return. If the anchor has focus when the user switches to another window or tab, the browser focuses it again when they come back, and the tooltip opens although the user did nothing. This happens even when that focus came from the keyboard, for example after closing a menu with Escape.
The available option, openEvents={{ focus: false }}, fixes both but removes the tooltip for keyboard users, which trades the bug for an accessibility gap.
Proposed solution
Open on focus only when the user moved focus to the anchor, the way Floating UI's useFocus does (visibleOnly, on by default):
- Open only if the anchor matches
:focus-visible. The browser already makes this call: keyboard focus matches, and focus a script moves after a mouse click does not. This covers case 1. - Skip the focus that follows a window
bluron the element that was focused at the time: recorddocument.activeElementonwindowblur, and ignore the next focus on that element. This covers case 2.
Both fit v6's design: one window blur listener next to the shared document listeners, not one per anchor, and a matches(':focus-visible') check in the existing focusin handler. Safari needs care: it can fail to match :focus-visible when focus comes from outside the document, which useFocus handles by tracking whether the last input was a key or a pointer. JSDOM does not match :focus-visible, so tests need a fallback.
This could be the default, or opt-in if a change in default behaviour is a concern.
Alternatives considered
openEvents={{ focus: false }}: removes keyboard tooltips.onFocus={(e) => e.target.blur()}, suggested in [BUG]: tooltip show again after closing the modal #1096: breaks keyboard focus on the trigger.disableTooltip(anchor): decides per anchor, not per focus, so it cannot tell a Tab from a restored focus.- A capture-phase
focuslistener onwindowthat stops propagation of an unrequested focus. This worked on v5, where anchors listen tofocus, but not on v6, which listens to delegatedfocusin. It depends on internals.
We're happy to send a PR if the approach sounds right.
Example usage
// Opt-in form, if the new behaviour should not be the default:
<Tooltip id="tip" openEvents={{ mouseover: true, focus: 'visible' }} />Additional context
Repro: https://codesandbox.io/s/nyy6rr (react-tooltip 6.0.9, React 19.2)
import { useRef, useState } from 'react';
import { Tooltip } from 'react-tooltip';
export default function App() {
const [open, setOpen] = useState(false);
const triggerRef = useRef<HTMLButtonElement>(null);
const close = () => {
setOpen(false);
// Return focus to the trigger once the menu has closed, as the menu button pattern asks.
setTimeout(() => triggerRef.current?.focus(), 150);
};
return (
<main style={{ padding: 80, display: 'flex', gap: 16 }}>
<button ref={triggerRef} data-tooltip-id="tip" data-tooltip-content="Select model" onClick={() => setOpen(true)}>
Model
</button>
{open && <button onClick={close}>Pick an item</button>}
<Tooltip id="tip" />
</main>
);
}- Click "Model", then click "Pick an item". Focus returns to "Model" and its tooltip opens, with the pointer elsewhere. On 6.0.9, returning focus in the same tick as the click happens not to reopen it; returning it 50 ms or more later does, which is what a menu with a closing animation does.
- Click "Model", move the pointer away, switch to another window, then switch back. The tooltip opens.
- Reload and press Tab. The tooltip opens, as it should; this is the behaviour to keep.
Case 1 seen on 6.0.9 and 5.30.0 in Chromium on Linux; case 2 seen on 5.30.0 in an Electron app, and v6's focusin handler has no window check either. Related: #1201.
Investigation and write-up prepared with AI assistance and reviewed before posting.
Checks
- I searched existing issues and did not find an open match.
Activity
danielbarion commentedon Oct 26, 2026
@gogakoreli can you please check the beta release from #1288 and let me know your thoughts?
gogakoreli commentedon Oct 28, 2026
Thanks @danielbarion! Tried the beta, it fixes both cases from this issue. I found two smaller problems and left the details on the PR: #1288 (comment)