-->

Prevent background items from receiving focus whil

2019-06-21 16:39发布

问题:

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 kwydown event, resp. on pressing tab and shift+tab. As far as I ahve tested, it works everywhere.

`Basic 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