Skip to content

ally.js

JavaScript library to help modern web applications with accessibility concerns by making accessibility simpler

# 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.

Mutating the active element on jsbin.com

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