😏

ホバーの分岐にはメディアクエリのwidthではなくhoverを使おう

2022/04/01に公開2

メディアクエリにはhoverプロパティがあり、ユーザーがホバーに対応しているデバイスかどうかの判定が行えます。

https://developer.mozilla.org/ja/docs/Web/CSS/@media/hover

これを使用することによって、画面幅での分岐が必要なくなるためタブレットのlandscape含めた対応も容易になります。

また、メディアクエリはJavaScriptからもmatchMediaという関数を使う事で使用することが可能なのでCSSとJavaScriptでの利用例を載せていきたいと思います。

https://developer.mozilla.org/ja/docs/Web/API/Window/matchMedia

CSS

CSSでの利用は簡単ですね

.link {
  background: rgba(0, 0, 0, 1);
}

@media (hover: hover) {
  .link:hover {
    background: rgba(0, 0, 0, 0.7);
  }
}

下記のようにホバーができる端末のみ、ホバー前のスタイルを用意したい場合にも分岐が簡単になります。

.link {
  background: rgba(0, 0, 0, 1);
}

@media (hover: hover) {
  .link {
    transform: scale(1.1);
  }
  .link:hover {
    background: rgba(0, 0, 0, 0.7);
    transform: scale(1);
  }
}

SCSSなどを利用している場合はこのままmixinにして使いまわしてもいいと思います。

JavaScript

JavaScriptの利用方法は下記のようにmatchMediaを利用するだけになります。

const hasHover = window.matchMedia("(hover: hover)").matches;

if(hasHover) {
  /* ホバーできる場合の処理 */
}

例えばmouseenterイベントでgsapなどでアニメーションをつけたい場合の分岐も容易になりますね。

ReactでFramerMotionTailwindなどクラス名で分岐したい場合は下記のようにしてもいいと思います。

/* FramerMotionのアニメーション用にstateを変更する場合 */

const Component = () => {
  const hasHover = window.matchMedia("(hover: hover)").matches;
  const [isHover, setHover] = useState(false)

  const onMouseEnter = useCallback(() => {
    if(hasHover) {
      setHover(true)
    }
  }, [])

  const onMouseLeave = useCallback(() => {
    if(hasHover) {
      setHover(false))
    }
  }, [])

  return (
    <a
      onMouseEnter={onMouseEnter}
      onMouseLeave={onMouseLeave}
    >
      {/* isHoverでアニメーションの出しわけ */}
      <motion.div />
    </a>
  )
}
/* Tailwindなどでクラス名を分岐させる場合 */

const Component = () => {
  const hasHover = window.matchMedia("(hover: hover)").matches;

  return (
    <a
      className={clsx(
        'text-white',
        hasHover && 'hover:opacity-75'
      )}
    >
      ...
    </a>
  )
}

Reactの場合にはメディアクエリを利用できるライブラリもあったりするのでそちらを利用してみてもいいかもしれません。

https://www.npmjs.com/package/@react-hook/media-query

最後に

直近の仕事でも利用してみましたが、画面幅でホバーを管理していた頃よりコードも書きやすく見通しも良くなった印象でした。

またReactの場合はonMouseEnterのような関数までを返すカスタムhooksを作って運用していましたがそれもおすすめです。

最後まで読んでいただきありがとうございました。

Discussion

junerjuner

メディア特性 hover は hover が有効であることをチェックするなら (hover: hover) よりも (hover) で良いのではないでしょうか………?

nanaki14nanaki14

ご指摘ありがとうございます!
たしかに(hover)でも良さそうですね!
記事を書いた当初は(hover: hover)(hover: none)をわかりやすくする目的であえてこのように書いていたと思うので今後は(hover)で利用していこうと思います