Dynamic focus

Associated themes:
  • Component

Publication date

Update of the article from

Introduction

Managing focus in rich applications is complex. Several cases occur:

  • Appearance/disappearance of content generated by a user action with a page reload
  • Appearance/disappearance of dynamic content generated by a user action without a page reload
  • Special case: dynamic iframes

Content generated by a user action with a full page reload

This case covers situations where a user action triggers a full page reload to modify part of the content. While this may seem simple, this approach has several drawbacks, including loss of context and a less fluid experience.

If there is no way to use asynchronous loading, you should at least:

  1. Inform the user about the page reload with a message such as “Loading, please wait” (for example, you can move focus to the message, use role="alert", aria-busy, or aria-live...) to avoid loss of context.
  2. On reload, place focus on the generated content or, when content disappears, on the triggering element (if it still exists after the reload) or set focus just before the content that was removed.
  3. Ensure the focus order remains logical and sequential after the content appears or disappears.

Dynamic content generated by a user action without page reload (asynchronous)

This is the most common case: only the DOM is modified by adding or removing content during a user action without reloading the entire page (for example, displaying search results, opening a modal).

Handling content addition

  1. Inform the user that content is appearing (for example, for a submenu to expand, you can add text like “expand” and set the aria-expanded property to "true"). If this content may disappear (a pop-in, for example), remember the element that triggered the appearance to restore focus to it when the content disappears.
  2. Best practice: insert the new content immediately after the trigger element in the source code (e.g., dropdown menu),
  3. Move focus to a relevant element within the new content (the container, a heading, a link, a button, etc.).

Handling content removal

  1. If necessary, inform the user before they trigger this removal,
  2. Return focus to the triggering element or, if that is not possible, just before the removed content. For example, when closing a dialog, focus should be returned to the element that triggered the opening (button).

In all cases, ensure the focus order remains logical and sequential after content appears/disappears.

Notifying the user about content changes

This is a precaution to take for all users, and particularly for people with visual, cognitive, or attention impairments.

The user must be able to detect the appearance or disappearance of content and interact with it if needed.
Therefore, the user should be notified, and there are several solutions for this:

  • Provide explanatory text,
  • Manage focus movement,
  • Use ARIA roles.

See the dropdown menu example for how to use the aria-expanded attribute.

The tabindex attribute

As much as possible, avoid redefining the reading order by using tabindex attributes.

If the element you move focus to is not an interactive element (heading, list, text, etc.), you will need to use one of these two methods:

  • tabindex="-1": to prevent an element from receiving focus via the tab key while allowing it to receive focus via JavaScript (JavaScript focus() method).
  • tabindex="0": to make a natively non-focusable element focusable via the tab key and via JavaScript.

Special case of a dynamic update to an iframe

When an iframe is dynamically updated by a user action, you should:

  • Update the title attribute to indicate the change to the user.
  • If needed (the user must have access to the updated iframe immediately after the action that caused the update), set focus to the iframe element.
  • Ensure the focus order remains logical and sequential both on the page and inside the iframe.