On this page
Laptop and cactus on the left side and coffee on the right side. Image by Megan Rexazin Conde from Pixabay.

Explore focusable, clickable, tabbable or active states

Explore focusable, clickable, and active states for accessibility, and determine the focusable state using JavaScript.

Determining whether an element is interactive or not can be crucial for various purposes, such as improving accessibility, enhancing user experience, testing purpose or debugging issues.

An interactive element is one that can receive focus, be clicked, be active, or be tabbable, allowing users to engage with it in some way. Let’s find out how to determine if an element is focusable or active using JavaScript.

What is the difference between an interactive element that can receive focus, be clicked, be active, or be tabbable?

The terms focusable, clickable, active, and tabbable refer to different aspects of an element’s interactivity. Here’s a breakdown of each:

  • Focusable: an element is focusable if it can receive keyboard focus, meaning it can be selected and highlighted using the keyboard (usually by pressing the Tab key). Focusable elements typically have a visible outline, border or shadow when focused.
  • Clickable: an element is clickable if it can be activated by a mouse click or a touch event. This usually triggers an event handler or executes a specific action.
  • Active: an element is active when it is being interacted with, such as when a user is tabbing, clicking, or dragging on it. The active state is usually indicated by a visual change, like a changed background color, border style or shadow.
  • Tabbable: an element is tabbable if it can be reached by pressing the Tab key, allowing users to navigate to it using their keyboard. Tabbable elements are usually focusable, but not all focusable elements are necessarily tabbable. For example, element with a negative tabindex="-1" is removed from the default tabbing order, while still allowing it to be focused programmatically using JavaScript.

Determine if an element is clickable

To determine if an element is clickable, you can check several properties. However, keep in mind that this is not an exhaustive list, and there might be other factors that affect an element’s clickability. However, this should cover most common cases.

Determine if an element is clickable
function isElementClickable(element) {
  if (element === null || typeof element === 'undefined' || element.nodeType !== Node.ELEMENT_NODE) {
    return false;
  }

  const styles = window.getComputedStyle(element);

  if (
    element.disabled ||
    element.ariaDisabled === 'true' ||
    element.tabIndex < 0 ||
    styles.pointerEvents === 'none' ||
    styles.display === 'none' ||
    ['hidden', 'collapse'].includes(styles.visibility) ||
    styles.opacity === '0' ||
    styles.cursor === 'not-allowed' ||
    styles.cursor === 'none'
  ) {
    return false;
  }

  // Additional check for overlapping elements (simplified)
  // This is a basic check; more sophisticated checks might be needed
  const rect = element.getBoundingClientRect();
  const elementsFromPoint = document.elementsFromPoint(
    rect.left + rect.width / 2,
    rect.top + rect.height / 2,
  );

  if (elementsFromPoint.includes(element) === false) {
    return false; // Another element is on top
  }

  return true;
}

Determine if an element is focusable

Determining if an element is focusable can be done on two levels:

  1. Level 1: Determining if an element is a type of focusable element.
  2. Level 2: Determining if the element can receive focus right now.

Determining if an element is a type of focusable element

Determine if an element is a type of focusable element
const FOCUSABLE_ELEMENTS_CSS_SELECTOR = [
  '[tabindex]',
  '[contenteditable=""]',
  '[contenteditable="true"]',
  'a[href]',
  'area[href]',
  'audio',
  'button',
  'embed',
  'form',
  'iframe',
  'input:not([type="hidden"])',
  'label',
  'link',
  'object',
  'select',
  'summary',
  'textarea',
  'video'
];

function isFocusableType(element) {
  const focusableElementsSelector = FOCUSABLE_ELEMENTS_CSS_SELECTOR.join(', ');
  return element.matches(focusableElementsSelector);
}

Determining if the element can receive focus right now

Determine if an element can receive focus now
function canReceiveFocus(element) {
  let originalFocus = document.activeElement;

  if (originalFocus !== null) {
    while (originalFocus.shadowRoot?.activeElement) {
      originalFocus = originalFocus.shadowRoot.activeElement;
    }
  }

  let result = false;

  if (typeof element.focus === 'function') {
    try {
      element.focus({
        preventScroll: true
      });
      result = element === document.activeElement;

      if (originalFocus) {
        originalFocus.focus({
          preventScroll: true
        });
      }
    } catch (_error) {
      // Handle scenario when method "focus" throws an exception
    }
  }

  return result;
}

The function covers the following cases:

  • It attempts to focus the element: by trying to call the focus method on the element, we’re checking if the element can actually receive focus. This approach is more reliable than just checking the element’s properties or attributes.
  • It handles exceptions: if the focus method throws an exception, the function catches it and returns false, indicating that the element cannot receive focus.
  • It restores the original focus: after attempting to focus the element, the function restores the original focus to the previous active element, which is a good practice to avoid disrupting the user’s interaction with the page.
  • It checks for shadow DOM: the function also checks if the original focus is within a shadow DOM, which is an important consideration when working with web components.

Determine which element is active now

To determine which element is active now, you can use the document.activeElement property. This property returns the currently active element in the document, which is the element that has focus.

Determine element that currently has focus
const activeElement = document.activeElement;
console.log(activeElement);

Note that document.activeElement can return null if no element is currently active, such as when the document is first loaded or when the user has not interacted with the page yet.

Also, if you’re working with shadow DOM, you may need to recursively check the activeElement property of the shadow root to find the active element within the shadow DOM.

Here’s an example of how to recursively check the activeElement property of the shadow root:

Determine element that currently has focus in the Shadow DOM
function getActiveElement(element) {
  if (element === null || document.hasFocus() === false) {
    return null;
  }

  if (element.shadowRoot && element.shadowRoot.activeElement) {
    return getActiveElement(element.shadowRoot.activeElement);
  }

  return element;
}

const activeElement = getActiveElement(document.activeElement);
console.log(activeElement);

This function recursively checks the activeElement property of the shadow root until it finds the active element within the shadow DOM.

The document.hasFocus() used above is a method that returns a boolean value indicating whether the document (or any element within it) currently has focus. In other words, it checks if the user is currently interacting with the document, either by clicking on an element, typing in a form field, or navigating through the document using the keyboard.

Related posts

Comments

Leave a Reply

Search in sitelint.com

Elevate your website’s accessibility with SiteLint

Your reliable source for inclusive online experiences. Our cutting-edge tools and services enable businesses to create user-friendly web environments. Join the digital inclusivity movement to discover new ways to engage and grow. Discover the SiteLint platform today and transform your online presence into a beacon of accessibility.

Real-user monitoring for Accessibility, Performance, Security, SEO & Errors (SiteLint)