От автора: недавно на работе была поднята тема того, как делается блокировка ссылок. В прошлом году каким-то образом к стилям наших шрифтов был добавлен якорь disabled, пока я этого не видел. Есть проблема: в HTML нет реального способа заблокировать тег a (с валидным атрибутом href). А зачем вообще это делать? Ссылки лежат в основе веба.
В какой-то момент я понял, что моим коллегам это не понравится, поэтому я начал думать, как реализовать блокировку ссылок. Понимая, что это потребует много времени, я хотел доказать, что затея не стоит тех усилий и кода, который придется поддерживать. Но я боялся, что если покажу, что это все-таки сделать можно, они проигнорируют все мои предупреждения и просто используют мой пример как доказательство того, что все в порядке. Меня это не потрясло, но я подумал, что вам может пригодиться мое исследование.
Во-первых:
Просто не делайте этого
Заблокированная ссылка уже не ссылка, это текст. Вам нужно пересмотреть дизайн, если в нем есть заблокированные ссылки.
В BootStrap есть примеры применения класса .disabled к якорям, я их ненавижу. По крайней мере, они сказали, что этот класс всего лишь добавляет стиль disabled. Формулировка вводит в заблуждение. Чтобы реально отключить ссылку, недостаточно просто придать ей вид отключенной ссылки.
Надежный способ: удалите href
Если вы приняли решение игнорировать мои предупреждения и все еще хотите заблокировать ссылку, то лучший способ, который я знаю – удалите href атрибут.
Из спецификации Hyperlink: «Атрибут href в тегах a и area необязателен; когда атрибут отсутствует, эти элементы не создают гиперссылки.»
У MDN определение попроще: «Для создания плейсхолдер ссылки этот атрибут можно пропустить (в HTML5). Плейсхолдер ссылка похожа на обычную ссылку, но она никуда не ведет.»
Стандартный JS код вставки и удаления атрибута href:
1 2 3 4 5 6 7 8 9 10 11 |
/* * Use your preferred method of targeting a link * * document.getElementById('MyLink'); * document.querySelector('.link-class'); * document.querySelector('[href="//unfetteredthoughts.net"]'); */ // "Disable" link by removing the href property link.href = ''; // Enable link by setting the href property link.href = '//unfetteredthoughts.net'; |
Стилизовать через CSS тоже довольно просто:
1 2 3 4 5 6 |
a { /* Disabled link styles */ } a:link, a:visited { /* or a[href] */ /* Enabled link styles */ } |
Этого мало, хочу что-то сложнее, чтобы казаться умнее!
Если хотите усложнить, то вот на что стоит смотреть. Надеюсь, вы поймете, что то, что я хочу вам показать, не стоит затраченных усилий.
Во-первых, ссылку необходимо стилизовать так, чтобы она выглядела отключенной.
1 2 3 4 5 6 |
.isDisabled { color: currentColor; cursor: not-allowed; opacity: 0.5; text-decoration: none; } |
1 |
<a class="isDisabled" href="//unfetteredthoughts.net">Disabled Link</a> |
Установка color в значение currentColor должна сбросить цвет шрифта к обычному, не ссылочному. Я также задал курсор not-allowed – при наведении на элемент показывается иконка, что взаимодействие невозможно. Мы упустили пользователей, у которых нет мыши, которые не могут навести курсор на элемент, которые в основном касаются экрана и клавиатуры. Далее необходимо прозрачность задать на половину. Согласно WCAG, отключенные элементы не обязаны соблюдать правила цветового контраста. Думаю, это очень рискованно, так как сейчас это просто текст, а снижение прозрачности на половину сильно затрудняет чтение пользователям с плохим зрением – еще одна причина, почему я ненавижу блокировку ссылок. И наконец, удаляем подчеркивание текста, так как подчеркивание явный признак ссылки. Теперь ссылка выглядит как заблокированная!
Но не совсем! Пользователь все еще может кликнуть, нажать пальцем на нее. Я уже слышу, как вы кричите pointer-events.
1 2 3 4 |
.isDisabled { ... pointer-events: none; } |
ОК, закончили! Ссылка отключена! Отключена только для пользователей с мышью и пользователей сенсорных устройств. А если браузер не поддерживает pointer-events? Согласно caniuse поддержка отсутствует в Opera Mini и IE<11. IE11 и Edge не поддерживают pointer-events, если display не задан в block или inline-block. Установка pointer-events в none переписывает наш курсор not-allowed, и пользователи с мышью опять будут думать, что ссылка рабочая. Все уже начинает разваливаться. Теперь необходимо поменять разметку и CSS…
1 2 3 4 5 6 7 8 9 10 |
.isDisabled { cursor: not-allowed; opacity: 0.5; } .isDisabled > a { color: currentColor; display: inline-block; /* For IE11/ MS Edge bug */ pointer-events: none; text-decoration: none; } |
1 |
<span class="isDisabled"><a href="//unfetteredthoughts.net">Disabled Link</a></span> |
Оборачивание ссылки в тег span и присвоение ему класса isDisabled дает нам половину стилей disabled. Забавно, но теперь класс disabled стал общим, и его можно использовать на других элементах (кнопках и элементах формы). Теперь реальная ссылка имеет pointer-events и text-decoration заданные в none.
А что с пользователями клавиатуры? Пользователи с клавиатурой будут активировать ссылку по ENTER. pointer-events только для курсоров, keyboard-events нет. Также нужно предотвратить активацию для старых браузеров без поддержки pointer-events. Теперь нужно подключить JS.
Подключаем JS
1 2 3 4 5 6 |
// After using preferred method to target link link.addEventListener('click', function (event) { if (this.parentElement.classList.contains('isDisabled')) { event.preventDefault(); } }); |
Теперь наша ссылка выглядит отключенной и не реагирует на активацию по клику, нажатию и ENTER. Но и это не все! Пользователи скрин ридеров не знают, что эта ссылка отключена. Необходимо дать описание, что ссылка отключена. Атрибут disabled невалиден на ссылках, но мы можем указать aria-disabled=»true».
1 |
<span class="isDisabled"><a href="//unfetteredthoughts.net" aria-disabled="true">Disabled Link</a></span> |
Сейчас я стилизую ссылку по атрибуту aria-disabled. Люблю использовать ARIA атрибуты как хуки для CSS, так как присутствие неправильно стилизованных элементов является индикатором отсутствия важной доступности.
1 2 3 4 5 6 7 8 9 10 |
.isDisabled { cursor: not-allowed; opacity: 0.5; } a[aria-disabled="true"] { color: currentColor; display: inline-block; /* For IE11/ MS Edge bug */ pointer-events: none; text-decoration: none; } |
Теперь наша ссылка выглядит отключенной, ведет себя как отключенная и описана как отключенная.
К сожалению, даже если ссылка описана отключенной, некоторые скрин ридеры (JAWS) будут говорить, что она кликабельна. Они делают это для всех элементов, у которых есть обработчик события клика. Они делают это из-за тенденции разработчиков превращать неинтерактивные элементы типа div и span в псевдоинтерактивные с простым обработчиком. Здесь мы ничего сделать не можем. Мы не смогли обмануть вспомогательную технологию удалением всех признаков ссылки. Что еще ироничнее, потому что мы пытались обмануть ее раньше.
А что если переместить обработчик в body?
1 2 3 4 5 6 |
document.body.addEventListener('click', function (event) { // filter out clicks on any other elements if (event.target.nodeName == 'A' && event.target.getAttribute('aria-disabled') == 'true') { event.preventDefault(); } }); |
Получилось? Не совсем. В какой-то момент нам понадобится активировать эти ссылки. Поэтому необходимо написать код, который будет переключать состояние/поведение.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
function disableLink(link) { // 1. Add isDisabled class to parent span link.parentElement.classList.add('isDisabled'); // 2. Store href so we can add it later link.setAttribute('data-href', link.href); // 3. Remove href link.href = ''; // 4. Set aria-disabled to 'true' link.setAttribute('aria-disabled', 'true'); } function enableLink(link) { // 1. Remove 'isDisabled' class from parent span link.parentElement.classList.remove('isDisabled'); // 2. Set href link.href = link.getAttribute('data-href'); // 3. Remove 'aria-disabled', better than setting to false link.removeAttribute('aria-disabled'); } |
Теперь все. Теперь наша ссылка отключена визуально, функционально и семантически для всех пользователей. Это потребовало всего лишь 10 строк CSS, 15 строк JS (в том числе 1 обработчик в body) и 1 HTML тега.
Серьезно, ребят, не делайте этого.
Автор: Gerard Cohen
Источник: //css-tricks.com/
Редакция: Команда webformyself.
Комментарии (2)