もうoutlineを消さない。クリック・タップ・キーボードのイベントを判定してくれるJSライブラリ「what-input」
フォーカスインジケータ(フォーカスリング、outline
プロパティ)を消したことはありませんか?
フォーム要素やアコーディオンなどの動きのあるモジュールをコーディングするときに、Tabキーなどで操作したときはフォーカスインジゲータを表示したいけど、クリックやタップで操作するときはフォーカスインジゲータを消したい場合、時間がなかったり、やり方が分からなかったりで、結局outline: none;
してしまったことがありました。
現段階ではCSSだけでこれらのイベントを判定することはできません(focus-visibleという疑似クラスもありますが、草案の段階でほとんどのブラウザが対応していません)。
でも、「what-input」というJavaScriptのライブラリを使うと、クリック・タップ・キーボードのイベントを判定してくれるので、それぞれに対して適切なスタイルにすることができます。
what-inputの始め方
以下の方法でインストールできます。
npmから。
npm install what-input
Yarnから。
yarn add what-input
以下の方法でファイルを読み込みます。
JSのファイルを直接読みこむ。
<script src="path/to/what-input.js"></script>
CDNから読みこむ。
<script src="https://cdnjs.cloudflare.com/ajax/libs/what-input/5.1.2/what-input.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/what-input.min.js"></script>
ES2015でインポートする(JS側でメソッドを呼び出す場合はwhatInput
という名前でインポートします)。
import whatInput from 'what-input';
使い方
公式のデモが用意されているので、動きはここで確認できます。
ページを開いてデベロッパーツールのElementsを表示、htmlタグの以下の属性を確認してください(画面上でもイベント名などがハイライトされます)。
属性 | 説明 |
---|---|
data-whatinput="mouse | keyboard | touch" |
選択した要素に対するイベントが出力されます |
data-whatintent="mouse | keyboard | touch" |
画面に対してのイベントが出力されます |
data-whatelement="button" |
選択した要素のタグ名が出力されます |
data-whatclasses=".foo,.bar" |
選択した要素のクラス名が出力されます |
data-whatinput
とdata-whatintent
が少し分かりにくいと思いますが、デモで色々な操作をして確認してみてください。data-whatintent
はキーボードとクリックを完全に分離して切り替え、data-whatinput
は両方同時に使って操作していく感じかなと思います。
デモではクラスが設定されていないので、data-whatclasses
は確認できません。
セレクタの指定方法
具体的な指定方法です。
以下のように指定すると、クリックやタップで要素を選択したときにだけフォーカスインジゲータが出なくなります。フォーカスインジゲータをoutline
プロパティだけで指定している場合はこれだけで大丈夫かもしれません(詳細度は要確認)。
[data-whatinput="mouse"] *:focus,
[data-whatinput="touch"] *:focus {
outline: none;
}
フォーカスインジゲータをbox-shadow
などで指定する場合は少し調整が必要です。
例としてinput[type="text"]
を調整してみます。
input[type="text"] {
// キーボードで選択された場合はbox-shadowを表示する
[data-whatinput="keyboard"] &:focus {
outline-width: 0;
box-shadow: 0 0 6px 3px #1589ee;
}
// クリックかタップで選択された場合
[data-whatinput="mouse"] &:focus,
[data-whatinput="touch"] &:focus {
}
}
上記ではキーボード操作でフォーカスが当たった場合に、outline
を非表示にして、その代わりにbox-shadow
でフォーカスインジゲータを表示しています。
クリックとタップでもスタイルの追加が可能です。
他にも、リンクエリアをマウスオーバーで透過や画像の拡大などにしている場合に、スマホでスクロールをするだけでも効果が出てしまうことがあります。写真を見たいのに透過で見えなくなってしまったり、スクロールのたびに画像が拡大されると、使いにくいと感じる利用者もいると思います。
ブレイクポイントでPC相当の横幅からマウスオーバーの指定をして対応することも多いと思いますが、html:not([data-whatinput="touch"]) &:hover
のようにすることで、タッチ操作では効果が出ないように指定することもできます。
.link {
html:not([data-whatinput="touch"]) &:hover {
opacity: 0.6;
}
}
下記のようにSassのmixinにしておくと扱いやすくなります。
@mixin focus-with-keyboard {
html[data-whatinput="keyboard"] &:focus {
@content;
}
}
@mixin focus-without-keyboard {
html[data-whatinput="mouse"] &:focus,
html[data-whatinput="touch"] &:focus {
@content;
}
}
@mixin hover-with-touch {
html[data-whatinput="touch"] &:not(:disabled):hover,
html[data-whatinput="touch"] &:not(.-disabled):hover {
@content;
}
}
@mixin hover-without-touch {
html[data-whatinput="mouse"] &:not(:disabled):hover,
html[data-whatinput="mouse"] &:not(.-disabled):hover,
html[data-whatinput="keyboard"] &:not(:disabled):hover,
html[data-whatinput="keyboard"] &:not(.-disabled):hover {
@content;
}
}
JSで値を取得する
data-whatclasses
以外はJS側から取得する関数(whatInput.ask()
とwhatInput.element()
)が用意されています。
以下はCDNから読み込んだ場合です。ボタンをクリックしたら、そのイベントとタグ名を表示します。
$('#foo').on('click', function(e) {
console.log(whatInput.ask()); // デフォルトでは`data-whatinput`の値を取得する
console.log(whatInput.ask('intent')); // `data-whatintent`を取得する場合は`intent`を渡す
console.log(whatInput.element()); // => `data-whatelement`の値を取得する
e.stopPropagation();
});
buttonタグにフォーカスが当たった状態でEnterやスペースキーを押すとclick
イベントが発生します。keydown
やkeyup
のようなイベントからevent.keyCode
で何を押したかを判定するよりも、whatInput.ask()
でイベント名を直接取得したほうがいい場面もあると思います。
詳しい仕様はドキュメントを確認してください。
まとめ
「what-input」を使うと、CSSとJSの両方で、選択した要素のイベント(クリック・タップ・キーボード)を取得することができます。
クリック・タップ・キーボード操作のスタイルや動作をそれぞれで変更することができるので、見た目を損なうことなく、使いやすさも担保することができるようになります。
余談ですが、reset.cssはすべての要素に対してoutline: none;
やoutline: 0;
を指定しているものがあります。利用者の体験を大きく損なう可能性が高いので、見直すようにしていきましょう。
参考: