Menü schliessen
Created: September 27th 2024
Last updated: November 7th 2024
Categories: Common Web Development,  CSS,  JavaScript Development
Author: Tim Fürer

HTML: How to Prevent User Interaction with Inert and Alternatives

Tags:  CSS,  guide,  HTML,  Javascript
Donation Section: Background
Monero Badge: QR-Code
Monero Badge: Logo Icon Donate with Monero Badge: Logo Text
82uymVXLkvVbB4c4JpTd1tYm1yj1cKPKR2wqmw3XF8YXKTmY7JrTriP4pVwp2EJYBnCFdXhLq4zfFA6ic7VAWCFX5wfQbCC

The inert HTML attribute, when applied to an element, allows us to prevent user interaction with the element as well as its children. Specifically the following types of interaction are prevented:

  • Clicking
  • Focusing (for example, through tabbing navigation)
  • Selection
  • Editing (for elements that are editable, such as inputs or textareas)

Furthermore, inert elements are hidden from assistive technologies. This makes the attribute a convenient and effective method for preventing user interaction whilst retaining accessibility.


Application

Sections

A basic application of the inert attribute is for preventing interaction with a section containing interactive elements, essentially disabling the section.

<div inert>
  <button onclick="alert('This text will never be alerted')">
    You can't click me
  </button>
</div>

Dialogs and Modals

This basic usage of inert can be extended in combination with dialogs and modals where inert is used to disable elements in the background. This prevents the user from accidentally triggering something outside of the dialog or modal and assures that the focus remains contained.

<body>
  <main id="page-content" inert>
    Some interesting content

    <button onclick="alert('This text will never be alerted')">
      You can't click me
    </button>
  </main>

  <dialog open>
    Hello World!
  </dialog>
</body>

The inert attribute can be toggled through JavaScript using the inert property of an element.

element.inert = true; // enables inert on element

element.inert = false; // disables inert on element

 


Availability and alternatives

Inert is baseline available since April 2023. According to caniuse, over 90% of browsers in usage support it. However, if this support does not suffice for your needs, there are of course traditional alternatives that can be used over inert.

Preventing Clicking (Preventing Mouse Interaction)

Applying the CSS property pointer-events with a value of none to an element will prevent users from interacting with it through the mouse.

#prevent-mouse {
  pointer-events: none;
}

Preventing Selection

Applying the CSS property user-select with a value of none to an element will prevent users from selecting any contained text.

#prevent-selection {
  user-select: none;
}

Preventing Focus (Focus Trapping)

This one's more tricky to achieve without inert and requires us to do some manual focus management using JavaScript. The idea is that we have a range of elements within which we want to trap our focus. If the focus leaves the range, we force it back into it, ideally creating a "wrapping" effect. "wrapping" effect means that if the focus goes beyond the last element of the range, the focus is reset to the first element and vice-versa.

The following function takes a wrapper element and traps the focus within its focusable elements. The function returns another function that, if called, sets the focus free again (untraps it).

function trap_focus(wrapper) {
  const focusable_range = Array.from(wrapper.querySelectorAll(
    'a[href], button, input, textarea, select, details, [tabindex]:not([tabindex="-1"])'
  ));

  const first_element = focusable_range[0];

  const last_element = focusable_range[focusable_range.length - 1];

  const handle_trap = e => {
    if (e.key !== 'Tab') {
      return;
    }

    if (e.shiftKey) {
      if (document.activeElement === first_element) {
        last_element.focus();

        e.preventDefault();

        return;
      }
    }
    else if (document.activeElement === last_element) {
      first_element.focus();

      e.preventDefault();

      return;
    }

    if (!focusable_range.includes(document.activeElement)) {
      first_element.focus();

      e.preventDefault();
    }
  }

  document.body.addEventListener('keydown', handle_trap);

  return () => {
    document.body.removeEventListener('keydown', handle_trap);
  };
}

Use it like so:

const untrap_wrapper_element = trap_focus(wrapper_element); // traps focus

untrap_wrapper_element(); // untraps focus