# Mutating the active element
This document explains the browsers' behaviors when the active element is hidden, disabled or removed completely.
The "active element" refers to the DOM element that currently has focus. It is accessible through the DOM property document.activeElement
and can be queried via the CSS pseudo class :focus
.
The table of what browsers consider focusable shows that <input disabled>
cannot receive focus. But what the table does not show is how browsers react when the <input>
element has focus and is then disabled, or hidden, or even completely removed from the DOM.
# Specifications
The section Focus fixup rule one was introduced in February 2014 and made a little clearer in November 2016. It describes that focus is returned to <body>
when the active element loses its ability to receive focus.
The specification paints a different picture than browsers have implemented, as we'll see below. Thanks go to Domenic Denicola for his efforts to rectify the status quo by improving the spec, writing platform tests and filing browser bugs.
# Running tests
On macOS the browsers Safari and Firefox will not focus <button>
and <input type="radio|checkbox">
elements when they're clicked, but will shift focus to them when using the Tab key. These tests therefore show the result of keyboard interaction.
play with Mutating the active element on jsbin.com or open the document of Mutating the active element
# The results
Executing the test above in various browsers brings about the compatibility tables below. The results are fairly uniform, but there are a few interesting outliers in behavior that are worth discussing.
Regardless of the mutation method (disable, hide, remove) the Gecko, Blink and Webkit (Firefox, Chrome, Safari) always shift focus to the next focusable element in the document's sequential navigation focus order when the Tab key is pressed. This is obvious where the mutated element remains the activeElement. But any time the active element reverts to <body>
, this is achieved because the browser sets the sequential focus navigation starting point appropriately.
When removing the active element from the DOM (also called "detaching the element") browsers return focus to <body>
, while disabling, hiding or interting (removing the tabindex attribute from otherwise non-focusable elements) the active element it usually retains focus.
Internet Explorer and Microsoft Edge shift focus to the first focusable ancestor or <body>
when the active element is disabled.
Internet Explorer 9 doesn't cope well when the active element is removed, as it simply sets document.activeElement
to null
, instead of shifting focus back to <body>
. Internet Explorer 11 may still reference the element that was removed from the DOM, instead of shifting focus back to <body>
. Both scenarios can cause havoc in scripts accessing document.activeElement
without performing sanity checks. The ally.get.activeElement
utility is there to help with this.
# Disabling the active element
When the active element changes from <button>
to <button disabled>
:
Browser | active after disabling | active after Tab |
---|---|---|
Safari 10 | unchanged | next focusable element |
Firefox 49 | unchanged | next focusable element |
Chrome 53 | <body> |
next focusable element |
Internet Explorer 9 | #container |
first focusable element in #container |
Internet Explorer 11 | #container |
first focusable element in #container |
Edge 14 | #container |
first focusable element in #container |
# Hiding the active element
When the active element changes from <button>
to <button hidden>
:
Browser | active after hiding | active after Tab |
---|---|---|
Safari 10 | unchanged | next focusable element |
Firefox 49 | unchanged | next focusable element |
Chrome 53 | <body> |
next focusable element |
Internet Explorer 9 | unchanged | next focusable element |
Internet Explorer 11 | unchanged | next focusable element |
Edge 14 | unchanged | next focusable element |
# Removing the active element
When the active element is removed from the DOM:
Browser | active after removing | active after Tab |
---|---|---|
Safari 10 | <body> |
next focusable element |
Firefox 49 | <body> |
next focusable element |
Chrome 53 | <body> |
next focusable element |
Internet Explorer 9 | null |
first focusable element of the document |
Internet Explorer 11 | unchanged [1] or <body> |
first focusable element of the document |
Edge 14 | <body> |
first focusable element of the document |
[1] document.activeElement still points to the detached element, but when reattaching the element it does not match :focus
anymore.
# Removing tabindex from the active element
When the active element changes from <div role="button" tabindex="0">
to <div role="button">
:
Browser | active after removing tabindex | active after Tab |
---|---|---|
Safari 10 | unchanged | next focusable element |
Firefox 49 | unchanged | next focusable element |
Chrome 53 | <body> |
next focusable element |
Internet Explorer 11 | unchanged | next focusable element |
Edge 14 | unchanged | next focusable element |