yn2011's blog

技術メモ

img 要素に width と height を指定してもレイアウトシフトする原因は属性値が auto だからかも

環境

tldr;

img 要素に width と height を指定するとレイアウトシフトしないと聞いて試してみたが、レイアウトシフトが改善しないケースがあった。img 要素の width と height に auto を指定していて画像を読み込むまで幅を決定できない場合にレイアウトシフトが発生する。

width と height でレイアウトシフトを防ぐ

以下の HTML をブラウザで描画するとレイアウトシフトが発生する

<img src="https://via.placeholder.com/1200x600" />
<p>text</p>

画像が後から読み込まれ、text が下に移動する。

以下の場合はレイアウトシフトしない。ブラウザがアスペクト比を理解するからである。この例では実際の画像の幅と属性値が一致するが、一致していない場合でも width と height の属性値で描画を決定できるのでレイアウトシフトは回避できる。

<img src="https://via.placeholder.com/1200x600" width="1200" height="600" />
<p>text</p>

詳細な説明は以下を参照

width:auto と height:auto を組み合わせるとレイアウトシフトする

属性値でアスペクト比を伝えている場合でも、以下のスタイルを当てるとレイアウトシフトが発生する。

.image {
  width: auto;
  height: auto;
}
<img src="https://via.placeholder.com/1200x600" width="1200" height="600" class="image" />
<p>text</p>

これは、width と height 属性を HTML で指定していたとしても、CSS で値を auto にすると画像が読み込まれるまで幅と高さを解決できないからだと考えられる。

例えば、以下のように width と height を 200 に指定したとしても、auto のスタイルを当てている場合には無視されて実際の画像の大きさ(1200*600) で描画される。

 <img src="https://via.placeholder.com/1200x600" width="200" height="200" class="image" />

なのでブラウザは画像が読み込まれた後にレイアウトシフトを発生させるしかない。

片方だけ auto の場合はレイアウトシフトしない

width と height で片方が auto の場合は、属性値とアスペクト比から片方を事前に計算可能なのでレイアウトシフトを回避できる。ただし、画像の実際のアスペクト比と、width と height から計算するアスペクト比が一致していることは前提である。

これはOK

.image {
  width: auto;
}

これもOK

.image {
  height: auto;
}

これも大丈夫

.image {
  width: 100%;
  height: auto;
}

img 要素に指定した width と height から計算されるアスペクト比と、実際の画像のアスペクト比が一致しない場合

img 要素に指定した width と height から計算されるアスペクト比と、実際の画像のアスペクト比が一致しない場合でも片方が auto であれば画像が持つ正しいアスペクト比で幅を算出してくれる。

例えば、これはアスペクト比が 1 / 1 だと認識されそうだが

 <img src="https://via.placeholder.com/1200x600" width="200" height="200" class="image" />

height を auto にしている場合は、画像を読み込んだ後に本来のアスペクト比を利用して height = width / 2 で 100 としてブラウザは表示してくれる。

しかし、本来の画像のアスペクト比は画像をブラウザが読み込むまでは解決できないはずであるため、読み込み後にレイアウトシフトは発生する。この場合のレイアウトシフトは先に height=200 で確保した縦幅が 最終的に 100 になるので下から上へと要素が移動することになる。

逆に、上記の例で width を auto にした場合は(少なくとも上下の)レイアウトシフトは発生しない。画像の本来のアスペクト比が何であれ、高さが 200 であるということは変わらないからだ。左右に要素が存在する場合には横幅が画像を読み込んだ後に決定されるのでレイアウトシフトするはずだ。

疑問

CSS の width と height は初期値が auto なのに、CSS で明示する場合としない場合で挙動が変わるのはなぜ?

参考