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

Скажем, у меня есть документ, полный фокусируемых элементов, либо потому, что они изначально являются фокусируемыми (например, <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's 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, я бы предпочел ссылку на код библиотеки, который это делает.


person Chris Calo    schedule 24.10.2011    source источник
comment
Самый простой способ — привязать событие focusin к документу. В обработчике событий вы проверяете, находится ли текущий фокус в диалоговом окне. Если нет, то повторно примените фокус к какому-либо элементу в диалоговом окне. Не забудьте удалить привязку события к документу после закрытия диалогового окна.   -  person Bjorn    schedule 14.08.2015
comment
@Bjorn, похоже, вам нужно использовать таймер, чтобы сфокусировать внимание на другом элементе, а не на целевом элементе события focusin. Хотя для этого требуется меньше кода, похоже, что это может привести к ошибкам, если вы не выберете правильное время. Вы должны опубликовать отдельный ответ, если он заработает.   -  person Chris Calo    schedule 16.08.2015
comment
Для этого вам не нужен таймер, вы просто реагируете на событие focusin. У меня это работает, но мой код зависит от jQuery/Backbone/Marionette, поэтому нет четкого ответа, если вы работаете с другим фреймворком.   -  person Bjorn    schedule 17.08.2015


Ответы (3)


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

Чтобы реализовать это в моде jQuery, требуются две вещи:

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

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

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

Трудная часть — знать, какие элементы являются вкладками. Поскольку все элементы с вкладками — это фокусируемые элементы, у которых нет 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… считаются видимыми», но на них нельзя сфокусироваться.

person Chris Calo    schedule 24.10.2011

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

person Domenic    schedule 24.10.2011

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

person Moin Zaman    schedule 24.10.2011
comment
Спасибо, Мойн. Похоже, что пример 4 в dev.iceburg.net/jquery/jqModal пытается перехватить фокус внутри диалогового окна, но это не совсем работает (есть ссылка JavaScript, которая получает фокус). Тем не менее, это выглядит многообещающе, но я не уверен, как это достигается. Можете ли вы указать мне источник, который обрабатывает эту часть? - person Chris Calo; 24.10.2011
comment
Выполните поиск по букве «L» в верхнем регистре в плагине. Кажется, это события keypress, keydown и mousedown. - person Moin Zaman; 24.10.2011