😏
ホバーの分岐にはメディアクエリのwidthではなくhoverを使おう
メディアクエリにはhover
プロパティがあり、ユーザーがホバーに対応しているデバイスかどうかの判定が行えます。
これを使用することによって、画面幅での分岐が必要なくなるためタブレットのlandscape含めた対応も容易になります。
また、メディアクエリはJavaScriptからもmatchMedia
という関数を使う事で使用することが可能なのでCSSとJavaScriptでの利用例を載せていきたいと思います。
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でFramerMotionやTailwindなどクラス名で分岐したい場合は下記のようにしてもいいと思います。
/* 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の場合にはメディアクエリを利用できるライブラリもあったりするのでそちらを利用してみてもいいかもしれません。
最後に
直近の仕事でも利用してみましたが、画面幅でホバーを管理していた頃よりコードも書きやすく見通しも良くなった印象でした。
またReactの場合はonMouseEnter
のような関数までを返すカスタムhooksを作って運用していましたがそれもおすすめです。
最後まで読んでいただきありがとうございました。
Discussion
メディア特性 hover は hover が有効であることをチェックするなら
(hover: hover)
よりも(hover)
で良いのではないでしょうか………?ご指摘ありがとうございます!
たしかに
(hover)
でも良さそうですね!記事を書いた当初は
(hover: hover)
と(hover: none)
をわかりやすくする目的であえてこのように書いていたと思うので今後は(hover)
で利用していこうと思います