ホバーで画像を切り替える方法【CSS】フェードさせる書き方も
画像をホバーしたときに、JavaScriptを使わずCSSだけで別の画像へ切り替える方法を解説します。
結論から書くと、切り替え自体は :hover で background-image を差し替えるだけです。ただし background-image はトランジションが効かないので、ふわっと切り替えたい場合は2枚重ねて opacity を動かします。
▼やりたいことと使う方法
| やりたいこと | 方法 |
|---|---|
| パッと切り替える | :hover で background-image を差し替える |
| ふわっと切り替える | 2枚重ねて opacity をトランジションする |
| リクエスト数を減らす | スプライト画像と background-position |
<img>タグで切り替える |
<picture>ではなく重ね方式にする |
画像切り替えの基本
結論:通常時とホバー時の2枚を用意し、background-image を :hover で差し替えます。
画像を <img> ではなく背景画像として指定するのがポイントです。<img> の src はCSSから変更できないため、CSSだけで完結させるなら背景画像を使います。
CSSの基本設定
まず、通常時の状態を作ります。
.image-container {
width: 200px;
height: 150px;
background-image: url('通常の画像のURL');
background-size: cover;
background-position: center;
}
background-size と background-position も指定しておいてください。省くと、画像の縦横比が要素と違うときに切れ方が意図せず変わります。
ホバー時の画像切り替え
.image-container:hover {
background-image: url('ホバーした状態の画像のURL');
}
これで切り替わります。ただしホバーした瞬間に画像の読み込みが始まるため、初回だけ一瞬何も表示されないことがあります。次の見出しの重ね方式にすると、両方が最初から読み込まれるのでこの問題も同時に解決します。
トランジションは background-image には効かない
結論:transition: background-image は動きません。background-image は離散値(discrete)として扱われるプロパティで、途中の状態が存在しないためです。
次のように書いても、画像はアニメーションせず一瞬で切り替わります。
/* これは効きません */
.image-container {
transition: background-image 0.3s ease;
}
色(background-color)や位置(background-position)はアニメーションできますが、画像そのものの差し替えは補間できません。ふわっと切り替えたい場合は、2枚を重ねて透明度を動かします。
2枚重ねてopacityで切り替える
擬似要素にホバー時の画像を持たせ、その opacity をトランジションします。
<div class="image-container"></div>
.image-container {
position: relative;
width: 200px;
height: 150px;
background-image: url('通常の画像のURL');
background-size: cover;
}
.image-container::after {
content: "";
position: absolute;
inset: 0;
background-image: url('ホバーした状態の画像のURL');
background-size: cover;
opacity: 0;
transition: opacity 0.3s ease;
}
.image-container:hover::after {
opacity: 1;
}
inset: 0 は top/right/bottom/left をまとめて0にする指定です。これで擬似要素が親要素にぴったり重なります。
この方式なら、ホバー用の画像もページ読み込み時に取得されます。切り替えの初回だけ表示が遅れる問題が起きません。
動きを減らす設定にしている人への配慮も入れておきます。
@media (prefers-reduced-motion: reduce) {
.image-container::after {
transition: none;
}
}
スプライト画像を使用する
結論:2枚を1枚の画像にまとめ、background-position をずらして切り替えます。リクエスト数が半分になり、切り替えも一瞬です。
.image-container {
width: 200px;
height: 150px;
background-image: url('スプライト画像のURL');
background-position: 0 0; /* 通常の画像の位置 */
}
.image-container:hover {
background-position: 0 -150px; /* ホバーした状態の画像の位置 */
}
background-position はアニメーションできるので、こちらはトランジションが効きます。
.image-container {
transition: background-position 0.3s ease;
}
ただし位置が動くアニメーションになるので、フェードとは見え方が違います。切り替えというより「スライドする」印象になる点は意識して使ってください。
なお、HTTP/2以降は複数ファイルの並行取得が効率化されているため、リクエスト数を減らす目的だけでスプライトを使う必要性は以前より下がっています。管理の手間と相談して決めてください。
imgタグの画像を切り替えたい場合
結論:<img> の src はCSSから変更できません。画像として意味を持たせたいなら、2枚とも配置して opacity で切り替えます。
<div class="img-swap">
<img src="通常の画像.jpg" alt="商品の外観">
<img src="ホバー時の画像.jpg" alt="" aria-hidden="true">
</div>
.img-swap {
position: relative;
display: inline-block;
}
.img-swap img {
display: block;
width: 100%;
}
.img-swap img:last-child {
position: absolute;
inset: 0;
opacity: 0;
transition: opacity 0.3s ease;
}
.img-swap:hover img:last-child {
opacity: 1;
}
2枚目の alt は空にして aria-hidden="true" を付けてください。同じものを写した装飾用の画像なので、スクリーンリーダーが2回読み上げる必要はありません。
背景画像との使い分けは次のとおりです。
▼背景画像とimgタグの使い分け
| 背景画像 | <img>タグ |
|
|---|---|---|
| 画像の意味 | 装飾 | コンテンツ |
| 代替テキスト | 付けられない | altで付けられる |
| 検索エンジン | 画像として拾われにくい | 拾われる |
| 向いている用途 | 飾りのアイコン・背景 | 商品写真・記事の図 |
商品写真のように内容として意味のある画像は <img> を使ってください。背景画像にすると alt が付けられず、画像検索にも出なくなります。
タッチ端末での注意点
結論:スマートフォンにはホバーがありません。ホバーでしか見えない情報は、タッチ端末では見られないと考えてください。
タップすると一時的にホバー状態になる端末もありますが、挙動はブラウザによって異なります。切り替え後の画像に重要な情報がある場合は、ホバーだけに頼らない作りにしてください。
装飾としての切り替えなら、ホバーできる環境だけに限定しておくと安全です。
@media (hover: hover) and (pointer: fine) {
.image-container:hover {
background-image: url('ホバーした状態の画像のURL');
}
}
まとめ
CSSだけで画像をホバー時に切り替える方法を紹介しました。
いちばん注意すべきなのは、background-image にトランジションが効かない点です。フェードさせたい場合は必ず2枚重ねて opacity を動かしてください。
要素の表示・非表示を切り替える考え方そのものは、CSSとjQueryでアコーディオンを実装するやjQueryでドロップダウンメニューを実装する5つの方法でも使います。
CSSの書き方をまとめて学び直したい場合は、Webコーディングにオススメの学習本に学習順をまとめています。