Как контролировать tabIndex и фокусировать элементы без использования положительных значений? - PullRequest
2 голосов
/ 20 марта 2019

Может быть, это плохая идея, и я рад принять это за отзыв; Тем не менее, я ищу хороший подход к управлению элементом tabbable в документе.

Требования:

  • API для управления порядком табуляции, который выходит из потока с помощью DOM.
  • Фокусирующие ловушки, которые МОГУТ быть циклическими и должны быть явно экранированы.
  • Метод для помещения элементов без вкладок в поток вкладок.
  • Написано на Vanilla JS.
  • Нет положительных значений tabIndex .

Примечание. Это личный мысленный эксперимент, в котором я не уверен, хорошая это идея или нет. Потребуются любые сведения: статьи, библиотеки, фрагменты кода, но, очевидно, лучшим будет ответ, отвечающий всем требованиям (или любые другие идеи, которые вы можете придумать)!

    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 Ответ

1 голос
/ 20 марта 2019

Вообще говоря, вам следует избегать ловушек фокуса, за исключением модальных контекстов, таких как модальный диалог.

Предлагаемое здесь решение вместо этого создаст фокусную группу с ровингом Вкладка index , в которой вы можете перемещаться по фокусу с помощью клавиш со стрелками.Затем TAB покинет группу.

Имейте в виду, что вы используете фокус-группу только для шаблонов пользовательского интерфейса, в которых поведение можно ожидать.В стандарте ARIA упоминается клавиатурная навигация внутри компонентов :

[…] настоятельно рекомендуется использовать те же привязки клавиш, что и в аналогичных компонентах в обычных операционных системах с графическим интерфейсом, как показано в § 3. Шаблоны проектирования и виджеты .

Вы также можете, например, визуально отформатировать свои кнопки таким образом, чтобы они четко составляли одну группу, подобно Панели инструментов .

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", чтобы сделать один отдельный элемент фокусируемым.Это означает, что если вы повторно войдете в группу с помощью вкладки, последний фокусированный элемент снова будет сфокусирован.

...