Не ставить фокус по клику

Проблема заключалась в следующем: когда имеется интерактивный элемент с :focus стилем и вы щелкаете по этому элементу, на нём остаётся focus стиль (outline обводка). У нативной кнопки всё работает as expected, но стоит её добавить любой стиль и, как побочный эффект, меняется её поведение.

<button >Без фокуса после клика</button>
<button style="background-color: lightgray;">Фокус после клика</button>

Конечно outline можно убрать, но с интерактивными элементам так делать не стоит, так как он обеспечивает визуальную обратную связь для элементов, которые имеют «фокус» при навигации по документу с помощью клавиши TAB (или эквивалентной). Это особенно полезно для людей, которые не могут использовать мышь или имеют нарушения зрения. Если вы удалите outline, вы сделаете свой сайт недоступным для этих людей.

Решение на JS

Отмена действия браузера по-умолчанию. Есть два способа отменить действие браузера. Основной способ – это воспользоваться объектом event. Для отмены действия браузера существует стандартный метод event.preventDefault(). Если же обработчик назначен через on<событие> (не через addEventListener), то также можно вернуть false из обработчика.

В следующем примере при клике по ссылке переход не произойдёт:

<a href="/" onclick="return false">Нажми здесь</a>
или
<a href="/" onclick="event.preventDefault()">здесь</a>

Нажми здесь или здесь

Для события mousedown на кнопке действием браузера по-умолчанию является установка фокуса. Если отменить событие mousedown, то фокусирования не произойдёт.

Результат

document
  .querySelector('#custom')
  .addEventListener('mousedown', e => e.preventDefault());

Возможность установить фокус через Tab сохранена.

Еще одно решение на JS

Если есть причина не отменять действие браузера по-умолчанию, то можно сделать ручное управление фокусом. Идея следующая:

const isMouseDown = false; // в примере будет работать только для 1ой кнопки
const button = document.querySelector('button');
button.addEventListener('mousedown', () => (isMouseDown = true));
button.addEventListener('mouseup', () => (isMouseDown = false));
button.addEventListener('focus', () => {
    if (isMouseDown) {
      button.blur();
    }
};

Как там у других

  1. United States Web Design System - фокус остаётся
  2. United Kindom Design System - фокус остаётся
  3. Microsoft Design System - фокус НЕ остаётся
  4. Carbon Design System - фокус остаётся
  5. SalesForce Design System - фокус остаётся
  6. Контур - фокус НЕ остаётся
  7. Альфа - фокус НЕ остаётся

Нативное поведение повторяют не все, даже государственные дизайн-системы. Мне кажется это не есть гуд.


Похожие записи

RxJS. Delay from array

import { of, from } from 'rxjs'; 
import { map, concatMap, delay } from 'rxjs/operators';

from([2,4,6,8]).pipe(
  concatMap(item => of(item).pipe(delay(1000)))
).subscribe(console.log);