Простейший способ для вкладок только через фокусируемые потомки определенного элемента?

Скажем, у меня есть документ, полный сосредоточенных элементов, потому что они врожденно ориентированы (например, <input type="text">) или потому, что у них есть tabindex="0" или тому подобное.

Теперь скажем там раздел моего документа, который я хочу отобразить как модальное диалоговое окно, и я не хочу, чтобы пользователь был отвлечен чем-либо вне диалогового окна. Я хотел бы, чтобы клавиша табуляции выполняла цикл только через фокусируемые элементы внутри элемента контейнера для диалогового окна. Каков самый простой способ сделать это?

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

Моя первая попытка выглядит так, но работает только в прямом направлении (нажатие Tab). Он не работает в обратном направлении (нажимайте Shift + Tab).

<div>Focusable stuff outside the dialog.</div>
<div class="dialog" tabindex="0">
  <!-- Focus should be trapped inside this dialog while it open -->
  <div class="content">
    Form contents and focusable stuff here.
  </div>
  <div class="last-focus" tabindex="0" onfocus="this.parentNode.focus()"></div>
</div>
<div>More focusable stuff outside the dialog.</div>

Я бы предпочел увидеть чистые решения для JavaScript. Если есть способ сделать это с помощью библиотеки, такой как jQuery, я бы предпочел ссылку на код библиотеки, который делает это.

Ответ 1

В интересах полноты, я беру ссылку на диалог jQuery UI, который @Domenic предоставил и заполнил детали.

Для реализации этого в модуле jQuery требуется две вещи:

  • Прослушивание Tab или Shift+Tab (на keydown) для модального элемента, который должен удерживать фокус. Это единственный способ перемещения фокуса с клавиатуры. (Если вы хотите предотвратить взаимодействие с мышью с остальной частью документа, это отдельная проблема, решаемая путем покрытия ее элементом, чтобы предотвратить прохождение каких-либо событий мыши.)

  • Поиск всех элементов табуляции внутри модального элемента. Это подмножество всех фокусируемых элементов, исключая те, которые имеют tabindex="-1".

Tab идет вперед. Shift+Tab идет назад. При каждом нажатии Tab, когда последний элемент tabbable в модальном элементе сфокусирован, первый должен получать фокус. Точно так же в любое время Shift+Tab нажимается, когда первый сменный элемент сфокусирован, последний должен получать фокус. Это будет держать фокус внутри модального элемента.

Жесткая часть - это знание того, какие элементы табулируются. Так как элементы tabbable являются элементами фокусирующегося, которые не имеют tabindex="-1", тогда нам нужно знать, какие элементы являются ориентируемыми. Поскольку нет свойства, чтобы определить, является ли элемент ориентируемым, jQuery использует жесткое кодирование следующих случаев:

  • input, select, textarea, button и object элементы, которые не отключены.
  • a и area, которые имеют href или имеют числовое значение для tabindex.
  • любой элемент, который имеет числовое значение для tabindex.

Этого недостаточно, чтобы проверить эти три случая. jQuery продолжает следить за тем, чтобы элемент был виден. Это означает, что оба значения должны быть истинными:

  • Ни один из его предков display: none.
  • Вычисленное значение visibility равно visible. Это означает, что ближайший предок, имеющий набор visibility, должен иметь значение visible. Если никакой предок не установлен visibility, тогда вычисленное значение visible.

Следует отметить, что jQuery :visible селектор не подходит для этой реализации, потому что он говорит: "Элементы с visibility: hidden... являются считаются видимыми", но они не могут быть сфокусированы.

Ответ 2

Диалоговое окно jQuery UI делает это, захватывая события keydown, проверяя, являются ли они для TAB или нет, затем вручную фокусирует правильный элемент.

Ответ 3

jqModal Плагин jQuery делает это из коробки, установив для параметра modal значение true. Примеры на этой странице с формами должны показать это. Я помню, как прошел через код, чтобы увидеть, что происходит, и вы можете сделать это довольно легко с простой JS.