也许这完全不是一个好主意,并且很乐意将此作为反馈。但是,我正在寻找一种控制文档中可Tabable元素的好方法。
要求:
注意:这是个人思想实验,不确定是否是一个好主意。将采用任何形式的线索:文章,库,代码段,但显然最好的办法是满足所有要求(或您可以提出的任何其他想法)的答案!
button {
padding: 6px 8px;
margin: 12px;
}
.focus-trap{
padding: 24px;
background: rgba(150, 150, 48, .5);
}
.now-tabbable {
display: inline-flex;
padding: 15px 8px;
background: pink;
margin: 12px;
min-width: 45px;
justify-content: center;
}
<!--
.focus-traps:
- Are meant to declare that tab focus are order by the programmer.
- Focus-traps can be cyclical. currently denoted by class but up for any ideas data-attr etc.
.now-tabbable:
- Are meant to declare that this element is part of the tab flow.
-->
<div class="focus-trap">
<button>ZERO × escape</button>
<button>two</button>
<button>one</button>
<button>three</button>
<hr>
<div class="focus-trap cyclical">
<h1>Trap focus in here until escape</h1>
<button>four - B</button>
<button>four - C</button>
<button>four - A × escape</button>
</div>
<div class="now-tabbable">seven</div>
<div class="now-tabbable">five</div>
<div class="now-tabbable">six</div>
</div>
答案 0 :(得分:1)
通常来说,除了模态上下文(例如模态对话)之外,您应该避免焦点陷阱。
此处建议的解决方案将改为使用roving tab index建立焦点组,您可以在其中通过箭头键导航焦点。 TAB随后将离开小组。
请记住,您仅将焦点组用于可以预期其行为的UI模式。 ARIA标准提到了有关Keyboard Navigation Inside Components的信息:
[…]强烈建议使用与§ 3. Design Patterns and Widgets中所示的常见GUI操作系统中的相似组件相同的键绑定。
例如,您还可以以可视方式格式化按钮,使其清晰地组成一组,类似于Toolbar。
class focusGroup {
constructor(el, cyclical: false) {
this.cyclical = cyclical;
// store children sorted by data-tabindex attribute
this.children = Array.from(el.querySelectorAll('[data-tabindex]')).sort((el1, el2) => el1.getAttribute('data-tabindex') > el2.getAttribute('data-tabindex'));
// remove tab index for all children
this.children.forEach(childEl => childEl.setAttribute('tabindex', '-1'));
// make first child tabbable
this.children[0].setAttribute('tabindex', '0');
this.i = 0;
// bind arrow keys
el.addEventListener('keyup', e => {
if (e.key === 'ArrowRight' || e.key === 'ArrowDown') this.next();
if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') this.prev();
});
}
next() {
if (this.i < this.children.length -1) this.i += 1
else if (this.cyclical) this.i = 0;
this.updateFocus();
}
prev() {
if (this.i > 0) this.i -= 1
else if (this.cyclical) this.i = this.children.length -1;
this.updateFocus();
}
updateFocus() {
this.children.forEach(el => el.setAttribute('tabindex', '-1'));
this.children[this.i].setAttribute('tabindex', '0');
this.children[this.i].focus();
}
}
document.querySelectorAll('.focus-trap:not(.cyclical)').forEach(el => new focusGroup(el));
document.querySelectorAll('.focus-trap.cyclical').forEach(el => new focusGroup(el, true));
button {
padding: 6px 8px;
margin: 12px 0;
}
.focus-trap{
padding: 24px;
background: rgba(150, 150, 48, .5);
}
.now-tabbable {
display: inline-flex;
padding: 15px 8px;
background: pink;
margin: 12px;
min-width: 45px;
justify-content: center;
}
<!--
.focus-traps:
- Are meant to declare that tab focus are order by the programmer.
- Focus-traps can be cyclical. currently denoted by class but up for any ideas data-attr etc.
.now-tabbable:
- Are meant to declare that this element is part of the tab flow.
-->
<div class="focus-trap">
<button data-tabindex="0">ZERO × escape</button>
<button data-tabindex="2">two</button>
<button data-tabindex="1">one</button>
<button data-tabindex="3">three</button>
</div>
<div class="focus-trap cyclical">
<button data-tabindex>four - B</button>
<button data-tabindex>four - C</button>
<button data-tabindex>four - A × escape</button>
</div>
<div>
<button class="now-tabbable">seven</button>
<div class="now-tabbable">five</div>
<div class="now-tabbable">six</div>
</div>
Api,用于控制按DOM顺序不可用的制表顺序。
然后,视觉顺序将需要与焦点顺序对齐。在示例代码中,您可以使用data-tabindex="i"
来控制
焦点陷阱可能是周期性的,必须明确地进行转义。
您可以调用该类并将第二个参数作为true
来建立换行或循环顺序。
一种将不可粘贴元素放入标签流的方法。
只有具有属性data-tabindex
的元素才是可聚焦的。
没有正的tabIndex值。
您将需要使用正数data-tabindexes
,但是示例代码将始终仅使用tabindex="0"
来使单个元素可聚焦。这意味着,如果您通过Tab键重新进入群组,则最后一个聚焦的元素将再次被聚焦。