Увімкнути: зосередити увагу лише на використанні клавіатури (або натисканні табуляції)


79

Я хочу відключити, :focusколи це не потрібно, бо мені не подобається, як виглядає моя навігація, коли на ньому фокус. Він використовує той самий стиль, що .activeі бентежить. Однак я не хочу позбуватися цього для людей, які користуються клавіатурою.

Я думав додати клас enabled-focusна тілі при натисканні на вкладки, а потім створити, body.enabled-focus a:focus{...}але це додало б багато додаткового CSS для кожного елемента, який має фокус. Потім видаліть цей клас із тіла за допомогою першої миші вниз.

Як би я це зробив? Чи є краще рішення?


Додайте в jquery прослуховувач подій для певних клавіш, які ви хочете, і якщо вони натиснуті, просто скористайтеся addClass()елементами, які хочуть мати такий стиль фокусування.
odedta

Є рішення лише для CSS, але воно лише у Firefox. це пропозиція W3C css-tricks.com/keyboard-only-focus-styles
jcubic

Відповіді:


102

Це відмінна стаття від Романа Комарова є ефективне рішення для досягнення клавіатури тільки стилів фокуса для кнопок , посилань і інших елементів контейнерів , таких як прольоти або діви (які штучно фокусируемое з атрибутом TabIndex)

Рішення:

Codepen

1) Оберніть вміст оригінального інтерактивного елемента всередині додаткового внутрішнього елемента за допомогою tabindex="-1"(див. Пояснення нижче)

Тож замість сказати:

<button id="btn" class="btn" type="button">I'm a button!</button>

зробити це:

<button id="btn" class="btn" type="button">
    <span class="btn__content" tabindex="-1">
        I'm a button!
    </span>
</button>

2) Перемістіть стиль css до внутрішнього елемента (макет css повинен залишатися на вихідному зовнішньому елементі) - отже, ширина / висота зовнішнього елемента походить від внутрішнього і т.д.

3) Видаліть стиль фокусування за замовчуванням як із зовнішніх, так і з внутрішніх елементів:

.btn:focus,
.btn__content:focus {
    outline: none;
}

4) Додайте стиль фокусування назад до внутрішнього елемента лише тоді, коли зовнішній елемент має фокус:

.btn:focus > .btn__content  {
    box-shadow: 0 0 2px 2px #51a7e8; /* keyboard-only focus styles */
    color: lime; /* keyboard-only focus styles */
} 

Чому це працює?

Фокус тут полягає у встановленні внутрішнього елемента за допомогою tabindex="-1"- див. MDN :

Негативне значення (зазвичай tabindex = "- 1") означає, що елемент повинен бути сфокусованим, але не повинен бути доступним за допомогою послідовної навігації по клавіатурі ...

Отже, елемент можна сфокусувати за допомогою клацання миші або програмно, але з іншого боку - до нього неможливо дістатися за допомогою «вкладки» клавіатури.

Отже, коли натискається інтерактивний елемент - внутрішній елемент отримує фокус. Жодні стилі фокусування не відображатимуться, оскільки ми їх видалили.

.btn:focus,
.btn__content:focus {
    outline: none;
}

Зверніть увагу, що лише 1 елемент DOM може бути сфокусований в даний момент часуdocument.activeElementповертає цей елемент) - так лише буде сфокусовано внутрішній елемент.

З іншого боку: коли ми виконуємо вкладки за допомогою клавіатури - фокус буде потрапляти лише на зовнішній елемент (пам’ятайте: внутрішній елемент має tabindex = "- 1", і до нього неможливо дістатись за допомогою послідовної навігації на клавіатурі) [Зверніть увагу, що для власних не- фокусуються зовнішні елементи, як клікабельні <div>- ми повинні штучно зробити їх фокусуючими, додавши tabindex="0"]

Тепер наш CSS запускає і додає стилі фокусування лише на клавіатурі the inner element.

.btn:focus > .btn__content  {
    box-shadow: 0 0 2px 2px #51a7e8; /* keyboard-only focus styles */
    color: lime; /* keyboard-only focus styles */
} 

Звичайно, ми хочемо переконатися, що, коли ми натискаємо таб enter - ми не порушили наш інтерактивний елемент, і javascript запуститься.

Ось демонстрація, яка показує, що це справді так, зауважте, що ви отримуєте це безкоштовно лише (тобто натискаючи клавішу Enter, щоб викликати подію клацання) для властивих інтерактивних елементів, таких як кнопки та посилання ... для інших елементів, таких як прольоти - потрібно кодувати це вручну :)

