我正在努力使叠加模式更易于访问。它基本上像this JSFiddle。当您打开模态时,焦点没有正确进入模态,并且它继续关注页面中的其他(隐藏的,背景)项目。
您可以在我的JSFiddle演示中看到我已使用aria-controls
,aria-owns
,aria-haspopup
甚至aria-flowto
。
<button
aria-controls="two"
aria-owns="true"
aria-haspopup="true"
aria-flowto="two"
onclick="toggleTwo();"
>
TOGGLE DIV #2
</button>
然而,在使用MacOS VoiceOver时,这些都不是我想要的(尽管VoiceOver尊重我在div aria-hidden
上设置的two
)。
我知道我可以操纵tabindex
,但是,0以上的值对可访问性不利,所以我唯一的另一个选择是在页面上手动查找所有可聚焦元素将它们设置为tabindex=-1
,这在这个庞大而复杂的网站上是不可行的。
此外,我已经研究过使用Javascript手动拦截和控制标签行为,以便焦点移动到弹出窗口并在退出底部时回到顶部,但是,这也干扰了可访问性。
答案 0 :(得分:2)
可以使用focus()方法移动焦点。我已根据预期行为更新了jsFiddle。我在Windows和Chrome上的JAWS上进行了测试。
我在“two”div上添加了tabindex="-1"
,以便使用焦点方法可以对焦。
我将切换功能分成两个函数,这可能会被重构以满足您的需要,但是一个函数将aria-hidden属性设置为true并将焦点移动到新打开的模态上,另一个函数执行相反的操作
我删除了过多的咏叹调属性,咏叹调的第一条规则是只在必要时才使用它。如果您只是在咏叹调中捣碎,这可能会导致意外行为。
为了将焦点保持在模态中,不幸的是,最好的选择之一是将所有其他活动元素设置为tabindex="-1"
或aria-hidden="true"
。我已经应用了一个替代方案,其中事件监听器被添加到标签上的模态中的最后一个元素。为了符合要求,必须在第一个元素上添加另一个侦听器,以便在shift + tab事件时将焦点移动到最后一个元素。
不幸的是,据我所知,没有比上述解决方案更清晰的答案,无法将焦点保持在模态中。
答案 1 :(得分:2)
使你的模态的第一个和最后一个可聚焦元素对kwydown事件做出反应,resp。在按Tab键和shift + tab上。据我测试,它无处不在。
`基本示例:
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();
}});
}
当然,您需要知道模态的第一个和最后一个可聚焦元素是什么。通常它不应该太复杂。 否则,如果您不知道模态的第一个和最后一个可聚焦元素是什么,那么这可能表明您正在制作过于复杂的UI。
答案 2 :(得分:1)
aria-disabled
vs aria-hidden
首先,请注意,当元素在屏幕上可见时,不应使用aria-hidden
:
表示该元素及其所有后代对任何用户都不可见或不可感知
您应该使用的选项是aria-disabled
表示该元素是可感知但已禁用的,因此它不可编辑或可操作。
tabindex
如果仍可从屏幕阅读器或可点击的方式感知此链接,则从tabindex删除链接是WCAG失败。它必须与aria-disabled
或更好的disabled
属性一起使用。
pointer-events
css属性禁用鼠标事件的最简单方法是使用pointer-events
css属性:
pointer-events: none;
jQuery :focusable
选择器是你可以使用的最容易的东西
$("#div1 :focusable").attr("tabindex", -1);
$("#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>
答案 3 :(得分:0)
在模式弹出窗口中使用role =“ dialog” aria-modal =“ true”
答案 4 :(得分:0)
将来可以通过inert
属性:https://github.com/WICG/inert/blob/7141197b35792d670524146dca7740ae8a83b4e8/explainer.md
答案 5 :(得分:0)
我使用了这个 focusguard 元素的解决方案,它使用 JS 将焦点移动到所需的元素上。
在这里找到: https://jsfiddle.net/dipish/F82Xj/
<p>Some sample <a href="#" tabindex="0">content</a> here...</p>
<p>Like, another <input type="text" value="input" /> element or a <button>button</button>...</p>
<!-- Random content above this comment -->
<!-- Special "focus guard" elements around your
if you manually set tabindex for your form elements, you should set tabindex for the focus guards as well -->
<div class="focusguard" id="focusguard-1" tabindex="1"></div>
<input id="firstInput" type="text" tabindex="2" />
<input type="text" tabindex="3" />
<input type="text" tabindex="4" />
<input type="text" tabindex="5" />
<input type="text" tabindex="6" />
<input id="lastInput" type="text" tabindex="7" />
<!-- focus guard in the end of the form -->
<div class="focusguard" id="focusguard-2" tabindex="8"></div>
<!-- Nothing underneath this comment -->
JS:
$('#focusguard-2').on('focus', function() {
$('#firstInput').focus();
});
$('#focusguard-1').on('focus', function() {
$('#lastInput').focus();
});
答案 6 :(得分:0)
据我所知,没有原生 HTML aria
支持在模态关闭时恢复相同的焦点。
aria-modal
将取代 aria-hidden
。它应该与 role="alertdialog"
结合使用。此 www.w3.org/TR/wai-aria-practices-1.1 页面解释了它们的作用并提供了一个复杂的示例。受此启发,我制作了一个最小的片段。
切勿使用高于 tabindex
的 0
。 tabindex="0"
设置为模态标题。所以它通过 tab
键聚焦。打开按钮保存在变量 lastFocusedElement
中。当模态关闭时,焦点回到那里。
window.onload = function () {
var lastFocusedElement;
// open dialog
document.querySelector('#open-dialog').addEventListener('click', (e) => {
document.querySelector('#dialog').classList.add('d-block');
document.querySelector('#backdrop').classList.add('d-block');
lastFocusedElement = e.currentTarget;
});
// close dialog and back to last focused element
document.querySelector('#close-dialog').addEventListener('click', (e) => {
document.querySelector('#dialog').classList.remove('d-block');
document.querySelector('#backdrop').classList.remove('d-block');
lastFocusedElement.focus();
});
}
h2 { font-size: 1em }
.d-block {
display: block !important;
}
.dialog {
display: none;
position: fixed;
top: 1rem;
width: 25rem;
padding: 1rem;
background: #fff;
border: 1px solid #000;
z-index: 1050;
font-family: arial, sans-serif;
font-size: .8em;
}
#backdrop {
display: none;
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
z-index: 1040;
background: rgba(0, 0, 0, 0.5);
}
<label for="just-a-label">Just a label</label>
<button id="open-dialog" type="button" aria-labelledby="just-a-label">open dialog</button>
<div id="dialog" class="dialog" role="alertdialog" aria-modal="true" aria-labelledby="dialog-label" aria-describedby="dialog-desc">
<h2 id="dialog-label" tabindex="0">PRESS TAB to get here</h2>
<div id="dialog-desc">
<p>Dialog Description.</p>
</div>
<div>
<label for="formfield">
<span>another formfield:</span>
<input id="formfield" type="text">
</label>
</div>
<hr>
<div>
<button id="close-dialog" type="button" tabindex="0">CLOSE (and focus back to open button)</button>
</div>
</div>
<div id="backdrop"></div>