Разделенная кнопка с выпадающим меню
Этот сниппет создает разделенную кнопку с выпадающим списком (меню) при помощи CSS.
Разделенная кнопка с выпадающим меню
Наведите курсор мышки на иконку стрелки, чтобы открыть выпадающее меню:
Как сделать разделенную кнопку с выпадающим меню
Шаг 1) Добавляем HTML:
Создаем выпадающее меню, которое будет появляться, когда пользователь наводит мышку на иконку.
Ссылка 1 Ссылка 2 Ссылка 3
Объяснение примера
Для вызова выпадающего меню можно использовать любой элемент, например, , или
.
Для создания выпадающего меню используем контейнер (например, ) и добавляем в него ссылки выпадающего меню.
Оборачиваем кнопку и меню элементом , чтобы корректно задать положение выпадающего меню при помощи CSS.
Шаг 2) Добавляем CSS:
/* Вызывающая кнопка */ .btn < background-color: #2196F3; color: white; padding: 16px; font-size: 16px; border: none; outline: none; >/* Контейнер - нужен для позиционирования выпадающего контента */ .dropdown < position: absolute; display: inline-block; >/* Выпадающий контент (по умолчанию скрыт) */ .dropdown-content < display: none; position: absolute; background-color: #f1f1f1; min-width: 160px; z-index: 1; >/* Ссылки внутри выпадающего блока */ .dropdown-content a < color: black; padding: 12px 16px; text-decoration: none; display: block; >/* Изменяем цвет ссылки при наведении */ .dropdown-content a:hover /* Показываем выпадающий блок при наведении */ .dropdown:hover .dropdown-content < display: block; >/* Изменяем фоновый цвет вызывающей кнопки при выводе выпадающего блока */ .btn:hover, .dropdown:hover .btn
Активная кнопка в статичном меню html
Доброго вечера! Стоит следующая задача. Есть статичное меню на html и необходимо задать класс для активной кнопки. К примеру: Главная | Страница | Контакты нажав на контакты, кнопка становится активной Главная | Страница | Контакты В вордпресе знаю для активной кнопки присваивается класc сurrent, а как быть со статичным кодом не динамичным.
На сайте с 21.05.2010
20 декабря 2012, 20:51
Точно так же. Назначаете стиль/класс, и вешаете через jquery анимацию/действия/стиль на свою кнопку.
На сайте с 25.03.2011
20 декабря 2012, 20:57
через css #ваш id блока верхнего меню li a:active < а дальше какой вы ее хотите сделать (цвет, выделение и тд), напр. >например: #top_nav li a:active < color: #000; >
На сайте с 26.03.2011
20 декабря 2012, 21:45
Lester7:
через css
#ваш id блока верхнего меню li a:active <
а дальше какой вы ее хотите сделать (цвет, выделение и тд), напр.
#top_nav li a:active <
color: #000;
>
Этот стиль будет активным только в момент нажатия, а нужно после нажатия зафиксировать
VHS-1980:
Точно так же. Назначаете стиль/класс, и вешаете через jquery анимацию/действия/стиль на свою кнопку.
от если бы чучуть бы подробнее или направить туда где это можно почитать
На сайте с 17.11.2012
20 декабря 2012, 22:07
Ben_Laden:
Этот стиль будет активным только в момент нажатия, а нужно после нажатия зафиксировать
А при нажатии пользователь переходит на другую страницу? Или это навигация на AJAX? Или это не меню навигации? Потому что при переходе, html самосотоятельно запомнить класс не может, надо будет либо через JS либо PHP подставлять в текст html класс.
На сайте с 26.03.2011
20 декабря 2012, 23:03
Henryh:
А при нажатии пользователь переходит на другую страницу? Или это навигация на AJAX? Или это не меню навигации?
Потому что при переходе, html самосотоятельно запомнить класс не может, надо будет либо через JS либо PHP подставлять в текст html класс.
Да это навигация, при нажатии просто открывается новая страница, меню навигации написано на html, хотя сайт на вордпрессе, просто из за определенного тз пришлось сделать его не денамическим. пример меню обычный
Инклюзивные компоненты: меню и кнопки меню
Классифицировать сложно. Например, возьмём крабов. Раки-отшельники, веерные крабы и подковообразные крабы, с точки зрения таксономии, не настоящие крабы, но это не мешает использовать в их названиях слово «краб». Всё становится ещё запутаннее, когда через какое-то время благодаря процессу, называемому канцеризацией, ненастоящие крабы эволюционируют, чтобы больше походить на настоящих. Это то, что произошло с королевскими крабами, которые в прошлом были раками-отшельниками. Представьте размеры их раковин!
В дизайне мы часто совершаем такую же ошибку, когда называем разные вещи одинаковыми именами. Они кажутся похожими, но внешность может быть обманчивой. Это плохо сказывается на прозрачности вашей библиотеки компонентов. С точки зрения инклюзивности это может привести к тому, что вы измените нужную семантику и поведение компонента на совершенно противоположные. Пользователи будут ожидать одного поведения, а получат другое.
Классический пример — термин «выпадающий список» (dropdown). В интерфейсах многие вещи «выпадают», включая в , и подменю навигации со списком ссылок, которое раскрывается с помощью JavaScript. Одинаковые названия — разные явления. Некоторые называют это «выпадающее меню» (pull-down menu), но давайте не вдаваться в подробности.
Выпадающие списки, которые состоят из нескольких пунктов, часто называют «меню». Об этом я и хочу поговорить в этой статье. Мы будем делать настоящее меню, но по пути обсудим их не совсем настоящие реализации.
Давайте начнём с вопроса: является ли блок ссылок с картинки, выпадающий вниз из панели навигации, меню?
Правильный ответ: нет, это не настоящее меню.
То, что навигационная схема состоит из списков или ссылок — давнее соглашение. Оно почти так же давно предписывает, что дополнительная навигация (sub-navigation) должна быть вложенными списками со ссылками. Если бы я удалил стили для компонента, показанного выше, я бы увидел что-то вроде этого списка ссылок, только с Times New Roman и синего цвета.
• [Главная](#) • [О нас](#) • [Магазин](#) ◦ [Одежда для собак](#) ◦ [Вафельницы](#) ◦ [Магические шары](#) • [Контакты](#)
С точки зрения семантики вложенные списки со ссылками в этом контексте корректны. Системы навигации на самом деле оглавления: именно так они устроены. Единственное, что действительно заставляет думать, что перед нами меню — это стилизация вложенных списков и способ их отображения при наведении курсора или при фокусе.
В этом месте часто ошибаются и начинают добавлять семантику из WAI-ARIA: aria-haspopup=»true» , role=»menu» , role=»menuitem» и так далее. Их можно использовать, но в другом контексте. Вот две причины для этого:
- ARIA-меню предназначены не для навигации, а для десктопных приложений. Представьте себе систему меню для любого из них.
- Ссылка верхнего уровня должна быть ссылкой. То есть она не ведёт себя как кнопка меню.
Относительно второго пункта: при перемещении по области навигации в подменю ожидаешь, что каждое подменю будет появляться при наведении или при фокусе на ссылке верхнего уровня (например, «Магазин» из примера выше). Это одновременно показывает подменю и размещает ссылки в нём в порядке получения фокуса. С помощью JavaScript можно управлять событиями фокуса (focus) и его потери (blur) и сохранить внешний вид подменю пока это необходимо. При этом те, кто использует для навигации клавиатуру, должны иметь возможность переходить по очереди от одной ссылки каждого уровня к другой.
Кнопки меню, которым задан атрибут aria-haspopup=»true» , так себя не ведут. Они становятся активными по клику и у них нет другой цели кроме показа скрытого меню.

Как показано на картинке, вне зависимости от того раскрывается меню или скрывается, это будет объявлено благодаря атрибуту aria-expanded . Вам нужно только изменить это состояние при клике, а не при фокусе. Пользователи обычно не ожидают явного изменения состояния при простом событии фокуса. На самом деле в нашей системе навигации состояние не изменяется. Это просто трюк со стилизацией. С точки зрения поведения мы можем перемещаться с помощью клавиши Tab так, как если бы не было никакого трюка с показом и скрытием элемента.
Проблема с навигационными подменю Скопировать ссылку
Навигационные подменю (или для кого-то «выпадающие списки») отлично работают с мышью или с клавиатуры, но не так хороши для касаний. Когда вы в первый раз нажимаете на ссылку верхнего уровня «Магазин» из примера, то сообщаете, что надо открыть подменю и перейти по ссылке.
Здесь есть два возможных варианта решения проблемы:
- Избежать поведения по умолчанию ссылок верхнего уровня ( e.preventDefault() ) и написать скрипт для полной поддержки семантики и поведения меню WAI-ARIA.
- Убедиться, что каждая страница, на которую ведёт ссылка верхнего уровня меню, имеет оглавление в качестве альтернативы подменю.
Первое решение не самое хорошее. Я ранее замечал, что этот тип семантики и поведения нежелателен в данном контексте, где ссылки — это управляемые контролы (subject controls). Кроме того пользователи больше не смогут переходить на страницу верхнего уровня, если она есть.
Какие устройства — сенсорные? Скопировать ссылку
Заманчиво думать: «Это не самое хорошее решение, но я применю его только для сенсорных интерфейсов». Проблема в том, как определить, есть ли у устройства сенсорный экран?
Вам точно не стоит относить любой «маленький экран» к разряду «экранов с сенсорным управлением». Работая в одном офисе с людьми, разрабатывающими сенсорные дисплеи для музеев, я могу заверить вас, что самые большие экраны — сенсорные. Не забывайте о ноутбуках с клавиатурой и сенсорным дисплеем.
К тому же многие, но далеко не все устройства небольшого размера являются сенсорными. В инклюзивном дизайне вы не можете позволить себе предполагать.
Второе решение более инклюзивное и надёжное. Это «фолбэк» для пользователей всех устройств. Но я специально взял в кавычки это слово, потому что думаю, что на самом деле постраничные оглавления — это лучший способ обеспечения навигации.
Похоже, получившая премию команда Government Digital Services с этим согласится. Вы также могли видеть такие оглавления в Wikipedia.

Оглавление Скопировать ссылку
Оглавления — это навигация для связанных между собой страниц или их разделов, которые должны быть семантически похожи на основные области навигации сайта. Для них используются элементы с , списки и общие подписи к ним.
Примечания Скопировать ссылку
- В этом примере мы представляем каждый раздел как отдельную страницу, как если бы это было в выпадающем подменю.
- Важно, чтобы каждая страница из «Магазин» имела одинаковую структуру. Поэтому оглавление «Продукты» находится в одном и том же месте. Консистентность улучшает понимание.
- Список группирует элементы, а вспомогательные технологии могут установить их количество и объявить о них, например, с помощью синтезированного голоса в скринридерах.
- Тегу заголовок задан с помощью атрибута aria-labelledby . Это означает, что большинство скринридеров объявит «Продукты, навигация» при попадании в область с помощью клавиши Tab . Также это приведёт к тому, что такая навигация будет разбита скринридерами на отдельные элементы, через которые пользователи смогут переходить к областям страницы напрямую.
Всё на одной странице Скопировать ссылку
Если вы можете поместить все разделы на одной странице, при этом не сделав её слишком длинной и избежав утомительной прокрутки, то так даже лучше. Просто задайте для каждого раздела якорную ссылку. Например, href=»#waffle-irons» должна вести к id=»waffle-irons» .
Примечание: в некоторых браузерах фокус плохо переносится на отдельные фрагменты страницы. Добавление tabindex=»-1″ к нужному фрагменту исправляет это.
В случае, если на сайте много контента, лучше тщательно продумывать информационную архитектуру с оглавлениями меню, чем делать ненадёжную и громоздкую систему выпадающих меню. Так будет легче сделать адаптивную версию страницы с меньшим количеством кода, а также это упростит восприятие её структуры. Там, где системы выпадающих меню скрывают структуру, оглавления обнажают её.
На некоторых сайтах, включая правительственный цифровой сервис gov.uk, есть страницы с указателями (или «темами»), которые представляют собой просто оглавления. Это настолько мощная концепция, что популярный генератор статических сайтов Hugo создаёт такие страницы по умолчанию.

Информационная архитектура — это важная часть инклюзивности. Плохо организованный сайт может соответствовать нужным вам техническим требованиям, но при этом будет отталкивать многих пользователей. Особенно это касается тех, у кого есть когнитивные нарушения или у кого мало времени на то, чтобы с чем-то долго разбираться.
Кнопки навигационного меню Скопировать ссылку
Пока мы обсуждаем тему фальшивых навигационных меню, было бы упущением с моей стороны не поговорить про кнопки меню. Вы наверняка видели состоящую из трёх линий иконку-гамбургер или «navicon».
Даже с упрощённой информационной архитектурой и одним уровнем навигационных ссылок пространство на маленьких экранах на вес золота. Скрытие навигации за кнопкой означает, что во вьюпорте будет больше места для основного контента.
Кнопка навигации больше всего из того, что мы изучали, похожа на настоящую кнопку меню. Так как она открывает меню по клику, то она должна:
- Быть кнопкой, а не ссылкой.
- Содержать информацию о развёрнутом или свёрнутом состоянии соответствующего меню (которое, строго говоря, представляет собой просто список ссылок).
Прогрессивное улучшение Скопировать ссылку
Давайте не будем забегать вперёд. Мы должны помнить о прогрессивном улучшении и подумать, как это будет работать без JavaScript.
В изначальном HTML-файле мало что можно сделать с помощью кнопок (кроме кнопок для отправки данных, но они даже близко не связаны с тем, что нам нужно сделать). Может быть, вместо этого, нам следует начать с простой ссылки, которая приведёт нас к навигации?
Нет особой необходимости использовать ссылку, если между ней и навигацией нет большого количества контента. Так как навигация сайта в большинстве случаев должна размещаться наверху страницы, то в этом решении нет необходимости. Так что, действительно, навигационное меню без JavaScript должно быть просто… навигацией.
Вы улучшите его, добавив кнопку, которая в исходном состоянии скрывает навигацию с помощью атрибута hidden :
Некоторые более старые браузеры, сами знаете какие, не поддерживают hidden , так что не забудьте учесть это в вашем CSS. Селектор ниже решит эту проблему, так как display: none делает то же самое, что и атрибут hidden : скрывает меню от вспомогательных технологий и удаляет ссылку из порядка получения фокуса.
[hidden]
Делать всё возможное для поддержки старого программного обеспечения — это, конечно, часть инклюзивного дизайна. Некоторый софт невозможно обновить или его разработчики не хотят этого делать.
Расположение Скопировать ссылку
Многие неправильно размещают кнопку за пределами области (навигации). Это может привести к тому, что пользователи скринридеров, которые перемещаются по с помощью горячих клавиш, решат, что область пустая, что не особо им поможет. Со списком, скрытым от скринридеров, они просто столкнулись бы с этим:
Вот как мы можем переключить состояние:
var navButton = document.querySelector('nav button'); navButton.addEventListener('click', function() < let expanded = this.getAttribute('aria-expanded') === 'true' || false; this.setAttribute('aria-expanded', !expanded); let menu = this.nextElementSibling; menu.hidden = !menu.hidden; >);
ARIA-controls Скопировать ссылку
Как я уже писал в «Aria-controls Is Poop», атрибут aria-controls , который помогает пользователям скринридеров при переходе от контролирующего элемента к контролируемому, поддерживается только в JAWS. Так что на него нельзя положиться.
Без хорошего метода перемещения между элементами, вам нужно убедиться в том, что одно из следующего верно:
- Первая ссылка раскрывающегося списка — следующая в порядке фокуса после кнопки (как в предыдущем примере кода).
- При раскрытии списка на первую ссылку сделан фокус.
В нашей ситуации я рекомендовал бы первый вариант. Это намного проще, так как не нужно беспокоиться о перемещении фокуса обратно на кнопку и о том, какие события для этого нужны. Кроме того, сейчас нет ничего, что могло бы предупредить пользователей о том, что фокус будет перемещён в другое место. В настоящих меню, которые мы вскоре обсудим, за это отвечает aria-haspopup=»true» .
Использование aria-controls на самом деле не приносит большого вреда, за исключением того, что делает объявления в скринридере более подробным. Однако некоторым пользователям JAWS это может быть нужно. Вот как будет использоваться атрибут вместе с id для списка:
Меню и роли menuitem Скопировать ссылку
Настоящее меню (с точки зрения WAI-ARIA) должно себя идентифицировать с помощью роли menu (для контейнера) и, обычно, роли menuitem для дочерних элементов (или других ролей для подобных элементов). Эти роли для родителей и детей работают сообща и предоставляют вспомогательным технологиям нужную информацию. Вот как можно расширить список, добавив для него семантику меню:
Поскольку наше навигационное меню начинает вести себя как «настоящее» меню, не должно ли тут быть ролей menuitem ?
Краткий ответ — нет. Более подробный ответ: нет, потому что наши элементы списка содержат ссылки, а элементы menuitem не могут иметь интерактивных потомков. То есть они являются контролами в меню.
Мы хотим, чтобы пользователи знали, что они используют ссылку и могут ожидать от неё поведения ссылки, так что пример выше не очень хороший. Как я уже сказал, настоящие меню предназначены для (написанных на JavaScript) приложений.
То, что у нас осталось, это своего рода гибридный компонент, который не совсем настоящее меню, но, по крайней мере, сообщает пользователям, открыт ли список ссылок, благодаря состоянию aria-expanded . Это подходящий паттерн для навигационных меню.
Примечание. Элемент Скопировать ссылку
Если вы давно знакомы с адаптивным дизайном, то можете вспомнить паттерн, в котором навигация была свёрнута в элемент в небольших вьюпортах.

Как и в случае с переключателями на основе чекбоксов, которые мы обсуждали (см. в переводе — прим. переводчика), использование нативного элемента, который ведёт себя не так, как задумано, без дополнительного скрипта — хороший выбор. Особенно с точки зрения эффективности и производительности в случае мобильных устройств. Элементы — это своего рода меню, с семантикой, аналогичной меню, открывающееся кнопкой, которую мы скоро будем создавать.
Однако, так же как и с переключателем, основанном на чекбоксе, мы используем элемент, связанный со вводом данных, а не просто делаем выбор. Это может запутать многих пользователей, тем более, что этот шаблон использует JavaScript, чтобы выбранный элемент вёл себя как ссылка. Неожиданное изменение контекста, которое это вызывает, считается ошибкой согласно критерию 3.2.2 On Input (Level A).
Настоящие меню Скопировать ссылку
Теперь, когда мы обсудили ненастоящие меню и квази-меню, пришло время создать настоящее меню, которое открывается и закрывается настоящей кнопкой меню. С этого момента я буду называть вместе кнопку и меню — «кнопкой меню».
Но в каком случае наша кнопка меню может считаться настоящей? Что ж, это будет компонент меню, предназначенный для выбора параметров в приложении, который предусматривает всю нужную семантику и стандартное поведение для такого инструмента.
Как я уже упоминал, эти соглашения пришли из дизайна десктопных приложений. ARIA-атрибуты и управление фокусом с помощью JavaScript необходимы для их полной имитации. Одна из целей ARIA — помочь веб-разработчикам создавать сложные веб-интерфейсы и не нарушать при этом устоявшие паттерны взаимодействия, пришедшие из реального мира.
В этом примере мы представим, что наше приложение — это какая-то игра или викторина. Наша кнопка меню даёт пользователю выбрать уровень сложности. С учётом всей семантики меню выглядит следующим образом:
Примечания Скопировать ссылку
- aria-haspopup просто указывает на то, у кнопки есть скрытое меню. Это работает как предупреждение о том, что при нажатии на кнопку пользователь будет перемещён в выпадающее меню (скоро мы рассмотрим поведение фокуса). Его значение не меняется: оно всегда true .
- Элемент внутри кнопки содержит символ Юникод с маленьким чёрным перевёрнутым треугольником. Он визуально показывает, что нажатие на кнопку раскроет что-то под ней. Этого aria-haspopup не может показать. Атрибут aria-hidden=»true» не разрешает скринридерам объявлять «перевёрнутый треугольник» или что-то подобное. Благодаря aria-haspopup этого не нужно делать в невизуальном контексте.
- aria-haspopup дополняет aria-expanded . Атрибут сообщает пользователю, находится ли он в данный момент в открытом (развёрнутом) меню или оно закрыто (свёрнуто), переключаясь между значениями true и false .
- Само меню имеет (точно названную) роль menu . Для него нужны потомки с ролями menuitem . Они не обязательно должны быть прямыми потомками элемента menu . В этом примере так сделано для простоты.
Клавиатура и поведение при фокусе Скопировать ссылку
Когда дело доходит до того, чтобы сделать интерактивные контролы доступными для клавиатуры, лучшее, что вы можете сделать, это использовать правильные элементы. Поскольку здесь используются элементы , то мы можем быть уверены в том, что события по клику будут срабатывать при нажатии клавиш Enter и Space , как указано в HTMLButtonElement interface. Также это означает, что мы можем отключить пункты меню, используя связанное с кнопкой свойство disabled .
Есть намного больше способов взаимодействовать с кнопкой с клавиатуры. Вот краткий обзор поведения при фокусе с клавиатуры, которое мы будем реализовывать на основе WAI-ARIA Authoring Practices 1.1:
- Enter , Space или ↓ на кнопке меню — открывает меню.
- ↓ на пункте меню — перемещает фокус к следующему пункту меню или к первому, когда вы дошли до последнего пункта.
- ↑ на пункте меню — перемещает фокус к предыдущему пункту меню или к последнему, если вы находитесь на первом.
- ↑ на кнопке меню — закрывает меню, если оно открыто.
- Esc на пункте меню — закрывает меню и перемещает фокус на кнопку меню.
Преимущество перемещения фокуса между пунктами меню с помощью клавиш со стрелками состоит в том, что за Tab сохраняется функция выхода из меню. На практике это означает, что пользователям не нужно проходить через каждый пункт меню, чтобы выйти из меню. Это значительно улучшает юзабилити, особенно в меню, где много пунктов.
Добавление tabindex=»-1″ к пунктам меню делает их недоступными для фокуса с помощью Tab , но сохраняет возможность фокуса на элементах при нажатии на клавиши со стрелками.
Метод open Скопировать ссылку
Мы можем создать методы для обработки различных событий как часть продуманного дизайна API.
Например, метод open нужен для переключения значения aria-expanded на true , изменить значение меню hidden на false и сделать фокус на первом элементе меню с menuitem , который не скрыт:
MenuButton.prototype.open = function ()
Мы можем исполнить этот метод, когда пользователь нажимает клавишу вниз на кнопке меню, на которой сделан фокус:
this.button.addEventListener('keydown', function (e) < if (e.keyCode === 40) < this.open(); >>.bind(this));
Кроме того, разработчик, использующий этот скрипт, сможет теперь программно открывать меню:
exampleMenuButton = new MenuButton( document.querySelector('[aria-haspopup]') ); exampleMenuButton.open();
Примечание. Хак с чекбоксом Скопировать ссылку
Если вам не нужен JavaScript, то лучше не использовать его настолько, насколько это возможно. Использование третьей технологии поверх HTML и CSS всегда усложняет систему и приводит к появлению в ней слабых мест. Однако не все компоненты можно создать без использования JavaScript.
В случае с кнопками меню желание сделать их «работающими без JavaScript» привело к появлению так называемого хака с чекбоксом. В нём состояние checked (или unchecked ) скрытого чекбокса используется для переключения видимости элемента меню с помощью CSS.
/* Меню закрыто */ [type="checkbox"] + [role="menu"] < display: none; >/* Меню открыто */ [type="checkbox"]:checked + [role="menu"]
Для пользователей скринридеров роль чекбокса и состояние checked бессмысленны в этом контексте. Это можно частично исправить, добавив role=»button» для чекбокса.
К сожалению, это отменяет взаимодействие со встроенным состоянием checked , лишая нас обратной связи о состоянии элемента без JavaScript (что плохо, хотя в этом контексте это было бы так же связано с состоянием checked ).
Но это возможно имитировать с помощью aria-expanded . Нам просто нужно добавить наш лейбл к двум элементам со , как показано ниже.
Оба этих элемента визуально скрыты благодаря классу visually-hidden , но, в зависимости от того в каком состоянии мы находимся, только один из них скрыт от скринридера. То есть только одному задано свойство display: none и это определяется установленным (но не объявляемым) состоянием checked :
/* Класс, который скрывает визуально элементы со */ .vh < position: absolute !important; clip: rect(1px, 1px, 1px, 1px); padding:0 !important; border:0 !important; height: 1px !important; width: 1px !important; overflow: hidden; >/* Отобразить правильное состояние для скринридеров с помощью текущего состояния элемента */ [type="checkbox"]:checked + label .expanded-text < display: inline; >[type="checkbox"]:checked + label .collapsed-text < display: none; >[type="checkbox"]:not(:checked) + label .expanded-text < display: none; >[type="checkbox"]:not(:checked) + label .collapsed-text
Это разумное решение, но работа над нашей кнопкой меню всё ещё не закончена. Ожидаемое поведение фокуса, которое мы обсудили, просто не может быть реализовано без JavaScript.
Такое поведение принято и предсказуемо, оно делает кнопку более удобной. Однако, если вам действительно нужно сделать кнопку меню без JavaScript, то это самый подходящий вариант. Учитывая то, что урезанная кнопка навигационного меню, которую я рассмотрел ранее, предлагает контент меню, который не зависит от самого JavaScript (то есть ссылки), этот подход может быть подходящим вариантом.
Примечание: меню открывает только пробел.
Событие choose Скопировать ссылку
Исполнение некоторых методов должно вызывать события, чтобы мы могли настроить обработчики событий. Например, мы можем транслировать событие choose , когда пользователь кликает по пункту меню. Мы можем установить это с помощью CustomEvent , которое даёт нам возможность передавать аргумент в свойство события detail . В этом случае аргумент ( choice ) будет узлом DOM выбранного пункта меню.
MenuButton.prototype.choose = function (choice) < // Определить событие 'choose' var chooseEvent = new CustomEvent('choose', < detail: < choice: choice >>); // Передать событие (Dispatch the event) this.button.dispatchEvent(chooseEvent); return this; >
Есть много всего, что мы можем сделать с помощью этого механизма. Возможно, у нас есть live region с id со значением menuFeedback :
Теперь мы можем настроить обработчик событий и заполнить live region информацией, скрытой внутри события:
exampleMenuButton.addEventListener('choose', function (e) < // Получить текст узла (label) var choiceLabel = e.details.choice.textContent; // Получить узел live region var liveRegion = document.getElementById('menuFeedback'); // Заполнить live region liveRegion.textContent = `Your difficulty level is $`; >):

При выборе пункта меню пользователь скринридера услышит: «Вы выбрали [название пункта меню]». Изменения контента live region (которому задан здесь атрибут role=»alert» ) объявляются скринридерами при каждом его обновлении. Live region не является обязательным, но это пример того, что может произойти в интерфейсе, когда пользователь сделал выбор в меню.
Сохранение выбора Скопировать ссылку
Не все пункты меню нужны для выбора сохранения настроек. Многие из них ведут себя как стандартные кнопки, которые при нажатии приводят к тому, что что-то происходит в интерфейсе. Однако, в случае с нашей кнопкой меню со сложностью, мы хотели бы указать текущую настройку сложности. То есть ту, которая выбрана последней.
Атрибут aria-checked=»true» работает для элементов, которым вместо menuitem задана роль menuitemradio . Расширенная разметка со вторым выбранным элементом (установленным) выглядит так:
В нативных меню на многих платформах выбранные элементы отмечаются с помощью галочек. Мы можем это без проблем сделать, используя чуть больше CSS:
[role="menuitemradio"][aria-checked="true"]::before
При перемещении по меню с запущенным скринридером при фокусе на этом выбранном элементе он объявит что-то вроде: «Галочка, средний, пункт меню, выбрано».
Поведение при открытии меню с выбранным menuitemradio немного отличается. Вместо того, чтобы сделать фокус на первом (включённом) элементе в меню, фокус устанавливается на выбранном элементе.

Какая польза от такого поведения? Пользователь (любой) помнит о своём предыдущем выборе. В меню с многочисленными параметрами (например, с масштабом), люди, работающие на клавиатуре, находятся в оптимальном положении для изменения настроек.
Использование кнопки меню с помощью скринридера Скопировать ссылку
В этом видео я покажу вам как использовать кнопку меню в VoiceOver и Chrome. В примере используются элементы с ролью menuitemradio , атрибутом aria-checked и обсуждается поведение кнопки при фокусе. Таким образом ведут себя и другие популярные скринридеры.

Расшифровка Скопировать ссылку
Хейдон: Это кнопка меню со сложностью и я собираюсь протестировать её на VoiceOver в Chrome. Для начала я сделаю фокус на самой кнопке.
VoiceOver: Сложность, всплывающая кнопка.
Хейдон: Сначала вы услышите «Сложность» — это подпись. Затем «всплывающая кнопка», что означает её состояние, что ей задано area-expanded=»false» и что она всплывающая, потому что для неё установлено значение true в атрибуте aria-haspopup . Сейчас я открою меню с помощью клавиши со стрелкой вниз.
VoiceOver: Галочка, очень сложно, пункт меню, выбран, меню, пять пунктов.
Хейдон: Здесь много информации. Для начала «Галочка, очень сложная». Это подпись. Я использую псевдоэлемент для того, чтобы добавить галочку. «Пункт меню», потому что в этом случае это пункт меню с ролью menuitemradio . «Выбран», потому что задан атрибут aria-checked со значением checked . И также вы слышите, что в меню пять пунктов. Это потому, что этот пункт меню вместе с другими находится внутри элемента, которому задана роль menu . Сейчас я нажму на кнопку со стрелкой вниз, чтобы сделать фокус на пункте «Очень высокая».
VoiceOver: Очень высокая, пункт меню.
Хейдон: На этот раз вы услышали только подпись и роль элемента, потому что он очевидно не выбран. Для того, чтобы выбрать этот пункт, я нажимаю кнопку Enter .
VoiceOver: Сложность, всплывающая кнопка.
Хейдон: Это закрывает меню и перемещает фокус на саму кнопку меню «Сложность». Итак, вы вернулись туда, откуда начали.
Инклюзивная кнопка меню на Github Скопировать ссылку
Я и Хьюго Жирадель вместе работали над созданием компонента кнопки меню с фичами API, которые я описал, и ещё многим другим. Вы должны поблагодарить Хьюго за многие из этих фич, так как они основывались на работе над a11y-dialog — доступным модальным окном. Он есть на Гитхабе и в npm.
npm i inclusive-menu-button --save
Кроме того, Хьюго сделал специально для вас версию кнопки на React.
Чеклист Скопировать ссылку
- Не используйте семантику ARIA для меню в случае с системами навигационных меню.
- На сайтах с большим количеством контента не скрывайте структуру в выпадающих меню.
- Используйте aria-expanded у кнопок для того, чтобы сообщить о том, что навигационное меню открыто или закрыто.
- Убедитесь, что навигационное меню следующее в порядке фокуса после кнопки, которая его открывает и закрывает.
- Никогда не жертвуйте юзабилити ради решений без JavaScript. Это заносчиво.
Кнопка меню
В диалоге с пользователем бот может выводить меню. При нажатии на один из пунктов меню запускается определенный сценарий.
Такая логика реализуется с помощью связки: тригер Кнопка меню и элемент Показать меню.
- Триггер размещается в каждом сценарии, который будет запускаться из меню.
- Элемент добавляется в сценарий, в котором бот выводит меню пользователю.

Например, по умолчанию в готовых сценариях бота «Поддержка» добавлен триггер Кнопка меню . В стартовом сценарии бота размещен элемент Показать меню . При обращении на линию поддержки, бот отобразит для пользователя все вопросы, по которым он сможет помочь. Обратите внимание, если добавлено большое количество пунктов меню, темы отображаются в виде списка. Чтобы начать взаимодействие, пользователь может отправить номер пункта.

Настройки триггера
Разместите триггер в сценарии после ключевого интента и задайте название кнопки в меню.
Вы можете настроить несколько списков меню внутри одного бота, а также определить порядок отображения кнопок. Для этого наведите курсор на триггер, а затем на появившиеся рядом с его названием три точки и выберите значок . Откроется окно настройки.

- Категория — поле заполняется, если в одном боте необходимо выводить несколько меню со списком разных сценариев. Задайте общее название в каждом сценарии из одного меню. Затем при размещении элемента Показать меню укажите категорию сценариев, которые увидит пользователь в виде кнопок;
- Позиция — определите позицию кнопки по счету относительно других. Если поле будет пустым, или будут указаны повторяющиеся в рамках одного меню числа, кнопки будут отображаться в алфавитном порядке.
Сохраните изменения. Чтобы удалить триггер, наведите на него курсор, а затем нажмите на появившиеся рядом с его названием три точки и выберите значок .
Пример настройки нескольких меню
Рассмотрим применение нескольких настроенных меню на примере. В стартовом сообщении бота для персонала показаны все вопросы, по которым пользователь может обратиться к боту.

Предположим, что внутреннее обучение представлено в виде нескольких отдельных курсов, для каждого из которых создан свой сценарий. Чтобы пользователь мог выбрать курс, нажав на кнопку, разместим в каждом сценарии триггер Кнопка меню , укажем название, а также категорию, например, «Список направлений». Перейдем в сценарий «Внутреннее обучение» и добавим элемент Показать меню . В поле Категория введем название, указанное в каждом из направлений обучения. Теперь, когда пользователь выберет кнопку Внутреннее обучение , отобразится меню со списком доступных направлений.
