imgタグで行うウェブサイトの最適化

# imgタグ
HTML内に画像を埋め込むためのタグです。単純なタグですが、正しく設定することでアクセシビリティ、パフォーマンスの向上に繋がるとても興味深い要素となっています。
```html
<img src="画像のURL" alt="画像の説明">
```
これがhtmlで画像を表示するための最も基本的な形です。
`src`属性に画像のURLを渡してどこから読み込むかを指定して、`alt`属性にその画像の説明を記述します。

https://codesandbox.io/embed/img01-wemofp?fontsize=14&hidenavigation=1&theme=dark

## 画像形式
`<img>`で読み込める画像形式はHTML標準では定められていないため、各ブラウザによって異なります。一般的に利用される画像形式は7つの形式です。

|形式  |特徴  |
|---|---|
|APNG  |PNGを拡張してGIFより高い性能でアニメーションに対応した形式、IE非対応|
|AVIF  |画像と動画の両方に対応、WebPよりさらに高い圧縮率を誇るがIE・Safari非対応|
|GIF  |単純な画像やアニメーションに適した形式|
|JPEG  |写真などの色数の多い画像に適した形式|
|PNG  |イラストや図表などに適した形式、透過も可能|
|SVG  |異なる大きさでも綺麗に表示することが可能な形式|
|WebP  |画像とアニメーションを高い圧縮率で扱える形式、IE非対応|

GIFのようなアニメーションであれば`<img>`を用いて表示させられますが、MP4などの動画は`<video>`に埋め込んで利用してください。
IE非対応のサービスでWebPへの変換がデメリットに感じなければWebPを主な画像形式として利用することがおすすめです。WebPはその高い圧縮率から画像をより小さいサイズで扱うことができます。
ロゴやアイコンなどのシンプルな画像の場合はSVGを用いることがおすすめです。シンプルな画像の場合はファイルサイズの面で有利ですし、これらの画像を拡大縮小しても画質が荒くならないです。シンプルな画像というのは図形としてシンプルであることを指していて、文字などを含む画像はここで言うシンプルな画像ではありません。文字などが含まれている場合、SVGでは一見簡易な画像でもファイルサイズが想像よりも大きくなることに注意してください。
画像形式を適切に選ぶだけではなく、選択した画像形式の中で適切な最適化手法を取ることにより小さいファイルサイズをユーザーに提供することが可能になります(ある程度の劣化はありますが)。そのため、JPEGを用いる場合はMozJPEGのようなエンコードを行って最適化したものを利用するのもおすすめです。

## 主要な属性
imgには先述の`src`や`alt`など、さまざまな属性を与えることができます。そのうち主要なものとアクセシビリティやパフォーマンスの向上に大きく関わるものを紹介します。

### src属性
`src`属性は`<img>`にとって唯一の必須属性です。空文字列を渡した時や値を渡さなかった場合にはエラーとなってしまいます。
`src`属性に渡す画像のURLの一般的な指定方法は3種類あります。絶対パスと相対パス、ルートパスです。
絶対パスは`https://example.com/example01.jpg`のように完全なURL、相対パスは`./example01.webp`のように呼び出すファイルからの相対的な位置を表す文字列、ルートパスは`/example02.webp`のようにスラッシュから始まるドキュメントのトップからの相対的な位置を表す文字列です。
絶対パスは外部サイトの画像を参照する際に使われることが多く、サイト内部の画像を参照する場合はその性質から相対パスやルートパスを使うことが多いです。

### alt属性
`alt`属性は標準では必須とされていませんが、`<img>`にとってとても重要な属性です。
`alt`属性には`src`属性で指定した画像の説明を書くわけですが、その文字列は画像の代替テキストとして扱われます。画像が表示されない場合は代わりの説明として表示されたり、読み上げソフトで読み上げてくれたり、Googleなどの検索エンジンが画像を理解するための助けになるなど多くの場面で役立ってくれます。つまり、`alt`属性を設定することでアクセシビリティの向上や、SEO対策を図ることができるというわけです。
`alt`属性はただ設定するだけでは意味がなく、画像の内容を表す具体的な説明を書いて初めて成り立つことに注意してください。

例えば以下のような画像があったとき`alt`属性には`alt="夜空に浮かぶ満点の星の写真"`のように設定します。
![starry_sky.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/591069/3a1949ec-6cec-91a6-6b36-5b678b6a12ae.jpeg)
`alt="starry_sky.jpeg"`や`alt="画像"`のような、それだけを読んでもどのような画像かイメージがつかないものを設定すると却って良くないので注意してください。

