O webu
Proč a jak používat :focus stav

Odkazy, <input>y, tlačítka a všechny ostatní libovolné elementy s nezáporným atributem tabindex mohou mít :focus stav.

V praxi to znamená, že po kliknutí nebo odTabování je daný element zaměřitelný přes selektor :focus.

a:focus {
  /* styl vybraného odkazu */
}

Pro uživatele používající klávesnici je to dost užitečné, protože hezky vidí, kde se nacházejí. Dobrá ovladatelnost jen z klávesnice bývá u dobře přístupných webů samozřejmostí.

Výchozí :focus

Prakticky všechny novější prohlížeče se snaží focusovaný element nějak zvýraznit, aby to uživatel poznal.

Chrome a Opera tak činí 5px modrým rámečkem. Edge a Firefox potom 1px černým tečkovaným. Orámování je řešené vlastností outline. Ta dělá něco jako border, jen:

  1. Neovlivňuje obsah elementu, ale rámeček se dostane mimo.
    Neovlivňuje obsah elementu.

  2. Může dosáhnout i nepravidelných tvarů (v Opeře a Chrome).


  3. Vyleze i přes nastavené oříznutí přes overflow: hidden.

Pokud tedy autor CSS do výchozího :focusu nezasahuje, má od tvůrců prohlížečů tuto schopnost úplně „zadarmo“.

Vlastní styl pro :focus

Bohužel ne vždy si jde s výchozím stylem vystačit. Trpí několika problémy:

  1. Nemusí visuálně ladit k designu webu. Nebo nemusí být dostatečně kontrastní k pozadí na daném místě stránky.

  2. Vzhled se liší napříč prohlížeči.

  3. V některý případech se nehodí k tvaru tlačítek / ovládacích prvků. Typicky třeba u tlačítek s kulatými okraji.

Vypnutí výchozího rámečku tlačítka

Prosím vypnout ten ošklivý rámeček kolem tlačítek, který tam zůstává po kliknutí.

Bývají častá slova autora visuálního návrhu při pohledu na jeho podobu převedenou do prohlížeče.

Návrhy vzhledu webů od grafiků se stavy pro :focus bývají spíš výjimkou než pravidlem (autor článku se s nakresleným :focus stavem ještě nesetkal), takže se na to snadno zapomene.

Vypnout?

:focus {
  outline: none;
}

Tento kód bohužel vylévá vaničku i s dítětem. Místo ošklivého rámečku nebude označení :focusu žádné a uživatel se při použití Tabu ztratí na stránce.

Skoro automatický :focus

Pokud se člověku nechce vymýšlet další styl prvků, je relativně dobré řešení převzít styl :hoveru (ten občas grafici namalují) a použít ho i pro :focus.

a:hover,
a:focus {
  /* styly pro :hover i :focus */
}

Stavy se sice od sebe nebudou lišit (to je trochu škoda), ale je to skoro bez práce.

  • Postcss-hocus – filtr pro PostCSS, který umožní oba stavy napsat na jeden řádek

V dávných dobách se do tohoto předpisu přidávala i třída :active, protože starší IE ji používal pro odkazy zaměřené Tabem. V roce 2017 a později už je to ale zbytečné až nežádoucí:

Stav :active (po stisknutí tlačítka myši) by měl být rovněž visuálně navržen a měl by mít jiný vzhled než :hover.

Jaký styl pro :focus?

Nabízí se třeba změnit barvu nebo přidat rámeček okolo – pomocí outline.

Je-li potřeba rámeček s kulatými rohy, jde k tomu hezky použít 1px stín vytvořený vlastností box-shadow.

Zůstávání :focusu po kliknutí

Bohužel i při pečlivém návrhu se při střetu s realitou zjistí smutná věc.

Vlastní :focus styl se chová jinak. Dá se říct, že jeho současná implementace ve většině prohlížečů je rozbitá.

Některé prvky po vybrání myší sice získají :focus, ale výchozí outline prohlížeče se vůbec nezobrazí – zobrazí se jen při zaměření prvků klávesnicí.

Odlišné chování při zaměření myší a klávesnicí se týká:

  • odkazů,
  • tlačítek (<button> i <input type=submit>),
  • zaškrtávacích polí typu checkbox a radio,
  • volby rozsahu (<input type=range>),
  • možná ještě něčeho?

