Результат данного примера показан на рис. 1. При получении фокуса текстовое поле меняет цвет фона и цвет границы.
Рис. 1. Результат использования псевдокласса :focus
Браузеры
Chrome не добавляет стиль для селектора a , чтобы заставить его понимать правило a:focus , добавьте к тегу атрибут tabindex .
CSS по теме
Стилизуйте состояния hover, focus и active по-разному
Много лет я стилизовал hover, focus и active одинаково. Я даже не могу вспомнить, когда я начал это делать таким образом. Вот пример кода, который я обычно использую:
// Not the best approach. I'll explain why in this article
.selector &:hover,
&:focus,
&:active // Styles here
>
>
Как только я начал уделять больше внимания доступности (и как следствие уделять больше внимания состоянию focus), я начал понимать, что мы не должны стилизовать состояния hover, focus и active одинаковым образом.
Состояния hover, focus и active не должны быть стилизованы одинаково.
Вот простая причина для этого: это разные состояния!
Сегодня, я хотел бы показать вам магический способ стилизовать эти три состояния без особых усилий.
Давайте начнем с hover.
Стилизация состояния hover
Состояние hover инициируется, когда пользователь наводит свою мышь на элемент.
Состояние hover обычно представлено изменениями background-color (и\или color). Разница стилей между состоянием hover и его отсутствием не обязательно должна быть ярко выраженная, потому что пользователь уже знает, что он навел мышку на что-то.
button background-color: #dedede;
>button:hover background-color: #aaa;
>
Стилизация состояния focus
focus активируется, когда происходит фокусировка на элементе. Это может происходить в одном из двух случаев:
- Когда пользователь переключается с помощью кнопки tab между элементами
- Когда пользователь кликает на элемент, который поддерживает состояние focus
Следующие элементы поддерживают состояние focus:
Пара важных моментов:
- Пользователь не может переключиться с помощью кнопки tab на элемент со свойством tabindex=”-1″, но он может кликнуть на него, тем самым переключив элемент в состояние focus
- В браузерах Safari и Firefox (на Mac) клик на элемент button не вызывает состояние фокусировки. Подробнее по этой ссылке https://zellwk.com/blog/inconsistent-button-behavior/
- Когда вы нажимаете на элемент , фокус остается на до тех пора, пока вы не уберете палец с мыши. Когда вы все же уберете палец, фокус будет смещен в другой элемент, если атрибут href указывает валидный id элемента на вашей странице.
В случае состояния focus нас больше интересует, когда пользователь переключается между элементами с помощью кнопки tab, нежели когда он кликает на них мышкой.
Когда пользователь жмет tab, он не знает на какой элемент будет смещен фокус. Он может только догадываться. И поэтому нам требуется явное изменение стилей, чтобы пользователь заметил, что изменился фокус.
Использование стилизации по умолчанию состояния focus в большинстве случаев достаточно. Если же вы хотите сделать свой собственный дизайн для состояния focus, то подумайте о следующих вещах:
- Использование css свойства outline.
- Создание анимации с движением.
- Изменение css свойства background-color.
- Изменение css свойства color.
Так как зачастую css свойства background-color и color используются для состояния hover, то имеет смысл использовать css свойство outline или анимации для состояния focus.
Вы можете комбинировать свойства outline, border и box-shadow, чтобы создать хорошо выглядящие стили для focus. Я писал об этом в статье “Создание своих стилей для focus” https://zellwk.com/blog/creating-focus-style/
button background-color: #dedede;
>button:hover background-color: #aaa;
>button:focus outline: none;
box-shadow: 0 0 0 3px lightskyblue;
>
Стилизация состояния active
Когда вы взаимодействуете с различными вещами в реальной жизни, вы ожидаете ту или иную обратную связь. Например, если вы жмете кнопку, то вы ожидаете от кнопки быть нажатой.
Обратная связь так же полезна на веб-сайтах. Вы можете стилизовать нажатие кнопки с помощью состояния active. Состояние active инициируется, когда вы взаимодействуете с элементами. Под взаимодействием понимается следующее:
- Удержание левой кнопки мыши на элементе (даже на тех, которые не поддерживают состояние focus).
- Удержание кнопки пробела (на элементах button).
button:active background-color: #333;
border-color: #333;
color: #eee;
>
Два странных момента, на которые нужно обратить внимание:
- Удержание кнопки пробел вызывает состояние active на элементах button, но удержание кнопки ввод этого не делает.
- Нажатие на кнопку ввод активирует переход по ссылке, но не меняет их состояние на active. Нажатие на пробел не делает этого вообще.
Стили по умолчанию для ссылок
Ссылки имеют свой стиль по умолчанию. Они окрашиваются в красный цвет, когда вы по ним переходите.
Связь между состояниями active и focus
Когда вы зажимаете левую кнопку мыши на элементе, который поддерживает состояние focus, то вы переводите его в состояние active. В тоже время вы так же вызываете состояние focus.
Это условие работает для всех элементов, на которых возможна фокусировка, кроме ссылок и кнопок.
Для ссылок:
- Когда вы зажимаете левую кнопку мыши: инициализируются состояния active и focus в браузерах Firefox и Chrome, но в браузере Safari (проверено только на Mac) инициализируется только состояние active.
- Когда вы отпускаете левую кнопку мыши: состояние focus остается на ссылке (кроме тех случаев, когда ссылка ведет на элемент на той же самой странице), но в браузере Safari фокус переходит обратно на элемент body.
Для кнопок:
- Когда вы зажимаете левую кнопку мыши: инициализация состояний active и focus происходит только в браузере Chrome. Состояние focus не инициализируется при тех же самых действиях в браузерах Safari и Firefox (Mac). Я писал об этом странном поведении https://zellwk.com/blog/inconsistent-button-behavior/
Если вы хотите, чтобы клик на кнопку переводил ее в состояние focus, то вам нужно использовать следующий JavaScript как можно раньше (зачем это делать, вы можете прочитать по ссылке выше).
document.addEventListener('click', event => if (event.target.matches('button')) event.target.focus()
>
>)
Как только вы использовали этот код, клик на кнопках будет вызывать следующее поведение:
- Когда вы зажимаете левую кнопку мыши: инициализуется состояние active во всех браузерах. При этом focus происходит только в Chrome.
- Когда вы отпускаете левую кнопку мыши: переключается состояние focus в браузерах Safari и Firefox (Mac). Фокус остается на кнопках в остальных браузерах.
Поведение кнопки в браузере Safari после добавления приведенного выше фрагмента JavaScript
Теперь вы знаете все о состояниях hover, focus и active. Давайте поговорим о их стилизации.
Магическая комбинация
Следующая магическая комбинация позволяет пользователю получать обратную связь, когда происходит изменение состояний hover, focus и active, и взаимодействовать с элементами.
Вот тот кусочек css, который вам потребуется:
.element:hover,
.element:active /* Change background/text color */
>.element:focus /* Show outline /*
>
Для пользователей мыши:
- Когда пользователь наводит на элемент, то меняются свойства background-color и\или color. Таким образом он получают обратную связь.
- Когда пользователь кликает на элемент, используется свойство outline. Таким образом он получают обратную связь.
Для пользователей клавиатуры:
- Когда пользователь переключается между элементами с помощью tab, то используется свойство outline. Таким образом он получают обратную связь.
- Когда пользователь взаимодействует с элементов, то меняются свойства background-color и\или color. Таким образом он получают обратную связь.
Лучшее из двух миров!
- Я не очень тщательно проверял эту магическую комбинацию. Это лишь доказательство концепции. Я буду благодарен, если вы поможете мне различными тестами и дадите мне знает, как она будет использоваться в реальной жизни.
- Для ваших тестов не используете Codepen. Поведение состояния focus очень странно в Codepen. Если вы наведете на ссылку, то контур (outline) ссылки будет удален. Почему? Я не знаю. Иногда я думаю, что лучше проверять подобные вещи без всяких модных инструментов. Используйте старый добрый HTML, CSS и JS.
Не магическая (но возможно более лучшая) комбинация
Как я уже упоминал ранее, нажатия на кнопки могут провоцировать странное поведение в браузерах Safari и Firefox (Mac). Если вы добавите фрагмент JavaScript, который я ранее показал вам, то магическая комбинация будет работать. Однако, это неидеальное решение.
Вот что происходит в случае Safari и Firefox (Mac):
- Когда пользователь зажимает левую кнопку мыши, ничего не меняется.
- Когда пользователь отпускает левую кнопку мыши, то происходит фокусировка на элементе.
Если для вас этого достаточно, то тогда магическая комбинация работает достаточно хорошо. Вам больше ничего не требуется.
Но если вы считаете иначе, то вам необходимо стилизовать состояния hover, focus и active независимо друг от друга.
.element:hover /* Change background/text color */
>.element:active /* Another change in background/text color */
>.element:focus /* Show outline /*
>
Поведение кнопки в Safari, если вы применили все три стиля.
И это все! Надеюсь, что вы выучили сегодня что-то полезное.
Спасибо за чтение. Помогла ли вам эта статья? Если да, то я надеюсь, вы поделитесь ей. Это может помочь кому-то еще. Большое спасибо!
Управление фокусом и атрибут inert
Множество вспомогательных технологий используют навигацию с клавиатуры для помощи в восприятии и взаимодействии с контентом. Один из способов подобной навигации — клавиша Tab. Возможно, вы знакомы с ним, если используете Tab для быстрого перемещения между полями формы, не дотягиваясь до мышки или трекпада.
Tab будет перемещаться по интерактивным элементам в том порядке, в котором они отображаются в DOM. Вот почему так важно, чтобы порядок исходного кода соответствовал визуальной иерархии вашего дизайна.
Список интерактивных элементов, по которым можно пройтись клавишей Tab:
- Ссылки с заполненным атрибутом href ;
- ;
- и с сопутствующим им ;
- ;
- ;
- и при наличии контролов;
- , в зависимости от того, как он используется;
- любой элемент с overflow: scroll в Firefox;
- любой элемент с атрибутом contenteditable ;
- любой элемент с установленным атрибутом tabindex (о нём чуть позже).
Интерактивный элемент получает состояние фокуса, когда:
- На него переходят с помощью клавиши Tab,
- Он является ссылкой и на него кликают,
- Фокус программно задан с помощью element.focus() в JavaScript.
Фокус похож на ховер, поскольку так мы определяем элемент, с которым хотим провзаимодействовать. Вот почему визуально очевидные стили для фокуса имеют огромное значение.
Фокус следует по домашней странице. Начиная с логотипа, затем к товарам, услугам, вакансиям, блогу, контактам и останавливается на кнопке «Learn more».
Управление фокусом Скопировать ссылку
Управлять фокусом — значит определять, какие элементы могут его получать, а какие нет. Это один из самых сложных аспектов при разработке веб-интерфейсов, но он важен для доступности сайтов и приложений.
Полезные практики управления фокусом Скопировать ссылку
В 99% случаев вам стоит оставить порядок фокуса в покое. Не устану это повторять.
Состояние фокуса будет работать без дополнительных усилий, если вы используете для кнопок, для ссылок, для полей форм и т. д.
В редких случаях вам может понадобиться добавить фокус к элементам, к которым обычно событие focus не применимо. Вот некоторые рекомендации относительно того, как реализовать это доступно и интуитивно понятно для пользователя.
✅ Следует: узнать побольше про атрибут tabindex Скопировать ссылку
tabindex делает элементы фокусируемыми. В качестве значения он принимает число, в зависимости от которого меняется поведение фокуса.
❌ Не следует: устанавливать tabindex=»0″ там, где это не надо Скопировать ссылку
Нет необходимости устанавливать tabindex для интерактивных элементов, которые могут получать фокус с клавиатуры (например, ). Кроме того, вам не нужно прописывать tabindex неинтерактивным элементам, чтобы их могли прочесть вспомогательные устройства (на самом деле, отсутствие роли и доступного имени является ошибкой с точки зрения WCAG). На самом деле, это даже усложняет навигацию для тех, кто использует вспомогательные устройства. У таких пользователей уже есть другие, ожидаемые ими способы чтения контента.
✅ Следует: устанавливать tabindex=»-1″ для фокуса с помощью JavaScript Скопировать ссылку
Атрибут tabindex=»-1″ используется для создания доступных интерактивных виджетов посредством JS. Прописав tabindex=»-1″ , вы сделаете элемент фокусируемым для JS, а также по клику или тапу, но недоступным через клавишу Tab.
❌ Не следует: использовать положительное значение tabindex Скопировать ссылку
Это антипаттерн. Установив положительное значение tabindex , вы переопределите ожидаемый порядок элементов для фокуса через Tab и запутаете пользователя. Сделать так один раз — уже плохо, несколько — полный кошмар. Серьёзно, не надо так.
❌ Не следует: создавать собственный порядок фокусировки Скопировать ссылку
Пройтись по интерактивным элементам можно только в том порядке, в котором они следуют друг за другом. Не следует создавать множество tabindex со значением, которое увеличивается для каждого последующего элемента, в соответствии с вашим представлением о том, как пользователь должен читать ваш сайт. Позвольте DOM сделать это за вас.
Ловушка фокуса Скопировать ссылку
Иногда есть необходимость запретить состояние фокуса. Хороший пример запрета — это ловушка фокуса, которая временно ограничивает событие фокуса у родительского элемента и у его дочерних элементов.
Ловушку фокуса не стоит путать с ловушкой клавиатуры. Ловушки клавиатуры — это ситуации, когда невозможно закрыть виджет или перейти к другому компоненту из-за вложенного цикла плохо прописанной логики.
Практический пример того, как вы могли бы использовать ловушку фокуса — это модальное окно:
Фокус проходит по странице и открывает модальное окно, чтобы продемонстрировать отмену фокуса. Далее фокус двигается в рамках контента модального окна, на кнопку «Play», кнопку «Cancel», кнопку «Purchase» и кнопку закрытия (всё это время фокус на странице заблокирован). После закрытия модального окна он возвращается к исходному положению на странице до его открытия.
Почему это важно? Скопировать ссылку
Удержание фокуса в пределах модального окна помогает понять, что является его контентом, а что нет. Это аналогично тому, как зрячий человек может видеть, как окно всплывает поверх контента сайта или приложения. Это важная информация, если:
- У вас плохое зрение или слепота и вы полагаетесь на скринридер, чтобы узнать об изменениях после взаимодействия со страницей;
- У вас плохое зрение и интерфейс увеличен с помощью экранной лупы, при которой фокусировка за пределами модального окна может сбить с толку и дезориентировать;
- Вы перемещаетесь исключительно с помощью клавиатуры и, в случае закрытия модального окна, можете потеряться на странице при попытке вернуться обратно к нему.
Как это сделать? Скопировать ссылку
Надёжно управлять фокусом — дело сложное. Нужно прибегнуть к JavaScript, чтобы:
- Определить родительский блок для всех фокусируемых элементов на странице;
- Определить границы содержимого ловушки фокуса (например, модального окна), включая первый и последний фокусируемый элемент;
- Убрать как интерактивность, так и видимость всего, что может иметь фокус и находится вне рамок содержимого ловушки фокуса;
- Переместить фокус на содержимое ловушки фокуса;
- Обрабатывать события, сигнализирующие об уходе с выделенной области (сохранение, отмена, нажатие Esc и так далее);
- Выйти из содержимого ловушки фокуса, когда сработает нужное событие;
- Вернуть раннее отменённую интерактивность;
- Переместить фокус обратно на интерактивный элемент, который вызвал ловушку фокуса.
Зачем нам это? Скопировать ссылку
Не стану врать: все эти действия отнимают много времени. Но всё же, управление фокусом и удобный порядок фокусировки являются частью WCAG (руководства по обеспечению доступности веб-контента). Тема достаточна важна, чтобы считать её частью международного правового стандарта о юзабилити.
Видимость Скопировать ссылку
Существует небольшой трюк, с помощью которого можно легко ограничить видимость и интерактивность элемента.
У скринридеров есть режим взаимодействия, который позволяет им проходить по странице или просматривать её с помощью виртуального курсора. А ещё виртуальный курсор позволяет пользователю скринридера обнаруживать неинтерактивные части страницы (заголовки, списки и т. д.). В отличие от использования Tab, виртуальный курсор доступен только пользователям скринридера.
При управлении фокусом может потребоваться ограничить возможность обнаружения содержимого виртуальным курсором. В нашем примере с модальным окном это значит предотвратить случайный выход пользователя за рамки окна при чтении контента.
Видимость может быть заблокирована с помощью правильного применения атрибута aria-hidden=»true» . А вот с интерактивностью есть один тонкий нюанс.
Знакомство с inert Скопировать ссылку
Атрибут inert — глобальный атрибут в HTML, позволяющий легко убирать и восстанавливать видимость интерактивных элементов, а также их возможность получать состояние фокуса. Вот пример:
Save changes?
The changes you have made will be lost if you do not save them.
Я намеренно избегаю использования для модального окна из-за многочисленных проблем с поддержкой.
Атрибут inert задан элементу , следующему сразу после модального окна сохранения изменений. Теперь всё содержимое не может попасть в фокус и по нему нельзя кликнуть.
Фокус ограничен рамками модального окна. Когда мы закрываем его, то убираем inert из . Этот способ управления ловушкой фокуса проще, чем другие методы.
Помните: событие закрытия модального окна может быть вызвано не только нажатием на кнопки внутри нашего модального окна из примера, но и при нажатии на Esc. А ещё некоторые модальные окна могут быть закрыты по клику на оверлей.
Поддержка inert Скопировать ссылку
Все последние версии Edge, Chrome и Opera поддерживают inert , если включены экспериментальные функции веб-платформ. Также скоро появится его поддержка в Firefox! Единственное исключение — Safari, как мобильный, так и десктопный.
Мне бы очень хотелось, чтобы Apple реализовала встроенную поддержку inert . Хоть полифил и существует, но у него серьёзные проблемы с поддержкой скринридерами. Нехорошо!
Вдобавок хочется обратить внимание на пометку в README полифила:
Полифил обойдётся вам дорого с точки зрения производительности, в отличие от нативного inert , т. к. он требует изрядного количества перемещений по DOM.
Перемещение по DOM подразумевает, что JavaScript в полифиле требует высокой вычислительной мощности и, следовательно, в конечном итоге замедлит использование.
Для устройств с низким энергопотреблением, таких как бюджетные Android-смартфоны и устаревшие ноутбуки, а также тех, что выполняют сложные задачи (например, запуск нескольких приложений сразу), это может привести к зависанию или сбоям. Нативная браузерная поддержка делает такие процессы менее затратными в этом плане, так как у браузера есть доступ ко всем частям DOM, в отличие от JS.
Safari Скопировать ссылку
Лично я разочарован тем, что Apple не поддерживает inert . Хотя понимаю, что добавление новых функций в браузер — невероятно сложная и трудная работа. inert кажется функцией, которую Apple стоило бы начать поддерживать гораздо раньше.
macOS и iOS всегда имели хорошую поддержку доступности, а поддержка вспомогательных технологий — важная часть их маркетинговой кампании. Поддержка inert представляется как естественное продолжение миссии Apple ввиду того, что сама эта функция смогла бы облегчить разработку доступных веб-интерфейсов в разы.
К сожалению, Apple держит в тайне, когда появится поддержка этого атрибута. Поэтому поддержка inert — всё ещё открытый вопрос.
Igalia Скопировать ссылку
Igalia — компания, работающая над функциями браузеров. Сейчас они проводят эксперимент, в котором каждый может проголосовать за те возможности браузеров, которые ему хотелось бы видеть. Конечно, моя статья совсем не про это, но вы можете узнать больше в Smashing Magazine.
Одно из предложений Igalia — это добавление поддержки inert в WebKit. Если вы искали возможность принять участие в улучшении доступности веба, но не знали с чего начать, я могу вам её предоставить. 5 $, 10 $, 25 $ — совсем необязательно жертвовать большие суммы, даже маленький вклад ценен.
Итог Скопировать ссылку
Управление фокусом требует некоторых навыков и осторожности, но это того стоит. Атрибут inert значительно облегчит эту задачу.
Такие техники, как inert , являются примером одной из самых сильных сторон веба: способности кодифицировать неожиданное поведение в нечто простое и эффективное.
Что почитать Скопировать ссылку
- Controlling focus with tabindex (A11ycasts, эпизод 4).
- Using the tabindex attribute (The Paciello Group).
- Using JavaScript to trap focus in an element (Хидде де Врис).