装飾するための画像など、コンテンツに画像が存在しなくても成り立つ(スクリーンリーダでの読み上げが必要ない)場面は`alt`属性に空文字列を与えるようにしてください。ブラウザによっては`alt`属性が存在しない場合は画像のURLやファイル名を画像の説明(代替テキスト)として扱い、スクリーンリーダーで読み上げるなど意図しない挙動を引き起こす可能性があります。
さらに、`alt`属性の状態によって暗黙のARIAロールと許可されているARIAロールが異なりますので注意して設定してください。

||暗黙のARIRロール|許可されているARIAロール|
|---|---|---|
|`alt`属性が空以外の文字列|`img`ロール|`button`ロール<br>`checkbox`ロール<br>`link`ロール<br>`menuitem`ロール<br>`menuitemcheckbox`ロール<br>`menuitemradio`ロール<br>`option`ロール<br>`progressbar`ロール<br>`scrollbar`ロール<br>`separator`ロール<br>`slider`ロール<br>`switch`ロール<br>`tab`ロール<br>`treeitem`ロール|
|`alt`属性が空文字列|`presentation`ロール|`presentation`ロール<br>`none`ロール|
|`alt`属性がなし|`img`ロール|なし|

### width、height属性
`<img>`では`width`、`height`属性を用いて幅と高さを指定できます。
```html
<img src="画像のURL" alt="画像の説明" width="200" height="200">
```
`width`、`height`属性には`100`などの数値しか入力できないことに気をつけてください。`width`属性に与えられた数値のpx分だけの幅、`height`属性に与えられた数値のpx分だけの高さが画像に指定されます。cssのようにautoや`ren`などの他の指定方法を扱えないことに注意してください。
その自由さからCSSで幅と高さを指定したくなりますが、属性に指定することで画像を遅延読み込みさせるときに、事前に場所を確保しコンテンツのレイアウトが移動することを防ぐことができます。

