imgタグのwidthとheightを省略したらダメ! 画像によるレイアウトシフトを回避する最近登場した2つの優れた解決方法

レイアウトシフトとは、Webページがロードされる時に画像のスペースが確保されず、画像が表示された時にその分レイアウトがずれてしまうことです。

このレイアウトシフトを回避するために10年以上もの間、アスペクト比を手動で適用する必要がありました。しかし、現在ではそんな馬鹿げたハックは必要ありません。最近登場した2つの優れた解決方法を紹介します。

img要素によるレイアウトシフトを回避する最近登場した2つの優れた解決方法

Avoiding <img> layout shifts
by Jake Archibald (@jaffathecake)

下記は各ポイントを意訳したものです。
※当ブログでの翻訳記事は、元サイト様にライセンスを得て翻訳しています。

レイアウトシフトとは

デフォルトでは、<img>はブラウザが画像のサイズを知るためにロードするまで、画像のスペースはゼロになります。
下記は、イエローの上に画像があり、画像ファイルをロードする前の状態なので、画像のスペースはゼロです。

ロードする前の状態

ロードするまでは、画像のスペースはゼロ

画像がロードされると、画像のスペースが確保され、レイアウトが下にずれます。

ロードした後の状態

画像のスペース分、レイアウトが下にずれる

イエローの<figcaption>はすぐに表示され、その数秒後に後続のコンテンツが下方に移動し、画像のスペースが確保されます。レイアウトシフトとは、このようなレイアウトがずれてしまう現象のことです。レイアウトシフトが起こると、ユーザーの視線やタップしている指からコンテンツが移動してしまうため、ユーザーエクスペリエンスに大きなフラストレーションを与えます。

このレイアウトシフトを回避するために10年以上もの間、アスペクト比を手動で適用するために馬鹿げたハックを使用する必要がありましたが、最近では2つの優れた解決方法が同時に登場しました。
aspect-ratioプロパティと、widthheightの表示に関するヒントです。

どちらの解決方法を使用すべきでしょうか。
この記事では2つの解決方法がどのように機能するかくわしく解説します(というのも、かなり誤った情報が出回っているからです)。

aspect-ratioプロパティによるレイアウトシフトの回避方法

CSSのaspect-ratioプロパティは、下記のように使用します。

上記のCSSでアスペクト比16:9で表示されます。
※下記はキャプチャ画像です。

aspect-ratioプロパティは2021年初頭にChromeとFirefoxに実装され、同年末にSafariにも実装され、クロスブラウザ互換になりました。

参考: CSSのaspect-ratioプロパティがすべてのブラウザにサポートされました、画像をアスペクト比で実装する今までとこれからの実装方法

aspect-ratioプロパティはどのような要素でも機能しますが、ここでは<img>に使用します。

上記のCSSを使用すると、画像がドキュメントに表示されると同時にコンテンツ用のスペースが確保されるため、画像ファイルがロードされたときにコンテンツが移動することはありません。

もう1つの解決方法も見てましょう。

widthとheightによるレイアウトシフトの回避方法

もう1つの解決方法は、画像にサイズを設定する方法です。

そして、CSSでheight: auto;を設定します。

これで画像がロードされる前であっても、アスペクト比が適用されます。

この機能は2019年にChromeとFirefoxに導入され、その1年後にSafari14に導入されたときにクロスブラウザ互換になりました。そのため、aspect-ratioプロパティよりも少し長く存在しています。

この機能は<img>だけでなく、<video><input type="image">でも機能します。

しかし、ちょっとした誤解があります。

これはattr()を使用しません

多くの記事で、この機能は下記のようにattr()で設定するように説明されています。

まずこの機能は、embed, iframe, marquee, object, tableでは機能しません。また、attr()は文字列を返すので、この使い方は実際に機能しません。正しく機能させるためには、属性を数値にキャストする必要があります。

そして重要なことは、下記の通りです。

サイトのキャプチャ

attr()のサポート状況

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の機能を利用します。

新しく加えたautoに注目してください。仕様書ではこうなっています。

auto<ratio>が同時に指定されている場合、指定されたwidth / heightの比率が優先されます。ただし、自然なアスペクト比を持つ置換要素である場合は、そのアスペクト比が代わりに使用されます。
CSS-sizing level 4

仕様書のテキストが少し読みにくいためか、多くの記事ではこの点について触れていませんが、重要な動作について触れられています。

<img>は「置換要素」ですが、ブラウザは画像の幅と高さを知るのに十分な量のファイルをロードするまでは、「自然なアスペクト比」を持ちません。つまり、ブラウザは画像の実データを取得すると、16 / 9は無視されます。結果は同じになるので、このことは通常は問題ではありません。しかし、幅と高さを間違えて設定するとどうなるでしょうか?

ブラウザはaspect-ratio: auto 4 / 3という表示ヒントを使用しますが、実際には画像は 16 / 9になります。

画像をページに追加すると、指定した4 / 3のエリアを確保します。しかし、画像がロードされると、autoルールが働いて、ブラウザはアスペクト比を16 / 9に修正します。

autoを使用しない場合、<img>4 / 3のままで、画像が引き伸ばされて表示されます。その場合は、object-fitで引き伸ばしを回避できます。

参考: object-fitの使い方: レスポンシブ対応、動画や画像をブラウザいっぱいに表示するCSSのテクニック

この場合、画像の一部が切り取られます。
あ、あともう一つ。

Firefoxでのちょっとした不具合

レスポンシブ画像では、幅の異なる画像を用意し、使い分けることができます。

上記の場合、2つの画像のアスペクト比は異なります。
ChromeとSafariは、使用するソースに応じて正しいwidthheightを使用しますが、Firefoxは常に<img>に設定されているサイズを使用するため、計算されたアスペクト比が正しくないとわかるとコンテンツが移動(レイアウトシフト)します。

この現象はbugzillaにあるので、早く修正されるといいですね。

どちらの方法を使用すべきか

私の考えとしては、その答えは目新しいものではありません。

CSSベースのaspect-ratioという1つの解決方法があり、widthheightを使用する表示ヒントというもう1つの解決方法があります。これは「画像を<img>で配置すべきか、background-imageにすべきか」という質問と非常によく似ており、答えも同じです。「その画像はコンテンツなのかデザインなのか」です。

たとえば、記事に画像を使用する場合、それはコンテンツです。確保したスペースはコンテンツのアスペクト比になるようにしたい。widthheight属性を間違えると、コンテンツ画像から正しい値が使用されるほうがよい。だから、widthheightの方が最適であるように思います。これならインラインスタイルにこだわることなく、コンテンツを作成するだけです。

画像のレイアウトが特定のアスペクト比であることがデザイン上の要件である場合は、CSSでaspect-ratioを使用することが適切な場合があります。たとえば、ヒーロー画像はつねに16 / 9でなければならないとかです。画像が16 / 9でない場合は、デザインが台無しにならないようにデザインを優先させます。ただし、画像が実際にそのアスペクト比ではない場合は、引き伸ばし(object-fit: fill;)、レターボックス(object-fit: contain;)、切り抜き()のいずれかになります。どれも理想的ではありませんが。

<picture>とアートディレクションに関してFirefoxでサポートされていないことは、aspect-ratioとメディアクエリを使用することで対応できます。しかし、そのバグを早く修正して、ハックする必要がないようにしてほしいところです。

以上です!
この問題に対する真の解決方法を得るまでに10年かかりましたが、現在ではaspect-ratioを使用する方法、widthheightを使用する方法のいずれか適切な方を使用してレイアウトシフトを回避できるようになりました。

sponsors

top of page

©2022 coliss