Overview
Purpose: Tracks a component's focus state so that it can render a focus indicator (e.g., a glowing outline) if and only if the user has used the keyboard to interact with the component. This is modeled after the proposed focus-visible feature for CSS.
This mixin works at the beginning of the Elix render pipeline:
events ➞ methods → setState → render DOM → post-render
Expects the component to provide:
setState
method compatible withReactiveMixin
's setState.
Provides the component with:
[internal.state].focusVisible
member that is true if the element should render focus.
Usage
import FocusVisibleMixin from "elix/src/base/FocusVisibleMixin.js";
class MyElement extends FocusVisibleMixin(HTMLElement) {}
FocusVisibleMixin
is appropriate for all components that can receive the keyboard focus but only want to show the focus indicator when the keyboard is actually being used. This is particularly helpful on mobile devices such as phones, where most users don't need or want to see the keyboard focus.
See also KeyboardMixin, which can be used to handle keyboard events.
Example
Heuristic
This mixin sets [internal.state].focusVisible
if the document has received a keydown
event since the last mousedown
event (which includes taps).
Example: if the user presses the Tab key to move to a ListBox element, the ListBox
shows a focus indicator. If the user then clicks an item with the mouse, the focus indicator will disappear. If the user then presses an arrow key to move the selection, the ListBox
will show a focus indicator once again.
Note: A component can't actually force a focus indicator to appear when it wants, but can only suppress the focus indicator when it doesn't want one. In the above example, when we say that the ListBox
"shows a focus indicator, it's more precise to say that it "stops telling the browser it doesn't want a focus indicator". The difference is that the browser may have additional heuristics for deciding when to show a focus indicator or not. There may be cases where the component says it's okay letting a focus indicator appear, but the browser may decide for its own reasons not to render one.
FocusVisibleMixin
also tracks focus visibility across changes in window focus. If an element has [internal.state].focusVisible
set to true (i.e., it should show focus state) and the window loses focus, then [internal.state].focusVisible
will be set to false. If the window later regains focus, then the browser will restore focus to the previously-focused element, and [internal.state].focusVisible
will be set to true again.
To see this, use the keyboard to move the focus to one of the buttons in the demo above. Then tab away from that window. The button should drop its visible focus. Then tab back to the window. The button should once again show its visible focus.
API
Used by classes AutoCompleteComboBox, AutoCompleteComboBox, AutoCompleteInput, CalendarMonthNavigator, Carousel, CarouselSlideshow, CarouselWithThumbnails, ComboBox, ComboBox, DateComboBox, DateComboBox, DateInput, DropdownList, FilterComboBox, FilterComboBox, FilterListBox, HamburgerMenuButton, Input, ListBox, ListComboBox, ListComboBox, ListWithSearch, MenuButton, MultiSelectListBox, NumberSpinBox, OptionList, PlainAutoCompleteComboBox, PlainAutoCompleteComboBox, PlainAutoCompleteInput, PlainCalendarMonthNavigator, PlainCarousel, PlainCarouselSlideshow, PlainCarouselWithThumbnails, PlainComboBox, PlainComboBox, PlainDateComboBox, PlainDateComboBox, PlainDateInput, PlainDropdownList, PlainFilterComboBox, PlainFilterComboBox, PlainFilterListBox, PlainHamburgerMenuButton, PlainInput, PlainListBox, PlainListComboBox, PlainListComboBox, PlainListWithSearch, PlainMenuButton, PlainMultiSelectListBox, PlainNumberSpinBox, PlainOptionList, PlainPopupButton, PlainPopupSource, PlainSlideshowWithPlayControls, PlainSlidingPages, PlainSpinBox, PlainTabButton, PopupButton, PopupSource, SlideshowWithPlayControls, SlidingPages, SpinBox, TabButton, TooltipButton, and TooltipButton.