yasuda

もう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/what-input@5.1.2/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-whatinputdata-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イベントが発生します。keydownkeyupのようなイベントからevent.keyCodeで何を押したかを判定するよりも、whatInput.ask()でイベント名を直接取得したほうがいい場面もあると思います。

詳しい仕様はドキュメントを確認してください。

まとめ

「what-input」を使うと、CSSとJSの両方で、選択した要素のイベント(クリック・タップ・キーボード)を取得することができます。
クリック・タップ・キーボード操作のスタイルや動作をそれぞれで変更することができるので、見た目を損なうことなく、使いやすさも担保することができるようになります。

余談ですが、reset.cssはすべての要素に対してoutline: none;outline: 0;を指定しているものがあります。利用者の体験を大きく損なう可能性が高いので、見直すようにしていきましょう。

参考:

新しいウェブ体験を作ろう TAMのPWA開発
お問い合わせはこちら