Search code examples
javascripthtmlcssaccessibility

how to prevent entering interactive elements by press tab?


While pressing tab on the page, when tab on one interactive element(user defined), it automatically enter into it, and the tab navigating on the internal subelemnet of the interactive element

how to prevent this?

I hope we can use enter/esc to get in/out this interactive element, not directly get into it while tab on it.

moer details: I try to clear my question: Suppose my page have two elements like one button and one excel render view, I can use tab to focus on both of them, however, the view of excel is an interactive element, which means when I tab on the view of excel, the following tab will move in the cells of the excel I hope I can only tab between the button and the view of the excel, only if I use the enter key on the view of the excel, then let it tab between cells of the excel, and hopefully I can use ESC key to leave out from the view the excel


Solution

  • If the user component allows exiting by means of tab at the end of the table, you are not failing WCAG 2.1.2 No Keyboard Trap.

    That being said, and as you already have realised, this is not a great user experience.

    If you don’t have any control on that component, this is something you could try:

    • Make the wrapper of the component focussable by means of tabindex="0"
    • Give it an accessible name by means of aria-label or aria-labelledby
    • Give it an active/inactive status, which controls whether tab will navigate inside of it, or to the next component
    • Listen to Enter and Escape to toggle this status
    • Listen to Tab and the focusin event, to avoid navigation inside when it’s not active (you might skip listening on Tab, as the result would be another focusin)
    • Don’t forget to explain to the user (including screen reader users) how to navigate the thing

    c = document.getElementById('component');
    next = document.getElementById('next-tabstop');
    
    c.addEventListener('keydown', e => {
      switch (e.code) {
        case "Tab":
          // Tab to the next component if not active
          if (c.dataset.active !== "true") {
            next.focus();
            e.preventDefault();
          }
          break;
    
        case "Escape":
          // Exit tab of the table
          c.dataset.active="false";
          c.focus();
          break;
    
        case "Enter":
          // Tab into the table
          c.dataset.active="true";
          c.querySelector("[tabindex='0']").focus();
          break;
      }
    });
    
    // Avoid backwards navigation directly onto the last focusable descendend
    c.addEventListener('focusin', e => {
      if (c.dataset.active !== "true") {
        c.focus();
      }
    });
    table {
      border-collapse: collapse;
    }
    
    td,
    th {
      padding: .5em;
      border: 1px solid grey;
    }
    
    :focus {
      outline: 2px solid orange;
    }
    <p><button>A humble button</button></p>
    
    <h2 id="component-title">Table</h2>
    <p id="component-desc">Press <kbd>Enter</kbd> to navigate into the table, <kbd>Escape</kbd> to exit the table</p>
    
    <div tabindex="0" aria-labelledby="component-title" aria-describedby="component-desc" id="component" data-active="false">
      <table>
        <thead>
          <tr>
            <th tabindex="0"></th>
            <th tabindex="0">A</th>
            <th tabindex="0">B</th>
            <th tabindex="0">C</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <th tabindex="0">1</th>
            <td tabindex="0"></td>
            <td tabindex="0"></td>
            <td tabindex="0"></td>
          </tr>
          <tr>
            <th tabindex="0">2</th>
            <td tabindex="0"></td>
            <td tabindex="0"></td>
            <td tabindex="0"></td>
          </tr>
          <tr>
            <th tabindex="0">3</th>
            <td tabindex="0"></td>
            <td tabindex="0"></td>
            <td tabindex="0"></td>
          </tr>
        </tbody>
      </table>
    </div>
    
    <p><button id="next-tabstop">Let’s move on</button></p>

    To have an accessible page, the table component needs to be accessible, as well, and there are more guidelines to follow.

    One would expect to navigate grids by means of arrow keys, and also Home and End should work with certain modifiers. See, for example, ARIA Practice of Keyboard Navigation for Data Grids

    If inside these cells there are widgets again, it’s going to get complicated, because then keyboard control of the table itself becomes difficult. You can refer to the ARIA Grid Widget Role.