39

I have a div tag with some content getting loaded inside it. The content inside can have buttons, anchor elements, etc. which are focusable. I do not have control over the content but I can modify the 'div' tag attributes.

My problem is the focus still goes to the content (anchor, buttons, etc.) even if I specify the tabIndex -1 to the div tag.

<!-- HTML content here -->
<div tabindex="-1" id="externalContent">
  <div>
    ...
    <button>click me</button> <!-- Focus shouldn't come here -->
  </div>
</div>
<!-- HTML content here -->

Is there a way to skip the entire content while tabbing ? It's certainly not working with the above code.

Adarsh Konchady
  • 2,577
  • 5
  • 30
  • 50
  • 1
    You can use tabindex="-1".... Watch out though that this is a HTML5 feature and might not work with old browsers. http://stackoverflow.com/questions/5192859/how-to-ignore-html-element-from-tabindex – Nomistake Mar 13 '17 at 12:15
  • maybe you can use jquery to get tabindex="-1" to all possible elements inside the div... – Nomistake Mar 13 '17 at 12:17
  • Why do you want to prevent focus going to the content? If the content is actually interactive - ie if you can click the buttons/links with the mouse - you should generally let them take keyboard focus too. – BrendanMcK Mar 13 '17 at 22:15
  • The content inside the 'externalContent' here is partially visible on page load and hence for a11y, I need to skip it from the tab order. I do not want to append tabindex=-1 for every child since I need to reverse it once the content is fully visible. – Adarsh Konchady Mar 14 '17 at 17:30
  • 1
    A common usecase: show a dialog on webpage. – tsh Apr 22 '20 at 05:51

6 Answers6

40

Not sure why nobody has mentioned visibility: hidden yet. Setting display: none can in some cases mess up logic when dealing with dimensions of non-visual elements. visibility will persist the dimensions just like opacity: 0 would do, but also disable any tabbable children.

Example:

<div style="visibility: hidden;">
    <a href="#">I'm only tabbable if my parent is visible!</a>
</div>
Nordling Art
  • 857
  • 2
  • 9
  • 19
  • 3
    This is the best solution. Setting tabindex="-1" on the parent will not remove tabindex from the children but visibility: hidden on the parent will remove tabindex from the children. – VerticalGrain Aug 13 '19 at 22:04
  • 4
    Thanks! This great solution saved me a lot of work. This should be the accepted answer, in my opinion. – Eddy Vinck Mar 24 '20 at 12:56
  • 1
    One gotcha -- with visibility, a child can set `visibility:visible` on itself, which would allow it to receive keyboard focus even within a `visibility: hidden` ancestor: https://jakearchibald.com/2014/visible-undoes-hidden/ – Skitterm Sep 03 '20 at 00:04
21

It is possible leave an element BOTH visible and unfocusable, together with its children.

It's done via the HTML property inert: https://html.spec.whatwg.org/multipage/interaction.html#inert.

It's not widely supported yet, but there is a polyfill: https://github.com/WICG/inert.

npm install --save wicg-inert

require('wicg-inert')

<section inert>
  I am visible, but not focusable! 
</section>
Evgenia Karunus
  • 10,715
  • 5
  • 56
  • 70
13

Setting tabindex="-1" allows you to set an element’s focus with script, but does not put it in the tab order of the page. It also does not pull the children of something out of keyboard tab order.

tabindex="-1" is handy when you need to move focus to something you have updated via script or outside of user action.

If you are trying to remove an element from tabindex altogether, whether for screen readers or keyboard users, you will likely have to choose between one of these:

  1. Hide it altogether (via display: none),
  2. Use script on the element so that when it receives focus, the script shifts the focus somewhere else.

Without context (a working URL, a reason that you want to do this), this sounds very much like the opposite of accessibility. I encourage you not to mess with focus unless you have a very good reason.

aardrian
  • 8,581
  • 30
  • 40
  • "Setting tabindex="-1"...does not pull something out of keyboard tab order." On what browser? Setting tabindex to -1 has always worked for me to skip over an object in the tab order. – slugolicious Mar 13 '17 at 19:13
  • 1
    Sorry, meant to say that it does not pull an item's *children* out of tab order. Edited the answer accordingly. In the OP's question, it will not pull the nested ` – aardrian Mar 13 '17 at 21:12
  • Yeah using 'script' is the only option I could find too. Can't display none since the content inside my 'externalContent' div is partially visible on page load. – Adarsh Konchady Mar 14 '17 at 17:32
  • 1
    Was hoping to get a way to do this without an external event listener though! – Adarsh Konchady Mar 14 '17 at 17:33
0

The nearest you can go is using an iframe element, injecting your HTML inside using javascript.

<a href="somewhere.html">first link</a>
<iframe id="iframeid" tabindex="-1"></iframe>
<a href="somewhere_else.html">second link</a>

<script>
    document.getElementById('iframeid').contentWindow.document.body.innerHTML="<button>click me</button>";
</script>

But, this will lead to accessibility problems, like announcing links or buttons which can't be accessed by your keyboard.

Adam
  • 17,838
  • 32
  • 54
  • This isn't viable for me because of accessibility concerns. – Adarsh Konchady Mar 14 '17 at 17:32
  • @AdarshKonchady There's no answer to your question then. You can't be accessible if you specifically ask something which will lead to accessibility concerns. – Adam Mar 15 '17 at 08:03
  • @AdarshKonchady You should also read Aardrian warning with caution: "this sounds very much like the opposite of accessibility." – Adam Mar 15 '17 at 16:12
0
[tab-index="-1"] > * {
    visibility: hidden;
}

This hides any interactive children from tab navigation or mouse clicks, but leaves the parent in the shadow DOM and leaves all sizes of parent and children.

-2

For making tabindex -1 for child elements, lets say you have a wrapper div, // answer with respect to react, when we don't want grid filter to be accessible if its collapsed //this answer is for a special case - where we dont have refs and tabIndex Props does matter for big nested elements // Render method

// if Input and Button are from some kind(eg material UI) of Libraries which dont get tabIndex as a prop and doesnt give refs.

   render() {
    return (
    <div id="wrapper" tabIndex={isCollapsed ? -1 : 0 } >
      <div>
        <Input />
      </div>
      <div>
        <Button />
      </div>
    </div>
  )
}
componentDidMount() {
  this.changeTabIndex()
}

componentDidUpdate(prevProps, prevState){
  if(prevState.isCollapsed !== this.state.isCollapsed) {
     this.changeTabIndex();
  }
}

changeTabIndex() {
   const wrapper = document.getElementById("wrapper");
     const buttons = wrapper.getElementsByTagName("button");
     const inputs = wrapper.getElementsByTagName("input");
     const arr = Array.from(buttons).concat(Array.from(inputs));
     arr.foreach((elem) => { elem.setAttribute("tabIndex", this.state.isCollapsed ? -1 : 0 )});
}