Codepen


Примітка:

1) Хоча це здається надто складним рішенням, для рішення, яке не має JavaScript, насправді це досить вражаюче. Простіші рішення, що стосуються лише css, :hoverта :activeстилістичні псевдокласи просто не працюють. (якщо, звичайно, ви не припустите, що інтерактивний елемент відразу зникає при натисканні, як кнопка в модальному скажі)

Codepen

2) Це рішення не ідеальне: firefox у Windows все одно отримуватиме стилі фокусування для кнопок при натисканні - але це, здається, помилка firefox (див . Статтю )

3) Коли браузери реалізувати : фокус-кільце псевда клас - може бути набагато більш простим рішенням цієї проблеми - (див статті ) Для чого це коштує, є polyfill для :focus-ring- см цій статті Кріса Демарсе


Прагматична альтернатива стилям фокусування лише на клавіатурі

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

Codepen

Отже, хоча технічно це не реалізує стилі лише для клавіатури, це, по суті, позбавляє потреби в стилях лише для клавіатури.


1
Здається, помилка Firefox для Windows, як її не було станом на FF 60. Використання :not(:hover):focusселектора, здається, робить трюк.
wegry

@wegry :not(:hover):focus- не дуже вдале рішення через проблему, описану вище: "Як тільки ви наведете курсор - ви отримаєте стилістику фокусу назад - тому що вона все ще сфокусована (принаймні щодо кнопки та зони фокусування)"
Zbynek

@Zbynek Я погоджуюсь, але раніше я використовував його як зупинку.
wegry

Як це можна реалізувати, якщо ми маємо input type = "radio"?
thewebtud

83

Приклад: сторінка входу в Facebook

Facebook зараз використовує крихітний фрагмент Javascript на своїй сторінці входу (червень 2018 р.).

Javascript виявляє, коли користувач клацає мишею або використовує клавіатуру, і включає та вимикає клас на тілі: <body class="using-mouse">

Тоді правила CSS можуть використовувати цей клас, щоб показати або приховати відповідний стиль фокусування на відповідних елементах.

Ось приклад коду (також доступний на CodePen ). Порівняйте клацання та вкладки.

// Let the document know when the mouse is being used
document.body.addEventListener('mousedown', function() {
  document.body.classList.add('using-mouse');
});

// Re-enable focus styling when Tab is pressed
document.body.addEventListener('keydown', function(event) {
  if (event.keyCode === 9) {
    document.body.classList.remove('using-mouse');
  }
});

// Alternatively, re-enable focus styling when any key is pressed
//document.body.addEventListener('keydown', function() {
//  document.body.classList.remove('using-mouse');
//});
/* The default outline styling, for greatest accessibility. */
/* You can skip this to just use the browser's defaults. */
:focus {
  outline: #08f auto 2px;
}

/* When mouse is detected, ALL focused elements have outline removed. */
body.using-mouse :focus {
  outline: none;
}
<input>
<button>Submit</button>

Зверніть увагу, що :focusвище еквівалентно *:focus, збігаючись з усіма елементами. Якби ви хотіли лише прибрати стилістику з кнопок, button:focusзамість цього можете помістити туди.


Приклад: сторінка входу в GMail

Як варіант, у той час GMail просто оформляв сфокусовані кнопки з більш важкою тінню, ніж нефокусовані кнопки, незалежно від того, користувач знаходився на миші чи клавіатурі.

Це просто реалізувати та зрозуміти, і для нього не потрібен Javascript.

:focus {
  outline: none;
  box-shadow: 0 0px 16px #0005;
}

Але це компроміс. Він передає інформацію про фокус, яку користувачі миші насправді не цікавлять, і це може бути занадто тонко для користувачів клавіатури.

Тим не менше, цей компроміс, мабуть, кращий, ніж будь-яка з крайнощів (сильний контур для всіх користувачів або взагалі відсутні).


Основні кнопки StackOverflow використовують подібний підхід до GMail, але з більш стилізованим виглядом:

box-shadow: inset 0 1px 0 0 rgba(102,191,255,0.5), 0 0 0 4px rgba(0,149,255,0.15);

Особисто я б використовував сильніший (вищий контраст) колір для доступності.


5
Мені подобається рішення Facebook, незважаючи на те, що воно використовує Javascript. Код досить простий для розуміння, а CSS повністю у ваших руках. Думаю, я почну цим користуватися. Дякую.
Брем Ванрой,

