Links, please

Примеры, на которые ссылается пост, давно протухли, но смысл поста до сих пор актуален.

Однажды наступит тот день, когда самые продвинутые IT-компании научатся пользоваться ссылками. Тогда заживем.

Раз

Если вам в href очень хочется положить javascript: void(0);, # или еще какую-нибудь фигню, успокойтесь, глубоко вздохните и замените ссылку на button.

Кнопка воспринимает событие click, стили :hover и :active и попадает в общий tabindex точно так же, как и ссылка. Невероятно!

Можно также использовать любой другой тег, например span, но он не будет ловить фокус, а событие click не будет вызываться по нажатию энтера, что убивает все преимущества кнопки в плане доступности использования.

Можно добавить спану аттрибут tabindex="0" и повесить обработчики на нажатие энтера, а можно не париться и сделать button, который еще и семантически более уместен.

Два

Если вам очень хочется запихнуть в ваш элемент onclick="location.href='...'", остановитесь, вздохните еще раз и сделайте ссылку. Теперь вашу ссылку можно открыть в новой вкладке, вызвать контекстное меню браузера с кучей функций, а адрес ссылки будет виден при наведении мыши. Поразительно!

Три

Единственный случай, когда можно (и нужно) вешать обработчик на ссылку — если есть что показать по ссылке. При этом важно, чтобы обработчик срабатывал только при нажатии левой кнопки мыши без кнопок-модификаторов.

Классический пример — превьюшки фоток. При нажатии на превьюшку показывается большая фотка поверх текущего контента. При клике колесом или ctrl-клике фотка открывается в новой вкладке. Контекстное меню тоже работает. Юзеры с отключенным / сломанным яваскриптом получают рабочую ссылку на фотку. Все довольны:

HTML:

<a href="photo.jpg" class="preview">
<img src="p_photo.jpg">
</a>

JS + jQuery:

$('.preview').click(function(event) {
// If it's not LMB or if a modifier key is pressed – do nothing.
if (
event.button !== 0 ||
event.shiftKey ||
event.altKey ||
event.ctrlKey ||
event.metaKey
) {
return;
}

// Otherwise prevent the default action
event.preventDefault();

// and execute our function (e. g. show photo)
alert('Function executed');
});

Результат:

ИЕ версии 8 и ниже в событие click всегда возвращает button равный нулю. В результате клик колесом также приведет к выполнению функции, но все остальное будет работать корректно. Я бы с этим не заморачивался, но если очень хочется, можно заморочиться. Не стоит, ИЕ8 давно мертв.

Хорошие ребята

Есть много других ситуаций, в которых также очень желательно оставлять функционал ссылки. Смотрите, как делают хорошие ребята:

Переключение страниц

Пагинатор комментариев на vk.com переключает страницы без перезагрузки, но оставляет возможность открыть страницу в новой вкладке.

Форма логина / регистрации

На реддите форма логина и регистрации открывается в попапе поверх текущей странице, если кликнуть левой кнопкой мыши, и на новой вкладке, если нажать колесом. Правда, они не учли кнопок-модификаторов.

Дополненный функционал

Случайно наткнулся на этот пример, когда на метакритике отказались загружаться скрипты. Кнопка Expand под пользовательскими отзывами — ссылка на ту же страницу с GET-параметром, например ?user_review_id=1713311. При отключенном или сломанном (как в моем случае) яваскрипте сервер отдаст вам страницу с раскрытым отзывом пользователя. Отличный пример ненавязчивого яваскрипта. Грамотный фоллбек на серверную часть сохранил функционал сайта при упавшем CDN-сервере.

Плохие ребята

Twitter

Твиттер встал на путь истины и стал потихоньку исправлять свои косяки. Профиль юзера можно открыть в новой вкладке — гут. Кнопочка refresh в сайдбаре и кнопка нового твита используют button — тоже гут (а можно было и ссылку на форму нового твита сделать).

Тем не менее, постоянно попадаются <a href="#">. Ссылки reply, retweet и favourite никуда не ведут, а могли бы.

Google+

Та же ситуация, что и с Твиттером. В целом, ребята исправляются (раньше все было очень плохо), тем не менее на главной странице менюшка переключения кругов по-прежнему использует data-dest="stream/circles/p4765f1c30e7d2c98" вместо нормальной ссылки, а аватарка в верхнем правом углу завернута в ссылку с моим любимым javascript:void(0).

Instagram

Все плохо. javascript:; в кнопках, ни одна ссылка не открывается в новой вкладке. Ребята запилили навигацию через хистори стейты, но о вкладках не особо слышали. Ведь одной вкладки для среднего хипстера вполне достаточно, да?

TL;DR

Используйте button для действий на странице, a для ссылок и для действий с фоллбеком, доступным по ссылке.

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

Ссылки по теме