imgタグのwidthとheightを省略したらダメ! 画像によるレイアウトシフトを回避する最近登場した2つの優れた解決方法
Post on:2022年7月21日
レイアウトシフトとは、Webページがロードされる時に画像のスペースが確保されず、画像が表示された時にその分レイアウトがずれてしまうことです。
このレイアウトシフトを回避するために10年以上もの間、アスペクト比を手動で適用する必要がありました。しかし、現在ではそんな馬鹿げたハックは必要ありません。最近登場した2つの優れた解決方法を紹介します。
Avoiding <img> layout shifts
by Jake Archibald (@jaffathecake)
下記は各ポイントを意訳したものです。
※当ブログでの翻訳記事は、元サイト様にライセンスを得て翻訳しています。
レイアウトシフトとは
デフォルトでは、<img>
はブラウザが画像のサイズを知るためにロードするまで、画像のスペースはゼロになります。
下記は、イエローの上に画像があり、画像ファイルをロードする前の状態なので、画像のスペースはゼロです。
ロードするまでは、画像のスペースはゼロ
画像がロードされると、画像のスペースが確保され、レイアウトが下にずれます。
画像のスペース分、レイアウトが下にずれる
イエローの<figcaption>
はすぐに表示され、その数秒後に後続のコンテンツが下方に移動し、画像のスペースが確保されます。レイアウトシフトとは、このようなレイアウトがずれてしまう現象のことです。レイアウトシフトが起こると、ユーザーの視線やタップしている指からコンテンツが移動してしまうため、ユーザーエクスペリエンスに大きなフラストレーションを与えます。
このレイアウトシフトを回避するために10年以上もの間、アスペクト比を手動で適用するために馬鹿げたハックを使用する必要がありましたが、最近では2つの優れた解決方法が同時に登場しました。
aspect-ratio
プロパティと、width
とheight
の表示に関するヒントです。
どちらの解決方法を使用すべきでしょうか。
この記事では2つの解決方法がどのように機能するかくわしく解説します(というのも、かなり誤った情報が出回っているからです)。
aspect-ratioプロパティによるレイアウトシフトの回避方法
CSSのaspect-ratio
プロパティは、下記のように使用します。
1 2 3 |
.aspect-ratio-demo { aspect-ratio: 16 / 9; } |
上記のCSSでアスペクト比16:9で表示されます。
※下記はキャプチャ画像です。
aspect-ratio
プロパティは2021年初頭にChromeとFirefoxに実装され、同年末にSafariにも実装され、クロスブラウザ互換になりました。
参考: CSSのaspect-ratioプロパティがすべてのブラウザにサポートされました、画像をアスペクト比で実装する今までとこれからの実装方法
aspect-ratio
プロパティはどのような要素でも機能しますが、ここでは<img>
に使用します。
1 2 3 4 |
img { aspect-ratio: 16 / 9; width: 100%; } |
上記のCSSを使用すると、画像がドキュメントに表示されると同時にコンテンツ用のスペースが確保されるため、画像ファイルがロードされたときにコンテンツが移動することはありません。
もう1つの解決方法も見てましょう。
widthとheightによるレイアウトシフトの回避方法
もう1つの解決方法は、画像にサイズを設定する方法です。
1 |
<img width="1598" height="899" src="…" alt="…" /> |
そして、CSSでheight: auto;
を設定します。
1 2 3 |
img { height: auto; } |
これで画像がロードされる前であっても、アスペクト比が適用されます。
この機能は2019年にChromeとFirefoxに導入され、その1年後にSafari14に導入されたときにクロスブラウザ互換になりました。そのため、aspect-ratio
プロパティよりも少し長く存在しています。
この機能は<img>
だけでなく、<video>
や<input type="image">
でも機能します。
しかし、ちょっとした誤解があります。
これはattr()を使用しません
多くの記事で、この機能は下記のようにattr()
で設定するように説明されています。
1 2 3 4 5 6 7 8 9 10 |
img, input[type='image'], video, embed, iframe, marquee, object, table { aspect-ratio: attr(width) / attr(height); } |
まずこの機能は、embed
, iframe
, marquee
, object
, table
では機能しません。また、attr()
は文字列を返すので、この使い方は実際に機能しません。正しく機能させるためには、属性を数値にキャストする必要があります。
1 2 3 4 5 |
img, input[type='image'], video { aspect-ratio: attr(width number) / attr(height number); } |
そして重要なことは、下記の通りです。
attr()
は2022年7月現在、すべてのブラウザにサポートされていません。擬似要素のコンテンツのような特殊なケースは別ですが。いつかサポートされることを期待しています。
実際のところ、どのように機能しますか?
以下は、その仕様です。
width
およびheight
属性は、img
要素とvideo
要素のaspect-ratio
プロパティ(寸法規則を使用)、ならびに画像ボタン状態のtype
属性を持つinput
要素にマッピングされます。
Attributes for embedded content and images - HTML
つまり、aspect-ratio
プロパティにマッピングされるわけです。具体的には、
ユーザエージェントは解析された次元を
auto w / h
形式のaspect-ratio
プロパティのための表示ヒントとして使用することが期待されています。
Mapping to aspect-ratio - HTML
「表示ヒント(presentational hint)」とは、内部的に適用されるゼロ特異性のインラインスタイルに少し似ていると考えることができます。
これは、先ほど触れなかったaspect-ratio
の機能を利用します。
1 2 3 |
.aspect-ratio-demo { aspect-ratio: auto 16 / 9; } |
新しく加えたauto
に注目してください。仕様書ではこうなっています。
auto
と<ratio>
が同時に指定されている場合、指定されたwidth / height
の比率が優先されます。ただし、自然なアスペクト比を持つ置換要素である場合は、そのアスペクト比が代わりに使用されます。
CSS-sizing level 4
仕様書のテキストが少し読みにくいためか、多くの記事ではこの点について触れていませんが、重要な動作について触れられています。
<img>
は「置換要素」ですが、ブラウザは画像の幅と高さを知るのに十分な量のファイルをロードするまでは、「自然なアスペクト比」を持ちません。つまり、ブラウザは画像の実データを取得すると、16 / 9
は無視されます。結果は同じになるので、このことは通常は問題ではありません。しかし、幅と高さを間違えて設定するとどうなるでしょうか?
1 |
<img width="4" height="3" src="…" alt="…" /> |
ブラウザはaspect-ratio: auto 4 / 3
という表示ヒントを使用しますが、実際には画像は 16 / 9
になります。
画像をページに追加すると、指定した4 / 3
のエリアを確保します。しかし、画像がロードされると、auto
ルールが働いて、ブラウザはアスペクト比を16 / 9
に修正します。
1 2 3 4 |
img { aspect-ratio: 4 / 3; width: 100%; } |
auto
を使用しない場合、<img>
は4 / 3
のままで、画像が引き伸ばされて表示されます。その場合は、object-fit
で引き伸ばしを回避できます。
参考: object-fitの使い方: レスポンシブ対応、動画や画像をブラウザいっぱいに表示するCSSのテクニック
1 2 3 4 5 |
img { aspect-ratio: 4 / 3; width: 100%; object-fit: cover; } |
この場合、画像の一部が切り取られます。
あ、あともう一つ。
Firefoxでのちょっとした不具合
レスポンシブ画像では、幅の異なる画像を用意し、使い分けることができます。
1 2 3 4 5 6 7 8 9 |
<picture> <source width="1000" height="614" media="(max-width: 800px)" srcset="wide-view.jpg" /> <img width="800" height="547" src="zoomed-in.jpg" alt="…" /> </picture> |
上記の場合、2つの画像のアスペクト比は異なります。
ChromeとSafariは、使用するソースに応じて正しいwidth
とheight
を使用しますが、Firefoxは常に<img>
に設定されているサイズを使用するため、計算されたアスペクト比が正しくないとわかるとコンテンツが移動(レイアウトシフト)します。
この現象はbugzillaにあるので、早く修正されるといいですね。
どちらの方法を使用すべきか
私の考えとしては、その答えは目新しいものではありません。
CSSベースのaspect-ratio
という1つの解決方法があり、width
とheight
を使用する表示ヒントというもう1つの解決方法があります。これは「画像を<img>
で配置すべきか、background-image
にすべきか」という質問と非常によく似ており、答えも同じです。「その画像はコンテンツなのかデザインなのか」です。
たとえば、記事に画像を使用する場合、それはコンテンツです。確保したスペースはコンテンツのアスペクト比になるようにしたい。width
とheight
属性を間違えると、コンテンツ画像から正しい値が使用されるほうがよい。だから、width
とheight
の方が最適であるように思います。これならインラインスタイルにこだわることなく、コンテンツを作成するだけです。
画像のレイアウトが特定のアスペクト比であることがデザイン上の要件である場合は、CSSでaspect-ratio
を使用することが適切な場合があります。たとえば、ヒーロー画像はつねに16 / 9
でなければならないとかです。画像が16 / 9
でない場合は、デザインが台無しにならないようにデザインを優先させます。ただし、画像が実際にそのアスペクト比ではない場合は、引き伸ばし(object-fit: fill;
)、レターボックス(object-fit: contain;
)、切り抜き(
<picture>
とアートディレクションに関してFirefoxでサポートされていないことは、aspect-ratio
とメディアクエリを使用することで対応できます。しかし、そのバグを早く修正して、ハックする必要がないようにしてほしいところです。
以上です!
この問題に対する真の解決方法を得るまでに10年かかりましたが、現在ではaspect-ratio
を使用する方法、width
とheight
を使用する方法のいずれか適切な方を使用してレイアウトシフトを回避できるようになりました。
sponsors