3
Блискуче просте рішення.
Габріель Родрігес,

3
Я думаю, що це рішення є кращим і менш удачним, ніж прийняте. Крім того, для цього не потрібно стільки захаращення розмітки.
CunningFatalist

1
найкраще рішення +1
Mas

1
@IOIIOOIO Так, їх поведінка змінилася. Сьогодні вони, здається, знову вмикають стилі фокусування, лише натискаючи клавішу Tab, і є порожнє поле / помилка. Але насправді я думаю, що запропоноване вами є найбільш інтуїтивним ( ПОЛА ), тому я оновив відповідь, щоб відповідати цьому. Дякую!
joeytwiddle

44

Видалення outlineстрашне для доступності! В ідеалі кільце фокусування відображається лише тоді, коли користувач має намір використовувати клавіатуру .

2018 Відповідь: Використання : фокус-видимий . В даний час це пропозиція W3C щодо стилізації фокусу лише на клавіатурі за допомогою CSS. Поки основні браузери цього не підтримують, ви можете використовувати цей надійний поліфіл . Це не вимагає додавання зайвих елементів або зміни tabindex.

/* Remove outline for non-keyboard :focus */
*:focus:not(.focus-visible) {
  outline: none;
}

/* Optional: Customize .focus-visible */
.focus-visible {
  outline-color: lightgreen;
}

Я також написав більш детальне повідомлення на випадок, якщо вам знадобиться додаткова інформація.


2
Супер! Майте +1 Інтернет.
joeytwiddle

1
Ugghhhh, чому це ще не стандарт. Шановні майбутні читачі, я вам заздрю.
Chunky Chunk

Одне, що я помітив, це те, що це все одно буде відображати контур фокусування при фокусуванні inputелемента, незалежно від того, навігація або натискання клавіатури користувача.
Big Money

(якщо встановити контур через .focus-visible). Маленька картопля, чудова відповідь спасибі!
Великі гроші

це має бути перша відповідь. може заощадити багато часу.
Itay Tur

20

Це проблема, з якою ви, мабуть, стикаєтесь багато. Хороша річ у таких проблемах полягає в тому, що якщо ви одного разу знайдете рішення, це більше вас не турбуватиме.

Найелегантніше рішення здається найпростішим: не видаляйте контур на: focus, робіть це на: active замість цього - врешті-решт: active - це динамічний псевдо-клас, який явно має справу зі стилями, які слід застосовувати, коли клацаючий елемент натискається або активується іншим способом.

a:hover, a:active { outline: none; }

Єдині незначні проблеми з цим методом: якщо користувач активує посилання, а потім використовує кнопку назад браузера, контур стає видимим. О, і старі версії Internet Explorer, як відомо, плутають точне значення: focus,: hover і: active, тому цей метод не працює в IE6 і нижче.

Чайові

Існує тривіальне обхідне рішення, щоб запобігти «розливанню контурів», додавши просте overflow:hidden , який тримає контур в контролі навколо клікуючої частини самого елемента.


1
Це було проникливо! Дякую.
Miro

2
А як щодо кнопок?
DanV

5
&:focus:not(:hover) { }

Це не спрацює у 100% випадків, але я думаю, що для потреб більшості людей цього має бути достатньо.

Це запобіжить :focusспрацьовуванню стану при клацанні, оскільки миша повинна знаходитись над (наведенням) елемента, щоб натиснути його.

https://codepen.io/heyvian/pen/eopOxr


Найпростіша і найкраща відповідь! Багато подібного використання ви можете побачити на w3.org/TR/wai-aria-practices-1.1/examples/treeview/treeview-2/…
Канада Ван

4

Граючи з прийнятим рішенням Danield, я знайшов альтернативний, простіший спосіб, заснований на внутрішній / зовнішній концепції div.

1) Створіть зовнішній і внутрішній елемент. Дайте зовнішньому елементу tabindex = "0", а внутрішньому елементу tabindex = "- 1"

<div role="button" class="outer" tabindex="0">
    <span class="inner" tabindex="-1">
        I'm a button!
    </span>
</div>

2) У css видаліть контур з внутрішнього елемента, коли сфокусовано:

.inner:focus{
    outline: none;
}

3) Застосувати будь-які обробники подій миші або клацання до внутрішнього елемента. Застосувати будь-які події фокусування (onfocus, onblur, onkeydown) до зовнішнього елемента.

Наприклад:

