はじめに
私たちを取り巻くWeb技術は、もはや社会的なインフラとしてめまぐるしく進化しています。HTMLやCSSはもちろんのこと、JavaScriptやライブラリ、フレームワークなど、それぞれがニーズにキャッチアップする形で、機能強化を繰り返しています。その中でも、Web技術の中核に位置するにもかかわらず、意外と見過ごされがちなのがHTMLの進化です。本連載は、このHTMLと関連するJavaScript APIにフォーカスして、その新機能を手軽に試していただこうというものです。理解も利用もたやすいHTMLなので、ライトな気持ちで「こんなことができるようになったのか」を感じていただきます。
[NOTE]サンプルについて
本記事の配布サンプルは、以下のURLから入手できます。新機能を試していくので、ブラウザは最新である方がよいでしょう。本記事のサンプルは、執筆時点で最新のMozilla Firefoxで動作することを確認しています。
https://github.com/wateryinhare62/mynavi_html
連載第8回の目的
この回では、ピクセル密度や表示幅などの条件に応じて最適な画像をブラウザが選択する、img要素のsrcset属性とsizes属性を試します。また関連して、画像を先行読み込みできるlink要素のrel="preload"属性と、その画像を選択するimagesrcset属性とimagesizes属性も試して効果を確認します。
[NOTE]Firefoxを推奨
本稿の画像選択をブラウザで試すには、Firefoxを推奨します。Chrome、Safariでは最大サイズの画像がキャッシュされると、常にそれが使われてしまうためです。これを回避するには、Chromeの場合ならばキャッシュのクリアかハード読み込みを毎回行う必要があり、やや面倒です。このため、本連載ではPCにおける検証ブラウザをChromeとしてきましたが、今回は例外的にFirefoxを使っています。
srcset属性とsizes属性による画像の最適化
srcset属性とsizes属性は、img要素において画像ファイルの選択肢を提示し、ピクセル密度や表示幅から最適な画像をブラウザが選択できるようにする仕組みです。主に、以下の目的で使います。
- ピクセル密度に応じた画像を選択する。iPhoneのRetinaディスプレイへの対応など
- ビューポートの幅に応じた画像を選択する。スマートフォンでは小さい画像を使うなど
画像はコンテンツとして重要度が高いので、できるだけ品質が最高になる画像を使いたいところです。しかしながら画像ファイルのサイズは一般的に大きいので、むやみに大きな画像を使うと通信量の増加と閲覧環境の悪化を招きかねません。このため、最適な画像選択のためのsrcset属性とsizes属性がHTML5の登場から使えるようになっています。本稿では、画像の最適化という視点で、srcset属性とsizes属性を紹介します。
srcset属性でピクセル密度による画像選択
srcsetは、img要素で使用される、ピクセル密度や表示幅に応じた画像選択のための属性です。通常、img要素は単独の画像を固定サイズで表示するためのものですが、CSSやJavaScriptの助けを借りずにimg要素のみで画像選択を可能にするのがsrcset属性です。まずは、srcset属性の使い方をピクセル密度による画像選択を例に紹介します。
ピクセル密度を考慮せずにそのまま画像を表示させる一般的なimg要素の例が、リスト1です。
リスト1:srcset_retina_no.html
<img
src="https://annex.naosan.jp/mynavi_html/srcset/img/image_512.jpg" alt="image"
width="512px"
/>
これを、Retinaディスプレイを搭載したiPhoneで表示させると、図2のようになります。全体的にぼけた感じになり、拡大部分を見ると分かりますがジャギー(ギザギザ)も目立ちます。これは、Retinaディスプレイには倍の大きさの画像が必要なため、それに合わせるために画像が拡大されたためです。
[NOTE]HTMLと画像のURL
本稿のサンプルで参照する画像URLは、筆者運営のサイトにおけるものです。スマートフォンから閲覧したり、後ほど紹介する画像の先行読み込みの例に使うため、ローカル画像ではなく比較的読み込み時間を要するWeb上の画像としています。
ピクセル密度によって画像を切り替えるには、以下のようにsrcset属性を記述します。ピクセル密度記述子には「倍率x」の形式でピクセル密度を、例えば「1x」「2x」などのように指定します(倍率は小数でも可)。
srcset="画像URL1 ピクセル密度記述子1, 画像URL2 ピクセル密度記述子2, …"
リスト2は、幅512pxの画像と幅1024pxの画像をsrcset属性で指定し、ピクセル密度で画像を切り替える例です。srcset属性を指定するときのsrc属性には、いずれの画像も使えなかった場合のフォールバックとしての画像を指定します。
リスト2:srcset_retina.html
<img
src="https://annex.naosan.jp/mynavi_html/srcset/img/image_512.jpg"
alt="image" width="512px"
srcset="
https://annex.naosan.jp/mynavi_html/srcset/img/image_512.jpg 1x,
https://annex.naosan.jp/mynavi_html/srcset/img/image_512_2x.jpg 2x
"
/>
これを、同じくRetinaディスプレイを搭載したiPhoneで表示させると、図2のようになります。画像はシャープになり、拡大部分も滑らかです。これは、Retinaディスプレイではピクセル密度記述子に「2x」が指定された画像が選択され、倍のサイズの画像を同じ大きさ(512px)で表示したためです。
[NOTE]画像の選択はブラウザ依存
後述する表示幅による画像選択もそうですが、srcset属性による画像の選択はあくまでもブラウザに依存します。キャッシュの有無、ネットワークの状態、メモリの利用状況によっては、必ずしも最適な画像が選択されない可能性があります。srcset属性は、あくまでもブラウザに画像の選択候補を提示するものであることを押さえておきましょう。
srcset属性で表示幅による画像選択
srcset属性に幅記述子を指定すると、必要とされる表示幅に適した画像としてブラウザに伝えることができます。幅記述子は、「幅w」の形式で例えば「512w」「1024w」のように指定します。
srcset="画像URL1 幅記述子1, 画像URL2 幅記述子2, …"
前述したピクセル密度記述子と、幅記述子の使い分けは以下のようになります。
- ピクセル密度記述子は、画像とピクセル密度(1倍、2倍など)の対応を記述する。2xならば、この画像はRetinaディスプレイに最適な画像、というように
- 幅記述子は、画像と表示幅(512px、1024pxなど)の対応を記述する。512pxならば、この画像は512ピクセル幅で表示するのにふさわしい画像、というように。
リスト3は、幅512px、1024px、1920pxの画像をsrcset属性で指定し、ブラウザの表示幅で画像を切り替える例です。ブラウザは、ビューポートの幅に最適な画像を3つの候補から選択するという動作になります。
リスト3:srcset_basic.html
<img
src="https://annex.naosan.jp/mynavi_html/srcset/img/image_1920.jpg"
alt="image"
srcset="
https://annex.naosan.jp/mynavi_html/srcset/img/image_512.jpg 512w,
https://annex.naosan.jp/mynavi_html/srcset/img/image_1024.jpg 1024w,
https://annex.naosan.jp/mynavi_html/srcset/img/image_1920.jpg 1920w
" />
ブラウザで表示させると、表示幅によって図4のように画像が切り替わることが確認できます。
ここでは変化がはっきり分かるように、あえて全く異なる画像としましたが、本来であればサイズのみ変えた画像を用意して、表示幅に応じて切り替えるといった使い方をすべきでしょう。
sizes属性で表示幅の切り替え
sizesは、条件によって画像の表示幅を切り替え、それに基づいてsrcset属性から画像を選択させるための属性です。前述の例ではsizes属性を指定せず、srcset属性単独でビューポートの幅に最適な画像を選択するという動作になりましたが、通常は画像の表示幅をsizes属性で決めておき、その幅に最適な画像を選択させるという使い方が一般的です。
sizes属性には、条件と幅指定のペアをカンマ区切りで記述したものを指定します。条件は、メディアクエリと同様の構文で指定し、条件に合致した場合の幅が採用されます。条件を省略した場合には、必ず成立する条件となります。
sizes="条件1 幅1, 条件2 幅2, …"
幅は、ピクセル単位(px)でもビューポート幅の相対値(100vwを最大とした値)でも指定できるので、例えばスマートフォンではビューポート幅いっぱい、PCでは若干小さめの幅で表示するといった指定も可能です。 リスト4は、リスト3にsizes属性を追加し、ビューポートが512pxまでならフルサイズ、それを越える場合にはビューポート比95%で表示されるようにする例です。なお、vwを用いた指定でも、最終的な幅に最適な幅記述子を持った画像が選択されるという動作は、これまで通りです。
リスト4:sizes_basic.html
<img
src="https://annex.naosan.jp/mynavi_html/srcset/img/image_1920.jpg" alt="image"
srcset="
https://annex.naosan.jp/mynavi_html/srcset/img/image_512.jpg 512w,
https://annex.naosan.jp/mynavi_html/srcset/img/image_1024.jpg 1024w,
https://annex.naosan.jp/mynavi_html/srcset/img/image_1920.jpg 1920w
"
sizes="
(max-width: 512px) 100vw,
95vw
"
/>
ブラウザで表示させると、表示幅によって図5のように画像と余白が切り替わることが確認できます。
picture要素+source属性との違い
条件に応じて画像を選択する方法としては、picture要素とsource属性の組み合わせも利用されます。source属性に、条件と画像ファイルの対応を指定することで、条件に合った画像ファイルを選択する仕組みです。これだけ見ると、両者は同じようなものに思えるかもしれません。しかしながら、これらには以下のような違いがあります。
- srcset属性とsizes属性は、あくまでもブラウザが画像を選択する材料を与えるだけ
- picture要素とsource属性は、条件成立時に使う画像をブラウザに明示する
このため、前者はあくまでも画像の最適化に使い、後者は画像コンテンツの切り替え、例えばスマートフォンとPCで画面構成を変えるといった、目的の違いがあります。
imagesrcset属性とimagesizes属性による画像の先行読み込み
通常、画像などのリソースはimg要素などの出現ではじめて読み込みが開始されるわけですが、ページの表示速度の向上を目的として、先行読み込みが可能になっています。先行読み込みにより閲覧環境が良好になるのはもちろん、前節で紹介した画像選択に対応した先行読み込みも可能になっています。
link要素と先行読み込み
先行読み込みは、link要素にrel="preload"属性を付与して指示します。
<link rel="preload" as="種類" href="URL" …>
as属性には、先行読み込みするリソースの種類を指定します。以下が指定できます。
- fetch:JSONファイルのようなFetchまたはXHR(XmlHttpRequest)でアクセスされるリソース
- font:フォント
- image:画像
- script:JavaScript
- style:CSS
- track:WebVTT(video/audio要素で字幕、キャプション、チャプターなどを表示するためのW3C標準のテキストファイル形式(.vtt))
ここでは、as属性には画像(image)を指定するものとして例を紹介します。
imagesrcset属性とimagesizes属性による画像の選択
画像を単独で読み込むには、as属性に"image"を指定し、href属性に画像のURLを指定するだけなので、特に説明は不要でしょう。ただし、前節で扱ったように読み込む画像がimg要素の出現まで決まらない場合には、単にhref属性を指定しただけでは不十分です。
そこで、srcset属性とsizes属性のlink要素版である、imagesrcset属性とimagesizes属性を指定します。これらの属性の指定方法はsrcset属性とsizes属性と同じです。先行読み込みの時点で対象の画像を決定し、img要素では読み込まれた画像を使用します。通常は、キャッシュされた画像をそのまま使うことで高速化します。
なお、imagesrcset属性とimagesizes属性は、執筆時点ではWidely Availableで、2020年ごろから主要ブラウザで利用可能になった機能です。スマートフォン用のブラウザでは2026年サポートと、最近になって利用可能になりました。
リスト5は、リスト5の先行読み込み版です。img要素と全く同じ指定をlink要素にも指定したものです。
リスト5:srcset_preload.html
<link rel="preload" as="image"
href="https://annex.naosan.jp/mynavi_html/srcset/img/image_1920.jpg"
imagesrcset="
https://annex.naosan.jp/mynavi_html/srcset/img/image_512.jpg 512w,
https://annex.naosan.jp/mynavi_html/srcset/img/image_1024.jpg 1024w,
https://annex.naosan.jp/mynavi_html/srcset/img/image_1920.jpg 1920w
"
/>
開発者ツールで確認
リスト5をブラウザで表示させると、当たり前ですが図5と全く同じように表示されます。これだけだと先行読み込みの意義が伝わりにくいと思うので、ブラウザの開発者ツールを使って読み込みタイミングがどのようになっているかを確認してみます。
[NOTE]開発者ツール
開発者ツールは、ブラウザによって若干呼び名と表示手順が異なりますが、機能と操作方法はほぼ共通です。本稿で使用しているFirefoxでは、ビューポート上で右クリックして[調査]を選択するか、ハンバーガーアイコンから[その他のツール]-[ウェブ開発ツール]を選択してください。
開発者ツールを表示したら、まずはタブを「ネットワーク」に切り替えます。そして、毎回確実にWebからコンテンツを読み込むために、[キャッシュを無効化]にチェックを入れてください。その後、リスト3(srcset_basic.html)を読み込んでみます。注目すべきは右下のタイムチャートです。あくまでも筆者の環境における数値ですが、画像読み込みは440ミリ秒後から始まって、読み込み終了まで162ミリ秒掛かりました。
次は、リスト6(srcset_preload.html)を読み込みます。今度は、画像読み込みは320ミリ秒後から始まって、読み込み終了までが152ミリ秒となりました。画像読み込みの時間こそほぼ同一ですが、最終的に画像が準備できるのは、先行読み込みした方が若干速いタイミングとなります。
まとめ
srcset属性とsizes属性はいかがでしたでしょうか。多種多様な閲覧環境があり、良好なパフォーマンスが求められる環境下で有用な、画像読み込みの最適化の方法をお伝えできたのではないかと思います。次回は、ページのコンテンツをクリックでコピーしたり、ページをクリップボードの内容で更新したりするClipboard APIを紹介します。
WINGSプロジェクト 山内直(著) 山田 祥寛(監修)
有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。現在も執筆メンバーを募集中。興味のある方は、どしどし応募頂きたい。著書、記事多数。
RSS
X:@WingsPro_info(公式)、@WingsPro_info/wings(メンバーリスト)<著者について>
WINGSプロジェクト所属のテクニカルライター。出版社を経てフリーランスとして独立。ライター、エディター、デベロッパー、講師業に従事。屋号は「たまデジ。」。









