Prevent background items from receiving focus while modal overlay is covering them?

Brenton Strine picture Brenton Strine · Jul 10, 2017 · Viewed 14.2k times · Source

I am working on making an overlay modal more accessible. It works essentially like this JSFiddle. When you open the modal, the focus doesn't properly go into the modal, and it continues to focus on other (hidden, background) items in the page.

You can see in my JSFiddle demo that I have already used aria-controls, aria-owns, aria-haspopup and even aria-flowto.

<button 
  aria-controls="two" 
  aria-owns="true" 
  aria-haspopup="true"
  aria-flowto="two"
  onclick="toggleTwo();"
  >
  TOGGLE DIV #2
</button>

However, while using MacOS VoiceOver, none of these do what I intend (though VoiceOver does respect the aria-hidden that I set on div two).

I know that I could manipulate the tabindex, however, values above 0 are bad for accessibility, so my only other option would be to manually find all focusable elements on the page and set them to tabindex=-1, which is not feasible on this large, complicated site.

Additionally, I've looked into manually intercepting and controlling tab behavior with Javascript, so that the focus is moved into the popup and wraps back to the top upon exiting the bottom, however, this has interfered with accessibility as well.

Answer

Taylor N picture Taylor N · Jul 11, 2017

Focus can be moved with the focus() method. I've updated the jsFiddle with the intended behavior. I tested this on JAWS on Windows and Chrome.

I've added a tabindex="-1" on the "two" div to allow it to be focusable with the focus method.

I split the toggle function into two functions, this can probably be refactored to fit your needs, but one function sets the aria-hidden attribute to true and moves the focus on the newly opened modal, and the other function does the reverse.

I removed the excessive aria attributes, the first rule of aria is to only use it when necessary. This can cause unexpected behavior if you're just mashing in aria.

To keep focus within the modal, unfortunately one of the best options is to set all other active elements to tabindex="-1" or aria-hidden="true". I've applied an alternative where an event listener is added to the last element in the modal upon tabbing. To be compliant, another listener must be added to the first element to move focus to the last element upon a shift+tab event.

Unfortunately, to my knowledge there isn't a cleaner answer than those above solutions to keeping focus within a modal.