<div role="button" class="outer" tabindex="0" onfocus="focusEventHandler()" onkeydown="handleKeyDown.bind(this, myEventHandler)">
    <div class="inner" tabindex="-1" onClick="myEventHandler()">
        I'm a button!
    </div>
</div>

** Дотримуйтесь розміру та розташування таким чином, щоб внутрішній елемент повністю перекривав зовнішній елемент. Поставте всю "кнопку" зі стилем на зовнішній елемент.

Як це працює:

Коли користувач натискає на кнопку, він натискає на внутрішній елемент, у якого контур фокуса видалений. Клацнути на зовнішньому елементі неможливо, оскільки він покритий внутрішнім елементом. Коли користувач використовує клавіатуру для вкладки до "кнопки", він потрапляє до зовнішнього елемента (tabindex = "0" робить елемент доступним за допомогою "tab"), який отримує контур фокусу, але внутрішній елемент недоступний через табуляція (з tabindex = "- 1") і не отримує контуру фокусування при натисканні.


Чи не могли б ви розширити питання, чому це краще / простіше прийнятого рішення?
Тамлин

2

ОНОВЛЕННЯ 2020

:focus-visibleприземлився в стабільному Chrome. Просто використовуйте його вже!

ОРИГІНАЛЬНИЙ ПОСТ

Поки :focus-visibleйого немає у всіх популярних вічнозелених браузерах, ви можете використовувати цей простий трюк у глобальній частині вашого CSS, без будь-яких поліфілів:

@media (pointer: coarse) {
  *:focus {
    outline: none;
  }
}

а потім додайте ефекти фокусування, як зазвичай, за допомогою :focus.

На цьому етапі ви, напевно, дізналися, що встановлення outline: none;сфокусованих елементів за замовчуванням - жахлива ідея з точки зору доступності. Це, безумовно, правда.

Однак якщо ви застосуєте це правило до pointer: coarse застосуєте медіа-запиту, воно стане дуже корисним, оскільки воно стосуватиметься лише мобільних телефонів та планшетів, але не настільних комп’ютерів. А саме цього ви хочете досягти.

Єдине питання, про яке я можу подумати, - це мобільні користувачі з клавіатурами, які вони використовують для вкладки вмісту, але я не впевнений, чи багато таких користувачів. Отже, врешті-решт :focus-visibleбуде кращим рішенням, але наразі цього має бути достатньо.


Здається, це не працює і на робочому столі: jsfiddle.net/mirohristov/Lovp947w Чи можете ви надати робочу демонстрацію?
Miro

@Miro, це не передбачає "роботи" на робочому столі, дивіться pointer: coarseтрохи. Вирішення проблеми - приховати контур фокусування лише на мобільних пристроях (телефони, планшети або все інше без чіткого вказівного пристрою, наприклад, миші або сенсорної панелі / трекпада).
Нейромедіатор

1
Розумію. Я думав, ти відповідаєш на моє запитання. (Що стосується і настільних ПК)
Міро,

Так, але насправді ... Це було більше схоже на обхідний шлях для людей, які опиняються тут, шукаючи практичні рішення, доки їх :focus-visibleне впровадять у великих браузерах. Це правильно, що відповідь не має рішення для всіх проблем, перелічених у питанні, і в цьому випадку ви можете справедливо проголосувати за неї - жодна образа не буде сприйнята.
Нейромедіатор

Настала версія 2020 року, :focus-visibleяка реалізована в Chrome. Важлива віха!
Нейромедіатор

0

Чіткого рішення немає. Я зробив одне хакінське рішення: застосуйте подію click на своєму головному контейнері та напишіть код нижче за кліком

    _handleMouseClick = (event) => {
        if(event.detail){
            document.activeElement.blur();
        }
    }

При натисканні за допомогою миші ви отримаєте event.detail = 1, при цьому клік розмиває цей елемент, щоб він видалив контур, а при клацанні клавіатури ми отримуємо event.detail = 0, так що у випадку клавіатури поводитись нормально

АБО

У файлі css

     body.disableOutline *:focus{
        outline: none !important;
    }

У Main js

     document.addEventListener('click', _handleMouseClick,true);
            document.addEventListener('keydown',_keydown,true);
            function _handleMouseClick(event){
                if(event.detail){
                    document.getElementsByTagName("body")[0].classList.add("disableOutline");
                }
            }
            function _keydown(e){
                document.getElementsByTagName("body")[0].classList.remove("disableOutline");
            }

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.