Skip to content

Focus trap broken when first or last focusable element is hidden #35

@voidloaf

Description

@voidloaf

Issue
The focus layer keyboard trap can be broken if the first or last focusable element is hidden and therefore not considered tabbable/focusable by the browser. This is a result of the tree walker filtering elements with a tabindex, but not considering whether those elements may have display: none; or visiblity:hidden; applied.

A real-world scenario where this breaks is a modal with any kind of collapsed menu contained within it, such as a menu in a video player.

Test Case
Here is a working test case of the bug with repro steps: https://vigilant-cacti.surge.sh/.
My fork of this repository contains the source for the repro example.

Proposed Fix
Adding additional checks to the tree walker filter for height and visibility in src/util/wrapFocus.tsx to cover situations where an element has tabindex and is not disabled, but might be un-focusable due to display: none; or visiblity:hidden;.

function createFocusWalker(root: HTMLElement) {
  return document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT, {
    acceptNode: (node: HTMLElement) =>
NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP,
      node.tabIndex >= 0 && !node.disabled && node.getBoundingClientRect().height > 0 && window.getComputedStyle(node).visibility === 'visible' ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP,
  });
}

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