【HTML】ネイティブHTMLだけで作れるUIコンポーネント


UIコンポートネント作成の方法は今や様々な方法がありますが、JSを減らす、ないしは無くすことができればそれに越したことはありません。

ネイティブHTMLを活用することで、JSやCSSへの依存をある程度減らすことができるかもしれません。[Cool native HTML elements you should already be using](https://harrisonbroadbent.com/blog/cool-native-html-elements/)を読んでとても勉強になったので、備忘録として残したいと思います。

## ネイティブHTMLを使って嬉しいのか

以下のような利点があると思っています。

- JSの依存関係を考えなくても良くなる場合がある
- ブラウザネイティブなため、互換性の心配がUIライブラリと比較して小さい
- ネイティブHTMLはアクセシビリティへの配慮ができているので、ネイティブHTMLをうまく活用できるとアクセシビリティへの対策もできる

## モーダルウィンド
過去にまとめたことがあるので、よければ別の記事（[HTML】便利そうな dialog 使ってみた感想](https://qiita.com/kobayashimakoto/items/c5248c23c7d13cc640c5)）を参照してください。

- `dialog`要素にによりキーボードで操作可能
- モーダルを開くとフォーカスが自動的にモーダルの中に移動
- ESCキーでモーダルを閉じる
などが自動で実装されており大変便利です。

<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="WbbJJBK" data-pen-title="native html modal" data-user="knicisow-the-vuer" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">


## アコーディオン

```html
<details>
  <summary>クリックして開く見出し</summary>
  ここに隠されたコンテンツが入ります
</details>
```
`details`要素で追加の詳細情報を確認できるようになります。
ただ、`details`要素には必ず`summary`要素を含まなければならず、詳細情報が非表示になった状態では、`summary`要素の内容のみが表示されます。

**メモ**
- `details`要素はインタラクティブな要素の一つでもあるので、デフォルトでキーボードによるフォーカスが可能
- `details` 要素には`open` 属性をつけることができるので、デフォルトで開いた状態も作れる
- `name` 属性を活用することで排他的アコーディオンになる
- アコーディオンのネストも可能

### デモ
<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="vEEjMNY" data-pen-title="native html accordion" data-user="knicisow-the-vuer" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/knicisow-the-vuer/pen/vEEjMNY">
  native html accordion</a> by 小林誠 (<a href="https://codepen.io/knicisow-the-vuer">@knicisow-the-vuer</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://public.codepenassets.com/embed/index.js"></script>



## スライダー
`input` 要素の `type='range'` を活用すると実装できます。

スライダーをつまみで調整できるインターフェースなため、大まかな値を直感的に操作しやすい反面、細かい値の微調整には向きません。

ちなみに ARIAロールは `slider` です

### メモリ付き
範囲入力に`datalist`要素を使用することで、書く値のチェックマークを表示できます。
以下のように実装します。
```html
<input type="range" min="1" max="5" list="steps" />

<datalist id="steps">
  <option>1</option>
  <option>2</option>
  <option>3</option>
</datalist>
```

### リアルタイム値表示
少しのJSを使用することで簡単に表現できます。
`<input type='range'>`が動かされると、すぐ後ろの`<output>`要素の値をスライダーの値に同期させます。

```html
<input type="range" value='50' min="1" max="100" oninput="this.nextElementSibling.value = this.value" />

<output>50</output>
```

デモ
<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="JoomMQa" data-pen-title="native html slider" data-user="knicisow-the-vuer" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/knicisow-the-vuer/pen/JoomMQa">
  native html slider</a> by 小林誠 (<a href="https://codepen.io/knicisow-the-vuer">@knicisow-the-vuer</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://public.codepenassets.com/embed/index.js"></script>

[output要素](https://developer.mozilla.org/ja/docs/Web/HTML/Reference/Elements/output)はアプリケーションの実行結果やユーザーの操作結果を表示するために使用します。
`span`などでも代用できそうな気もしますが、
- name属性で名前をつけられる（formの値として送信はされない）
- フォームに配置するとリセットボタンで初期値に戻せる
- ARIAロールが `status`であり、値が変化した際に支援技術に対して通知する機能を持つ

と機能を持つので、`output`を使える場面ならこれを活用する方がいいでしょう。

## プログレスバー
nativeなhtmlがサポートしています。
参考: [progress](https://developer.mozilla.org/ja/docs/Web/HTML/Reference/Elements/progress)


<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="raaqJBJ" data-pen-title="native html progressbar" data-user="knicisow-the-vuer" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/knicisow-the-vuer/pen/raaqJBJ">
  native html progressbar</a> by 小林誠 (<a href="https://codepen.io/knicisow-the-vuer">@knicisow-the-vuer</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://public.codepenassets.com/embed/index.js"></script>

これは動的な進捗状況を表現するためのものなので、単なる静的なグラフとして表現したい場合は`meter` を使用します

### 静的なグラフ meter

棒グラフのようなゲージとしてレンダリングされます。

```html
<meter value='0.75>0.75</meter>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2508607/0428da34-4baa-44c8-86d4-f603b2c9e42c.png)

注意:
`value`だけでUIは表現できますが、`meter`要素を解釈しないブラウザに対するフォールバックとして、テキストを入れておくことが無難だそうです。

参考: [meter](https://developer.mozilla.org/ja/docs/Web/HTML/Reference/Elements/meter)

## 検索可能なドロップダウン
`datalist`や`optgroup`を活用することで検索したり、グループ化を簡単に実装できます。

### 入力補完の候補を表示
`datalist`要素に `id`属性を指定し、その`id`を`input`要素の`list`属性で指定することで、`datalist`の子要素である`option`要素が補完候補になります。

入力しながら絞り込みができるので、検索フォームで活用できます。

```html
<!-- datalist -->
<label for='browser'>リストを選択してください</label>
<input list='browser-names' name='browser' id='browser' />

<datalist id='browser-names'>
  <option value='Edge' />
  <option value='Firefox' />
  <option value='Chrome' />
  <option value='Safari' />
</datalist>
```

ちなみに`input`要素に`type='range'`を指定すると `option` で指定した位置にメモリがつくスライダーとなります。

### 候補のグループ化
`select`要素の中で、`optgroup`要素を使用すると、`option`要素をグループ化できます。
```html
<!-- group list -->
<label for='food'>食べ物を選択してください</label>
<select id='food'>
  <optgroup label='Fruits'>
    <option value=''>🍏Apple</option>
  </optgroup>
    <optgroup label='Vegetables'>
    <option value=''>🍅Tomata</option>
  </optgroup>
    <optgroup label='Desserts'>
    <option value=''>🍮Pudding</option>
  </optgroup>
</select>
```

注意:
- `optgroup` には `option` 以外を入れることができない
- `optgroup`を入れ子にできない

参考: [optgroup](https://developer.mozilla.org/ja/docs/Web/HTML/Reference/Elements/optgroup)

### デモ
<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="myyzXJd" data-pen-title="native html datalist" data-user="knicisow-the-vuer" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/knicisow-the-vuer/pen/myyzXJd">
  native html datalist</a> by 小林誠 (<a href="https://codepen.io/knicisow-the-vuer">@knicisow-the-vuer</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://public.codepenassets.com/embed/index.js"></script>

## キャプション付き画像
`figcaption`を使用するとキャプションを表現できます
`figcaption`は`figure`要素のなかの先頭か末尾におく必要があります。

以下のように書きます
```html
<figure>
  <img src="image.jpg" alt="..." />
  <figcaption>画像の説明</figcaption>
</figure>
```
<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="bNNmLEw" data-pen-title="Untitled" data-user="knicisow-the-vuer" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/knicisow-the-vuer/pen/bNNmLEw">
  Untitled</a> by 小林誠 (<a href="https://codepen.io/knicisow-the-vuer">@knicisow-the-vuer</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://public.codepenassets.com/embed/index.js"></script>

注意:
`figcaption`のスクローンリーダーの読み上げ対象となるので、`alt` と同じ文言を書いた場合、2度同じ内容が読み上げられるので注意が必要です。

参考: [figcaption](https://developer.mozilla.org/ja/docs/Web/HTML/Reference/Elements/figcaption)

## マークされたテキスト
強調やハイライトを表現し、強調したいケース以外に、**検索語句をハイライトとしたい**場合でも使用できます。

```html
<mark>ここテストに出ます！</mark>
```
<img width='200' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2508607/6fbf7026-0f07-4750-b763-b3a8e2da9330.png' />


## まとめ
ここまで見ていただきありがとうございます。

自分も過去にspanタグやjsを無理やり駆使して、車輪の再発明をしたことがあります。😅

こういったネイティブHTMLの挙動を知っておくと、実装を効率化させたり、よりアクセシビリティに配慮されたUIコンポーネントを実装できます。

誰かの参考になると嬉しいです。

## 参考
- [HTML解体新書](https://www.amazon.co.jp/HTML%E8%A7%A3%E4%BD%93%E6%96%B0%E6%9B%B8-%E4%BB%95%E6%A7%98%E3%81%8B%E3%82%89%E7%B4%90%E8%A7%A3%E3%81%8F%E6%9C%AC%E6%A0%BC%E5%85%A5%E9%96%80-%E5%A4%AA%E7%94%B0-%E8%89%AF%E5%85%B8/dp/4862465277)
- [Cool native HTML elements you should already be using](https://harrisonbroadbent.com/blog/cool-native-html-elements/)