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

我与影子孤独终老i 提交于 2020-01-03 08:53:12

问题


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.


回答1:


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.




回答2:


Make the first and the last focusable element of your modal react on event, resp. on pressing tab and shift+tab. As far as I tested, it works everywhere.

Example:

function createFocusCycle (first, last) {
first.addEventListener('keydown', function(e){
if (e.keyCode===9 && e.shiftKey) {
last.focus();
e.preventDefault();
}});
last.addEventListener('keydown', function(e){
if (e.keyCode===9) {
first.focus();
e.preventDefault();
}});
}

Naturally, you need to know what is the first and the last focusable element of your modal. Normally it shouldn't be too complicated. Otherwise if you don't know what are the first and last focusable elements of your modal, it's perhaps a sign that you are making a too complex UI.




回答3:


  1. aria-disabled vs aria-hidden

First, note that aria-hidden is not intended to be used when the element is visible on the screen:

Indicates that the element and all of its descendants are not visible or perceivable to any user

The option you should use is aria-disabled

Indicates that the element is perceivable but disabled, so it is not editable or otherwise operable.

  1. on using tabindex

Removing a link from the tabindex is a WCAG failure if this link is still perceivable from a screenreader or clickable. It has to be used conjointly with aria-disabled or better the disabled attribute.

  1. Disabling mouse events using pointer-events css property

The easiest way to disable mouse events is by using the pointer-events css property:

 pointer-events: none;
  1. Disabling keyboard focus

The jQuery :focusable selecter is the easiest thing you could use

$("#div1 :focusable").attr("tabindex", -1);

sample code

$("#div1 :focusable")
.addClass("unfocus")
.attr("tabindex", -1)
.attr("disabled", true);

$("button").on("click", function(){
    $(".unfocus").attr("tabindex", 0)
    .removeClass("unfocus")
    .removeAttr("disabled");
});
.unfocus {
  pointer-events: none;
  
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://code.jquery.com/ui/1.12.0/jquery-ui.min.js"></script>
<div id="div1">
    <a href="">non clickable link</a>
    <div tabindex="0">
      non focusable div
    </div>
</div>

<div id="div2">
    <button>click here to restore other links</button>
</div>



回答4:


Use role = "dialog" aria-modal="true" on your modal popup




回答5:


In the future this could be solved with the inert attribute: https://github.com/WICG/inert/blob/7141197b35792d670524146dca7740ae8a83b4e8/explainer.md



来源:https://stackoverflow.com/questions/45018047/prevent-background-items-from-receiving-focus-while-modal-overlay-is-covering-th

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!