skip to Main Content

Is there a way to apply a parent’s tabindex to all of it’s children?

Example code:

<div id="app">
  <div tabindex="2" class="parent-section">
    Section 2
    <a href="test.com" class="child-section bg-accent">test 2 a</a>
    <a href="test.com" class="child-section bg-accent">test 2 b</a>
    <a href="test.com" class="child-section bg-accent">test 2 c</a>
  </div>
  <div tabindex="1" class="parent-section flex-fill">
    Section 1
    <a href="test.com" class="child-section bg-neutral">test 1 a</a>
    <a href="test.com" class="child-section bg-neutral">test 1 b</a>
    <a href="test.com" class="child-section bg-neutral">test 1 c</a>
  </div>
  <div tabindex="3" class="parent-section">
    Section 3
    <a href="test.com" class="child-section bg-primary">test 3 a</a>
    <a href="test.com" class="child-section bg-primary">test 3 b</a>
    <a href="test.com" class="child-section bg-primary">test 3 c</a>
  </div>
</div>

What I want is to focus first on section 1 div, and then each of it’s selectable child elements WIHOUT SETTING A PARTICULAR TABINDEX ON EACH CHILD. The. it would tab to section 2, and so on.

I made a StackBlitz to demonstrate. https://stackblitz.com/edit/typescript-ypkxx3?file=index.html. You see you will tab to the section 1 div, section 2 div, and section 3 div first; and then it tabs through all the anchor elements.

This is a really basic example. I have an app with much more complex sections. Basically, I want to be able to set the tabindex of the section, and have it trickle down to all of it’s focusable child elements. These sections are large and dynamic, so it wouldn’t work if I have to add a particular tabindex on every single child element.

My actual application is an angular project.

2

Answers


  1. First off, non-interactive elements, such as a <div> should not receive keyboard focus. That will be confusing to both keyboard users, who might think they can hit ENTER on the element since it received focus, and it will be confusing for screen reader users because a role and label (accessible name) for the container won’t be announced.

    There is not a way to "inherit" the tabindex of a parent. You will have to set tabindex on all the children. You are allowed to have the same tabindex value for multiple elements. So your first container could have tabindex="1" for all the links and they will be processed first. When multiple elements have the same tabindex value, the browser will process them in the DOM order.

    If you don’t want to set the tabindex, then you’ll need a keyboard event handler, perhaps on the <body> element, to override the browser’s tab behavior. You would have to programmatically move the focus to the next element and then drain the event so that the browser doesn’t try to move the focus.

    Login or Signup to reply.
  2. DOM order is really important in accessibility. So we should instead keep the items in order in the DOM and then change the visual order with CSS.

    Note: 99% of the time "logical focus order" should go left to right (or RTL on RTL languages). I am assuming from the example given though that this is a general layout with 2 asides so it may be acceptable. Please take this into consideration though before you implement the following.

    There is also a second point here, and that is that tabindex should hardly ever be greater than 0.

    If it is that means those items will be focusable first (so if you put this half way down a page the first Tab into the page would land at tabindex="1" and be confusing.

    Finally by changing the tabindex to 0 it means we fixed your issue where the children elements are focused before the other parent elements as tabindex="0" doe not interfere with the natural focus order of the page.

    Using CSS to achieve your layout

    We can use CSS flex order property to achieve your layout / request.

    What we do is put the DOM order back to Section 1, Section 2, Section 3 and then use the order property to adjust the order that things are focused.

    Example

    #app {
      display: flex;
    }
    
    .parent-section {
      display: flex;
      flex-direction: column;
    }
    
    .flex-fill {
      flex: 1 1 auto;
    }
    
    .parent-section,
    .child-section {
      margin: 15px;
      padding: 15px;
      border: 1px solid black;
    }
    
    .bg-neutral {
      background-color: rgb(253, 180, 237);
    }
    .bg-accent {
      background-color: rgb(180, 253, 202);
    }
    .bg-primary {
      background-color: rgb(253, 219, 180);
    }
    
    /* I gave each section an ID but you can use unique classes or `:nth-child` if you prefer */
    #section1{
      order: 2;
    }
    #section2{
      order: 1;
    }
    #section3{
      order: 3;
    }
    <!-- notice that the sections are now in order within the DOM and I have changed the `tabindex` to 0 so it doesn't break DOM ordering -->
    <div id="app">
      <section class="parent-section flex-fill" id="section1" tabindex="0">
        Section 1
        <a href="test.com" class="child-section bg-neutral">test 1 a</a>
        <a href="test.com" class="child-section bg-neutral">test 1 b</a>
        <a href="test.com" class="child-section bg-neutral">test 1 c</a>
      </section>
      <aside class="parent-section" id="section2" tabindex="0">
        Section 2
        <a href="test.com" class="child-section bg-accent">test 2 a</a>
        <a href="test.com" class="child-section bg-accent">test 2 b</a>
        <a href="test.com" class="child-section bg-accent">test 2 c</a>
      </aside>
      <aside  class="parent-section" id="section3" tabindex="0">
        Section 3
        <a href="test.com" class="child-section bg-primary">test 3 a</a>
        <a href="test.com" class="child-section bg-primary">test 3 b</a>
        <a href="test.com" class="child-section bg-primary">test 3 c</a>
      </aside>
    </div>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search