question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

isFocusVisible is true when clicking label elements (e.g for Checkbox) inside elements with tabIndex

See original GitHub issue

🐛 Bug Report

Both useFocusRing and useFocusVisible report isFocusVisible as true when using a mouse to click a checkbox label element if the label has ancestor element with a tabIndex.

🤔 Expected Behavior

isFocusVisible should always be false when clicking with a mouse regardless of whether ancestors have a tabIndex.

😯 Current Behavior

If you click a label around a checkbox or radio input then isFocusVisible returns true. If you click the input element itself, the value is correctly false. If the label doesn’t have an ancestor with a tabIndex the value is correctly false.

This behaviour exists in Chrome and FIrefox but not Safari.

💁 Possible Solution

Sorry, I’m not sure why this happens, other than finding the cause is the tabIndex (even if it’s -1)

🔦 Context

The design of our app has a scrolling aside panel that can appear on the right of the screen. To scroll the main content of the page, instead of making the body scroll, I had to add scrolling to the main content element. To be able to scroll using the keyboard the main content needs to be focused (or contain a focusable element which is focused). If you click the main content and try to scroll using the keyboard, e.g. command + downarrow nothing happens. If I add a tabIndex={-1} to the main content then you can click and start scrolling. I also take advantage of main being focusable because I shift focus to main when you navigate in the SPA. Even without this, there are various other elements that use tabIndex to allow programmatic focus such as popovers. All radio and checkbox inside those elements are drawing keyboard focus when clicked with a mouse. We would prefer to not draw the focus unnecessarily.

💻 Code Sample

See Codesandbox reproduction using a Checkbox component:

https://codesandbox.io/s/serene-bird-51qhbv?file=/src/App.js:648-693

(Sorry, I didn’t use the template, I created the sandbox before I raised this issue)

🌍 Your Environment

Software Version(s)
react-aria 3.20.0
Browser Chrome 106.0.5249.119, FIrefox 106.0
Operating System MacOS 12.6

🧢 Your Company/Team

🕷 Tracking Issue (optional)

Issue Analytics

  • State:open
  • Created a year ago
  • Reactions:13
  • Comments:8 (3 by maintainers)

github_iconTop GitHub Comments

2reactions
ayip8commented, Nov 29, 2022

We’re encountering the same focus ring issue with radio buttons and checkboxes. In our case it’s happening when these components are placed inside a dialog. After finding this open issue I see that the useDialog hook is adding tabindex="-1" to the dialog container, which explains why this bug is being triggered for me.

Our implementation of radios/checkboxes/dialogs is pretty much straight from the React Aria docs, so I would expect this bug to be pretty common when placing these components inside a dialog. Just wanted to mention in case that helps with prioritization.

1reaction
aktanoffcommented, Dec 7, 2022

I’ve researched the reason why isFocusVisible becomes true, as @snowystinger said, problem is in handleFocusEvent method. When we have any container with tabIndex=‘-1’, focus event starts firing on this element, and hasEventBeforeFocus sets to false. After that focus event fires on checkbox/input/etc and we get into this if https://github.com/adobe/react-spectrum/blob/main/packages/%40react-aria/interactions/src/useFocusVisible.ts#L97, which sets modality to ‘virtual’

Maybe we should ignore tabIndex=‘-1’ elements here https://github.com/adobe/react-spectrum/blob/main/packages/%40react-aria/interactions/src/useFocusVisible.ts#L91?

Possible solution:

function isNegativeEventTargetTabIndex(e: Event) {
    if(!(e.target instanceof Element)) {
        return;
    }

    const tabIndex = e.target.getAttribute('tabIndex');

    if(tabIndex === null) {
        return;
    }

    const numberedTabIndex = parseInt(tabIndex);
    return isNaN(numberedTabIndex) ? undefined : numberedTabIndex < 0;
}

// Additional rule to ignore the event
(isNegativeEventTargetTabIndex(e) ?? false)
Read more comments on GitHub >

github_iconTop Results From Across the Web

Actions · adobe/react-spectrum
isFocusVisible is true when clicking label elements (e.g for Checkbox) inside elements with tabIndex PR comment #4226: Issue comment #3675 (comment) created ...
Read more >
tabIndex doesn't make a label focusable using Tab key
When a LABEL element receives focus, it passes the focus on to its associated ... but one focus on the input (in your...
Read more >
focus-visible - CSS: Cascading Style Sheets - MDN Web Docs
In this example, the :focus-visible selector uses the UA's behavior to determine when to match. Compare what happens when you click on the ......
Read more >
useCheckbox - React Aria - NET
If the checkbox does not have a visible label, an aria-label or aria-labelledby prop must be passed instead to identify the element to...
Read more >
useCheckbox – React Aria
Documentation for useCheckbox in the React Aria package. ... Subscribe Label Input ... useCheckbox returns props to be spread onto its input element: ......
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found