ホバーエフェクトはCSS?JS?実装パターン別の選択基準|現場で使える実践テクニック
こんにちは!
今日は「ホバーエフェクトはCSS?JS?実装パターン別の選択基準」について解説します。
ホバーエフェクトで迷う現場あるある
ボタンにホバーをかけるとき、皆さんはどうしていますか?
実はね、僕も駆け出しの頃「ホバーするたびにJavaScriptを呼び出してる」という、ほんまにもったいない実装してました。
後輩から「あ、クリオさん、これCSS で書けますよ」と言われたときの恥ずかしさ、今でも覚えてます(笑)
現場でよく見るのは、ディレクターやデザイナーから「ホバーで色が変わる」「スケールアップする」みたいな要件をもらうんですけど、その瞬間に「これはJSが必要かな?」と条件反射で考えてしまう人が多いんです。
でも実際のところ、85パーセント以上のホバーエフェクトはCSSだけで十分やん。
だからこそ、判断基準をはっきり持つことが大事です。
今日はそのポイントを、実例を交えて説明していきますね。
シンプルなホバーはCSS一択です
まず確認しておきたいのが、CSSホバーの強力さです。
次のようなエフェクトなら、めっちゃCSSで対応できます。
- 背景色の変更
- 文字色の変更
- ボーダーの変更
- スケール・回転・スキュー
- オパシティ(透明度)の変更
- シャドウの付与
- 複合的なトランジション
これらはすべて:hover疑似クラスとtransitionプロパティでカバーできるんです。
例えば、ボタンがホバーされたときにスケールアップと背景色が同時に変わる場合:
.button {
background-color: #3b79b7;
padding: 12px 24px;
transform: scale(1);
transition: all 0.3s ease;
}
.button:hover {
background-color: #5a9fd4;
transform: scale(1.1);
}
ほら、これだけです。
JavaScriptなんて一行も必要ないですよね。
僕の経験上、「ホバーでアニメーション」=「JSが必要」だと思い込んでいる人がめっちゃ多いんです。
でもtransitionがあるおかげで、CSSだけで十分スムーズなアニメーションが作れるんですよ。
しかも、パフォーマンスもいいし、コードも簡潔。
これは選ばない理由がないですよね。
注意点として、タッチデバイスでは:hoverが効かないことを頭に入れておいてください。
モバイルではホバーって概念がないので、ユーザーはタップで操作します。
そういう時は後で説明する@media (hover: hover)という書き方で、デバイスを判定できますよ。
複数の要素を連動させるならJavaScriptの出番
ここからが、CSSだけじゃ難しい領域です。
例えば、「親要素のボタンをホバーされたら、その下にある複数の子要素が順番にフェードインする」みたいなケースですね。
もしくは「ホバーされたら、全く別の場所にある要素が同時に動く」とか。
CSSの:hoverは、ホバーされた要素とその子孫にしか影響を与えられません。
兄弟要素や遠くの要素を動かしたい時点で、もうCSSだけじゃ足りないんです。
// ホバーで複数要素を制御したい場合
const button = document.querySelector('.menu-trigger');
const items = document.querySelectorAll('.menu-item');
button.addEventListener('mouseenter', () => {
items.forEach((item, index) => {
item.style.animationDelay = ${index * 0.1}s;
item.classList.add('is-active');
});
});
このように、複数要素の連動制御が必要な時は、JavaScriptの出番です。
クラスの付け外しで状態を管理して、CSSでそのクラスに対するスタイルを書く、というハイブリッドなやり方がおすすめです。
僕も現場で「ホバーされたら全要素が一斉に動く」みたいな要件をもらったことがあるんですけど、そういう時はクラスベースの設計にしておくと、後々の修正も楽ですよ。
マークアップも読みやすくなりますし。
デバイス判定が必要な時の選択肢
さっき少し触れましたけど、タッチデバイスの話をもっと深堀りしておきたいんです。
ホバーエフェクトを実装するときに忘れちゃいけないのが「このサイト、スマホでも見られるんだな」ってことです。
スマホ時代に:hoverだけで設計すると、タップしても何も反応がなく、ユーザーは「ボタン壊れてるのか?」って思っちゃいます。
そこで活躍するのが、メディアクエリの@media (hover: hover)という書き方です。
/* ホバーをサポートしてるデバイスのみ */
@media (hover: hover) {
.button:hover {
background-color: #5a9fd4;
transform: scale(1.1);
}
}
/* タッチデバイスの場合、フォーカスで反応させる */
@media (hover: none) {
.button:active, .button:focus {
background-color: #5a9fd4;
transform: scale(1.1);