こんにちは、お久しぶりです。岡部和昌(@kzms2)と申します。
突然ですが、弊社ヤフーがW3Cという標準化団体に参加しているのはご存じでしょうか。
W3C(ダブリュースリーシー)とは、各種ウェブ関連技術の標準化を推進する非営利の標準化団体の名称です。正式名はWorld Wide Web Consortiumで、ティム・バーナーズ=リーによって1994年に設立されました。また現在は400を超える会員組織を擁しており、主要な規格はHTML、XML、MathML、DOMなどがあります。
ヤフーは2018年10月から、ウェブの各種技術の標準化を推進するW3Cに参加しています。
自分はその活動に関して積極的に取り組んでおり、今回はその活動下で得た表示速度改善系の情報で有益だと感じたものをいくつかコード付きで簡単に解説します。
随分と前から存在している仕様の説明もありますが、表示速度向上に関しての最新のナレッジも含めた内容をシェアできればと考えています。
まずはじめに
今回紹介する仕様などは以下の通りです。全て知っていたらそっとこの記事を閉じてください。
- loading属性(Lazy loading)
- content-visibilityプロパティ
- containプロパティ
content-visibility
プロパティが今回のイチオシなのですが、説明する順番的にまずはLazy loading
に触れたほうが理解しやすいので、その順番で説明していきます。また今回は要素の読み込みや表示を調整するモノに的を絞って説明していきます。
1. Lazy loadingを可能にするloading属性
Lazy loadingとは画像がビューポート外にある時は読み込みを実行せず、ビューポートに近づいた時に画像の読み込みを開始する、表示速度を最適化する名称のことです。
値 | 説明 |
---|---|
auto | 既定値。画像の読み込み方をブラウザの実装に委ねる |
lazy | Lazy loadingを有効にする |
eager | 画像をビューポート外でもすぐに読み込む(全ブラウザの基本動作) |
これまではJavaScriptを用いて実装するしかありませんでしたが、ついにimg
やiframe
要素であればloading="lazy"
を付与するだけで、簡単に実装できます。
<!-- 画像に適用する場合 -->
<img src="pic.png" alt="画像の詳細" loading="lazy">
<!-- iframeに適用する場合 -->
<iframe src="external.html" loading="lazy"></iframe>
画面外では読み込みが発生しないので、必要になった時(画面内に要素が入りそうになった時)に読み込みが発生するのでパフォーマンスが向上します。
また画像についてはsrcset
を用いたレスポンシブな画像に対しても指定できますし、picture
要素を用いてfallback形式でも記述できます。
<img src="normal.png"
srcset="large.png 1024w, medium.png 640w, small.png 320w"
sizes="(min-width: 36em) 33.3vw, 100vw"
alt="画像の詳細" loading="lazy">
<picture>
<source media="(min-width: 1024px)" srcset="large-image.png 1x, large-image@2x.png 2x">
<source srcset="image.png 1x, image@2x.png 2x, image@3x.png 3x">
<img src="fallback.png" loading="lazy">
</picture>
※読み込みの初期段階で表示させたい要素、例えばファーストビューのメイン画像などには付与しないのが望ましいです。
対応ブラウザ
※対応ブラウザの表はIEはバージョン11で、その他は最新バージョンの対応状況です(2020年9月8日現在)
その他
ちなみにFacebookのソーシャルプラグインもLazy loading対応しているのですが、こちらはdata-lazy="true"
と書き方が異なるので注意が必要です。(公式のページプラグイン - ソーシャルプラグイン(外部リンク)を参照ください)
<div
class="fb-like"
data-href="https://www.yahoo.co.jp/"
data-lazy="true"></div>
※この書き方はW3Cの定義とは関係がないので、仕様というよりはFacebookのソーシャルプラグインだけに対する書き方だと思ってください
2. content-visibilityプロパティ
2020年8月末リリースのChrome85に実装された機能で、本日記載している内容で一番新しい仕様です。
このプロパティが優れもので、指定した要素の描画をスキップして、ビューポートに近づくと描画 を行います。Lazy loading
はデータの読み込みを遅延実行し、一方で、content-visibility
は表示(レンダリング)を遅延実行します。
値 | 説明 |
---|---|
visible | 既定値。要素の内容は、状態にかかわらずレイアウト・描画される |
hidden | 要素の内容のレイアウト・描画をスキップする(display:none; と近い挙動) |
auto | 要素のレイアウト・スタイル・塗りの封じ込めをオンにする。要素はクライアントが表示を求めない状態の時にはスキップする(ビューポート外など) |
content-visibility: auto;
とその要素を描画する予定の高さの指定をcontain-intrinsic-size
のプロパティを用いることによって使用できます。
<style>
section {
content-visibility: auto;
contain-intrinsic-size: 500px;
}
</style>
<main>
<section>…セクションの中身いろいろ</section>
<section>…別の内容のセクションの中身いろいろ</section>
<section>…そのまた別の内容のセクションの中身いろいろ</section>
</main>
上記の指定だと、各section
がビューポート外にある時は、高さを500pxを保持して中身がない状態で、描画がスキップされます。それにより無駄な描画をしないで済むので、読み込み速度が劇的に速くなります。
ただしcontain-intrinsic-size
の値を適切に設定しないと、指定したsection
がビューポート内に近づいた時に要素の高さが変動してしまいます。概算でも良いのである程度「その要素が本来保持するであろう高さ」を設定してください。
対応ブラウザ
3. containプロパティ
「レイアウト・スタイル・塗りの封じ込め」という表現が上記content-visibility
のauto
値の説明で出てきましたが、このcontain
プロパティの振る舞いが参考になるかもしれません。
content-visibility: auto;
を要素に指定した場合、ビューポート外にある時はcontain: style layout size;
が適用された状態に近いと考えてよいです。
ちなみにこのcontain
プロパティ、Chrome52という随分前の段階ですでに実装されているので厳密に言うと最新の仕様ではありませんが、content-visibility
の影響もあってか最近仕様のアップデートが行われています。
値 | 説明 |
---|---|
none | 既定値。封じ込めは行わない |
strict | contain: size layout paint; と同等 |
content | contain: layout paint; と同等 |
size | 指定した要素のサイズを封じ込め、子孫の影響をけないことを保証する。指定した時にはwidth, heightの指定をしないと要素の縦横幅がない状態で扱われることに注意 |
layout | 指定した要素のレイアウトを封じ込め、親兄弟要素の影響を受けないことを保証する |
paint | 指定した要素の外側に、子孫要素が描画されないことを保証する |
style | 指定した要素とその子孫以外に、影響を与える可能性のあるプロパティについて封じ込めることを保証する。この値は仕様書で「リスクあり」と明示されているので注意が必要 |
<style>
.timeline-item {
contain: strict;
}
</style>
<ul class="timeline">
<li class="timeline-item">style以外を封じ込めても問題ない要素</li>
<li class="timeline-item">style以外を封じ込めても問題ない要素</li>
<li class="timeline-item">style以外を封じ込めても問題ない要素</li>
</ul>
指定する値や条件にもよりますが大まかには以下のような状態です。
基本的にCSSの描画は一部が変更されただけでも全体の再レンダリングが必要となるのですが、このcontain
プロパティを用いることで指定した箇所のCSSのツリーの指定した描画などを封じ込めて、その中でのレンダリングは全体に影響を及ぼさないようになります。
これだけ聞くと良い点しかないように感じられますが、レンダリングを封じ込めるということはその指定した要素の変更が全体のレンダリングに影響しなくなるということなので、例えば非同期で子要素が更新された時、閉じ込めた中身が想定よりもコンテンツが多かった場合は表示されず、表示崩れを起こしてしまいます。
おそらくは上記の「値の説明」を読んでも正直意味がわからない方が多数だと思うのですが、「その要素自身に対して指定した値は、親や兄弟とは別軸でレンダリングされる」と考えると良いかもしれません。
これらの挙動や振る舞いに関しての詳細は、ブラウザの描画に関しての知識が必要なので、「ブラウザ レンダリング」でヤフって見てください。
またの機会にここも掘り下げて書いてみたいと思っています。
対応ブラウザ
まとめ
- Lazy loadingを
img
やiframe
に実装したい!loading="lazy"
を該当要素に追加
- 画面外で要素の描画をスキップして表示を速くしたい!
content-visibility: auto;
とcontain-intrinsic-size: 500px;
を該当要素に指定(500のところは適宜書き換え)
- 描画領域が決まっている箇所、また非同期通信などで取得する要素の描画を速くしたい!
- サイズが変化しない場合は
contain: strict;
を、サイズが可変の場合にはcontain: content;
を該当要素に指定(必要に応じて値は変更)
- サイズが変化しない場合は
このどれもすべての要素に指定するなどの暴挙は行わず、必要と感じた箇所にのみ指定をしてください。優先的に読み込みたい・表示させたい要素には指定しないことをオススメします。
また新しく追加された仕様ということもあって、仕様自体に変更が入る可能性が十分にありえます。W3CやWHATWGなどの一次ソースの確認を定期的に行い、仕様に変化が出ていないかなどの確認を行ってください。
情報を追う自信がない方は、岡部のTwitter(@kzms2)をフォローしておいていただければこれらの情報は必ずつぶやきます。
最後に一言と注意点
いかがでしたか。対応ブラウザに偏りがあるものの、比較的簡単に実装できることがわかったかと思います。注意点を強いて挙げるならば、まだ実装されたばかりなので、バグがある可能性、仕様が変更される可能性がある という点でしょうか。実際にcontain: style;
に関しては最近でも仕様の扱い方が揺れ動いています。
UIやデザインには好みがあるけど、ページが速く表示されて文句言う人はいない です。
今回はこれらの内容について 「簡易的に理解をしたい」 という方向けに書きました。仕様や詳細に関しては最後に参考リンクを貼っておくので、原文を見に行って読むことをとてもオススメします。
参考リンク(外部リンク)
- loading属性
- content-visibilityプロパティ
- containプロパティ
こちらの記事のご感想を聞かせください。
- 学びがある
- わかりやすい
- 新しい視点
ご感想ありがとうございました