### loading
`loading`属性は画像をどのように読み込むかを指定できます。デフォルト値は`eager`となっていて、`<img>`が解釈されると同時にすぐに画像の読み込みを開始します。
`loading`属性は`eager`の他に`lazy`という値もとれます。これを指定すると画像を遅延読み込みします。初期画面にない画像はスクロールして画像が表示される直前で読み込みが開始されるので、初回の読み込み量が少なくなります。最初に表示される画面もしくはその近い位置の画像は初期の読み込みファイルとして扱われます。
`lazy`を使う場合は`<img>`に`width`、`height`属性を付与しないと、読み込みが完了するまで画像のサイズを認識できず、読み込まれる度にレイアウトが再計算されてしまいます。これによって[CLS](https://web.dev/cls/)低下の恐れがあるので`width`、`height`属性を付与することを忘れないようにしてください。

https://codesandbox.io/embed/img02-yv2f9b?fontsize=14&hidenavigation=1&theme=dark

### fetchpriority
実験的な機能ですが、画像読み込みの優先順位を与える機能として、`fetchpriority`属性があります。対応しているブラウザを知りたい方は[こちら](https://developer.mozilla.org/ja/docs/Web/API/HTMLImageElement/fetchpriority#%E3%83%96%E3%83%A9%E3%82%A6%E3%82%B6%E3%83%BC%E3%81%AE%E4%BA%92%E6%8F%9B%E6%80%A7)を参照してください。
この属性は`high`、`low`、`auto`の3つの値を取ります。`high`は他の画像に比べて優先度が高いことを、`low`は低いことを示しています。`auto`はデフォルトで読み込みの優先順位を自動で決定してくれます。
サービスロゴなどの優先順位を高くして、表示速度を体感速くする工夫などを凝らすことができます。

## 画像の出し分け
画面サイズによる複数画像の出し分けはCSSやJavaScriptで制御すると該当の画面サイズでは読み込む必要がない画像まで読み込んでしまうことがあります。
必要最低限の画像しか読み込まないようにするための方法として`<img>`の`srcset`属性と`sizes`属性の2種類の属性を用いる方法を用いる方法と、`<picture>`と`<source>`の2つのタグを用いる方法があります(もちろん他の方法も存在します)。
`<img>`の`srcset`属性と`sizes`属性を用いる方法では以下のように書きます。
```html
<img
  srcset="画像1URL 320w,
　　　　　　　　　　　　　　　　　　　　画像2URL ６40w,
          画像3URL 1280w"
  src="画像3URL"
  sizes="(min-width: 1280px) 1280px, (min-width: 640px) 640px, 320px"
>
```
`srcset`属性にはユーザーが使用可能な画像のセットを渡します。画像のセットは画像のURLと選択の材料となる値をスペースで区切ったものをカンマ区切りにした文字列で渡されます。
選択の材料となる値は幅記述子(単位w)と画素密度記述子(単位x)を選択できます。選択の材料となる値の指定がない場合は`1x`として見做されます。1つの`srcset`属性で記述子を混合して利用できないこと、同じ記述子は設定できないことに注意してください。
まずは`sizes`属性が不要な画素密度記述子です。画素密度記述子で指定した場合はブラウザが自動的に適切な画像を割り振ってくれます。`1x`、`1.5x`、`2x`などの数値で指定されることが多く、ユーザーが扱う端末の解像度(デバイスピクセル)を元に割り振ります。具体的には[CSSピクセル](https://developer.mozilla.org/ja/docs/Glossary/CSS_pixel#:~:text=CSS%20Pixel%20%E2%80%94CSS%20%E3%81%A7%E6%8E%A5%E5%B0%BE,%E3%81%AE%E7%89%A9%E7%90%86%E7%9A%84%E3%82%B5%E3%82%A4%E3%82%BA%E3%81%A7%E3%81%99%E3%80%82)に対するデバイスピクセルの割合を計算して割り振りますが、1つのCSSピクセルに対して2つのデバイスピクセルが占めている場合`2x`で指定された画像が表示されるようになっています(2つのCSSピクセルに対して3つのデバイスピクセルが占めている場合は`1.5x`です)。

https://codesandbox.io/embed/img03-oku4sj?fontsize=14&hidenavigation=1&theme=dark

次に幅記述子です。幅記述子は内在的なピクセル数です。MacであればCommnad + Iで表示される画像の情報から見ることができます。
![スクリーンショット 2023-01-19 14.02.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/591069/bbb72a8f-2865-c360-8c3d-8c05976f09a3.png)
幅記述子で画像の横幅を指定した場合は`sizes`属性と組み合わされます。`sizes`属性ではメディア条件とその時に表示させたい画像の幅を設定します。例として利用した以下の条件を考えます。このケースでは1280px以下であれば1280pxの画像を表示して、1280px未満640px以上であれば640pxの画像を表示して、それ以外は320pxの画像を表示するようになります。
```
sizes="(min-width: 1280px) 1280px, (min-width: 640px) 640px, 320px"
```
これらの`srcset`属性があるときに`src`属性として指定された画像は`srcset`属性を利用できないブラウザなどで表示される画像となります。

https://codesandbox.io/embed/img04-ff2m4w?fontsize=14&hidenavigation=1&theme=dark

`<picture>`と`<source>`の2つのタグを用いる方法は以下のように書きます。
```html
<picture class="sample">
  <source srcset="画像1URL" media="(min-width: 1280px)" />
  <source srcset="画像2URL" media="(min-width: 640px)" />
  <img src="画像3のURL" alt="画像の説明"/>
</picture>
```
1280px以上の幅を持つ画面であれば画像1を、1280px未満640px以上の幅を持つ画面であれば画像2をそれ以外(640px未満)の画面であれば画像3を読み込んで表示させるようなコードとなっています。
定義された順番に表示する画像を決めているので、下のようなコードでは640px以上の幅を持つ画面であれば常に画像2が表示されるようになってしまいます。誤った表示にならないように条件が厳しい物から列挙して定義するように注意してください。
`<picture>`と`<source>`の2つのタグを用い手出し分けされた画像の代替テキストは`<img>`の`alt`属性によって定まります。

https://codesandbox.io/embed/img05-bzmxmi?fontsize=14&hidenavigation=1&theme=dark

# まとめ
この記事では`img`タグの基本的な使い方からアクセシビリティ、パフォーマンス向上のための方法までを説明しました。
近年は[NextjsのImage](https://nextjs.org/docs/api-reference/next/image)のようにフレームワークで最適化をしてくれる場合もあります。ここで説明したことだけを行っているわけではないですが、どのように設定されていてどのように働くかを知っておくとより有効的に利用できるのではないでしょうか。
画像の最適化を行ってくれるフレームワークを利用していない場合は、いくつかの簡単な設定でサイトのアクセシビリティとパフォーマンスの向上に寄与することができるので今後`img`を用いる時は意識して実装していただければ幸いです。