如何使用正值控制tabIndex和不带元素的焦点捕获?

时间:2019-03-20 18:43:14

标签: javascript html css accessibility tabindex

也许这完全不是一个好主意,并且很乐意将此作为反馈。但是,我正在寻找一种控制文档中可Tabable元素的好方法。

要求:

  • Api,用于控制按DOM顺序流出的制表顺序。
  • 焦点陷阱可能是周期性的,必须明确地转义。
  • 一种将非可粘贴元素放入标签流的方法。
  • 用Vanilla JS写。
  • No positive tabIndex values

注意:这是个人思想实验,不确定是否是一个好主意。将采用任何形式的线索:文章,库,代码段,但显然最好的办法是满足所有要求(或您可以提出的任何其他想法)的答案!

    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 &times; 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 &times; escape</button>
  </div>
  <div class="now-tabbable">seven</div>
  <div class="now-tabbable">five</div>
  <div class="now-tabbable">six</div>
</div>

1 个答案:

答案 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 &times; 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 &times; 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键重新进入群组,则最后一个聚焦的元素将再次被聚焦。