🖼️

background-imageの表示ラグ問題を(ほぼ)CSSのみで実装するCSS遅延読み込みで解決

2022/02/14に公開

ベストプラクティスとも限らないですが、プレーンなHTML・CSSサイトでも手軽に実装できる方法を紹介します。

background-imageの一瞬表示されない問題

CSSのbackground-imageはその画像が表示されるまで読み込まれません。例えばユーザーのアクションに応じてbackground-imageを変える場合、変更後の画像はユーザーアクションが行われてから初めて読み込まれます。(この読み込みは初回のみ行われるので、2回目以降は発生しません)

遅延表示のサンプル

より具体的な現象としては、独自デザインのラジオボタンやチェックボックスをクリックした時、初回のみ一瞬遅れて表示される、というのがあります。他にもdisplay: noneで非表示にしている要素を表示させた場合でも同様の表示ラグが発生します。

▼初回のみ表示が遅くなっている様子(分かりやすくするためにネットワーク速度を落としています)

このラグを解決するために予め画像を読み込んでおく必要があります。ストレートな対応としてはページ読み込み時にそのような画像も読み込んでしまえば解決できますが、表示速度が重視されている昨今では、ページ時読み込み時に使わない画像のロードは避けたいです。

ほかにはrel="preload"を使って画像を読み込んでおく手法があります

<link rel="preload" href="hoge.png" as="image">

これを使えば指定した画像を遅延読み込みしてくれます。遅延読み込みしたい画像が少数なら良いですが、ある程度の規模だとサイト全体で背景画像の数もそれなりの数になり、1つ1つ記述していくのは少し不便[1]です。

手軽にほぼ[2]CSSのみでこの一瞬表示されない問題を解決する手法を紹介します。

background-imageを遅延読み込みさせる

この解決のためにCSSの背景画像を遅延読み込みさせます。HTMLの<img>タグにはloading属性があり、loading="lazy"などを指定すれば手軽に遅延読み込みを実装できますが、CSSにはありません。

そこで、次のような遅延読み込みしたい画像用のCSSを作ります。

▼preload.css

html:after {
  content: "";
  background-image: url("https://picsum.photos/id/120/1200/900"),
    url("./images/radio_on_preload.png"),
    ...
}

background-imageプロパティは画像を複数指定できるので、遅延読み込みさせたい画像の分だけ記述していきます。このCSS<HTML>タグの疑似要素に背景画像を設定するものですが、サイズがないので実際の表示されることはありませんが、display:noneや使われていない背景画像と異なり、このCSSが適用されれば画像自体は読み込まれます。

つづいて<head>タグ内のCSS読み込みを次のようにします。

<link
  rel="stylesheet"
  href="./preload.css"
  media="print"
  onload="this.media='all'"
/>

media="print"onload="this.media='all'"が見慣れないかもしれませんが、CSSのレンダリングブロックを回避するテクニックです。

読み込み時はmedia="print"(印刷用CSS)なので読み込まれずスルーされます。インラインJavaScriptでonloadイベントがあるので、ページ全体の読み込みが終わったらthis.media='all'が実行されます。つまり、読み込み完了時にmedia="print"からmedia="all"に変化し、この時点でpreload.cssが読み込まれます。

preload.cssが読み込まれると、先程設定したHTMLの疑似要素にCSSが設定され、画像が読み込まれます。これで画像が読み込まれたので動的な変更時にもラグなく表示されます。

まとめ

media="print"this.media='all'を使ってCSSファイルの遅延読み込みさせる方法で表示ラグを解決する方法を紹介しました。余談ではありますが、この方法はWebフォントを遅延読み込みさせる方法[3]など、CSSファイルの遅延読み込みとして使えます。初回の一瞬だけのチラツキとはいえ、こうした部分にも配慮があるとよりユーザー体験の良いサイトになるでしょう。

脚注
  1. 静的サイトジェネレーターやフレームワークを使えばこの辺は解決できるかもしれませんが、本記事ではスコープ外とします。 ↩︎

  2. 微量のインラインJavaScriptが含まれているので純粋にCSSのみではないです。 ↩︎

  3. The Fastest Google Fonts – CSS Wizardry – Web Performance Optimisationを参照 ↩︎

Discussion