Tyto elementy po kliknutí myší :focusovaný stav nemají.

Naopak následující :focusovatelné prvky mají outline i po kliknutí:

  1. skoro všechny textové, číslené, telefonní a podobné <input>y,
  2. <textarea>,
  3. <select>,
  4. obecný element s tabindexem (kromě -1)

Styl :focusovaného elementu po kliknutí může vypadat rušivě. Navíc se v tomto chování liší od výchozího chování v prohlížečích.

Jediné, co jde bezpečně měnit, aby styl pro :focus po kliknutí nezůstal, je outline-color. Změny jiných vlastností se projeví i po kliknutí.

(Ne)řešení

Nějaké universální jednoduché řešení moc neexistuje. Nejsnazší je obvyklé nic nedělání: měnit u výchozího rámečku jen barvu nebo vůbec nic.

Jinak je potřeba využít JavaScript:

Vyvolání události blur

Po kliknutí na prvek u něj okamžitě zavolat blur() a tím zrušit :focus.

$('a, button, input[type=checkbox], input[type=radio]').click(
  function () {
    $(this).blur();
  }
);

Vypadá to relativně funkčně — dokonce si prohlížeče mimo MS Edge i pamatují, kde skončilo Tabování, a jsou schopny navázat.

Bohužel to trochu mění chování, protože prvky po kliknutí už skutečně ztratí focus – takže třeba zaškrtávátko checkbox už nepůjde znovu odškrtnout/zaškrtnout Mezerníkem. Stejně tak tlačítko <button> nepůjde z klávesnice vícekrát zmáčknou, to bude zvlášť problém u +/− tlačítek.

A trochu bych se bál, že to bude přinášet obtížně zjistitelné problémy do budoucna.

Přepínání tříd

Nabízí se myšlenka třeba při kliknutí myší na políčko (onmousedown) přidat elementu třídu typu is-mouse-focus a při onbluru ji zase odebírat.

V selektoru pro :focus potom použít ještě :not, aby se styly v tomto případě neaplikovaly.

Bohužel současná pravidla v prohlížečích jsou tak komplexní, že to úplně jednoduše zapsat nejde. Například zůstávání :focusu se liší u jednotlivých typů <input>ů – tlačítka, checkboxy, radia nebo rozsahy :focus mít nemají, ale všechno ostatní ano.

Dále je možné prvek :focusovat i kliknutím jinam, tj. přes značku <label>.

Touto problematikou se zabývají následující stránky:

Pseudotřída :focus-visible

Specifikace (draft) CSS 4 selektorů počítá s doplněním :focusu o :focus-visible.

Až bude fungovat, bude se pravděpodobně používat místo současného :focusu.

Tato pseudotřída dokáže v závislosti na použitém rozhraní (myš/klávesnice/dotyk) určit, jestli je žádoucí, aby se zobrazilo zvýraznění. Půjde tak docílit vlastního vzhledu, který se ale bude chovat jako současný nativní outline.

element:focus-visible {
  /* libovolné styly pro :focus */
}

Polyfill pro :focus-visible

Existuje hotový maličký JS polyfill, který daným prvkům nastavuje CSS třídu focus-visible ve stejných případech, jako prohlížeče výchozí outline:

Ve Firefoxu :moz-focusring

Ve Firefoxu už funguje tato vlastnost s prefixem a psaná bez spojovníku. Ještě ve starší podobně názvu, který měl znít :focus-ring

Knihovna What Input

Hotové JS řešení What Input? dokáže stránce přiřazovat atributy podle toho, zda se vstup provedl myší nebo z klávesnice. V případě, že proběhl myší, jde tak výchozí :focus v některých případech potlačit.

Focus uvnitř :focus-within

Docela novinka, ale podporovaná už v současných versích většiny prohlížečů (kromě Edge a Android Browser).

Na nějakém rodičovském prvku (třeba značce <form>) jde zjistit, jestli nemá :focus něco uvnitř.

form:focus-within {
  box-shadow: 0 0 5px 0 red;
}

Takový formulář během vyplňování políček získá červený stín.

Odkazy jinam