- HTML のリストは視覚的な見た目ではなく、意味と相互作用の方法で選ぶべきであり、コントロール・順序・説明・メニュー・非順序リストに分かれる
- 固定された選択肢には
<select>/<option>、候補入力には <datalist> が適切であり、multiple、optgroup、size、value の動作を区別して理解する必要がある
<ol> は順序を入れ替えると意味が変わる手順・出来事・連続体に使い、reversed は番号だけを逆順にするもので、実際の項目順は変えない
<dl> は HTML5 で定義リストを超えて説明リストへと拡張され、用語と値、メタデータ、JSON デバッグのようなキー・バリューの組に適している
<menu> はツールボタンのようなコマンドの一覧に使い、nav とは意味と許可されるコンテンツが異なり、それ以外の一般的なリストは <ul> が担う
コントロールリスト: <select>/<option> と <datalist>
- フォームでもユーザーが相互作用する リスト を構成できる
-
固定された選択肢は <select> と <option>
- ユーザーがリスト内の項目だけを選べるようにしたい場合は
<select> と <option> を使う
- 言語リストの例では、
Select a Language、English、French、Spanish、Portuguese といった <option> を <select name="languages"> の中に配置する
- デフォルトの
<select> は、ちょうど 1 つの選択肢だけを選ばせる
- 複数項目を選べるようにするには
multiple 属性を追加する
multiple を使うとリスト表示が変わってすべてのオプションが見えるようになり、ユーザーは Shift または Cmd + クリックで複数項目を選択できる
- 実際に
select と option を使えば、role="listbox" の付いたリスト要素に aria-multiselectable を手動で付けなくても、ブラウザのデフォルトセマンティクスが処理してくれる
-
関連するオプションは <optgroup> でまとめる
- 言語を語族ごとにまとめたいなら、
<optgroup> でオプションの一覧をグループ化する
- 例では
Germanic、Romance、Celtic という label を持つ <optgroup> を作り、各グループに English、French、Spanish、Portuguese、Irish、Welsh を配置する
- 特定の下位グループを選択できないようにしたい場合は、その
<optgroup> に disabled 属性を追加する
- 例では
Celtic グループに disabled を付け、Irish、Welsh を含むグループを無効化している
-
標準の HTML 機能で改善する
- グループ間に視覚的な区切りが必要なら、
<select> 内で許可されている <hr> を使える
size 属性は一度に表示する項目数を制御するので、長いリストで役立つ
size と optgroup を一緒に使うと、グループラベルも表示領域を消費する
- 例では
<select name="languages" size="4" multiple> に Germanic、Romance、Celtic、Afroasiatic の各グループと、グループ間の <hr /> を入れ、Hebrew、Arabic まで含めている
候補リスト: <datalist>
- ユーザーにリストからの選択を強制するのではなく、リストを 候補として提示したいなら
<datalist> を使う
<datalist> は 2 段階で接続する
<datalist> を作成し、id を付与する
- 対応する
<input> の list 属性にその id の値を入れる
- 言語候補の例では、
<datalist id="languages"> の中に English、French、Spanish、Portuguese、Irish、Welsh、Hebrew、Arabic の各オプションを置き、<input name="language" list="languages"> で入力フィールドと結び付ける
-
<option value> の動作
<option> のデフォルト値は囲まれたテキストであり、value 属性があるとその値がデフォルト値を上書きし、テキストはラベルのように振る舞う
<select> ではユーザーはテキストだけを見るので大きな問題にはなりにくいが、<datalist> ではユーザーがラベルを見て選んだあと、入力欄には value が入るため混乱を招くことがある
- 例で
<option value="cy">Welsh</option> を選ぶと、ユーザーには Welsh が見えるが、入力には cy が入る
<datalist> を使うときは、挿入される値がラベルではなく value であることを前提にすべきである
-
複数の入力タイプと組み合わせる
<datalist> はテキストオプション専用ではなく、ほかの input タイプとも組み合わせて使える
- 週単位の選択例では、
<input type="week" name="week" id="camp-week" min="2026-W2" max="2026-W51" list="preferred-weeks" /> に <datalist id="preferred-weeks"> を接続する
- 候補の週は
2026-W22、2026-W23、2026-W24、2026-W25 である
-
<input type="range"> と組み合わせる
<datalist> は文字列値だけに限定されず数値でも動作するため、range 入力と組み合わせれば、範囲上にラベル付きの地点を作れる
- チップ率入力の例では、
<input type="range" name="tips" id="tips" min="0" max="50" step="1" list="recommended-tips" /> に <datalist id="recommended-tips"> を接続し、10%、18%、30%、45% のラベルを置く
- Chrome 系では
@supports (x: attr(x type(percentage))) で attr() の label 値を読み取り、type() で値をパーセンテージとして宣言したうえで、オプションを position: absolute で配置する
- Firefox 向けの方法では
@supports not (x: attr(x type(percentage))) を使い、値は ::before で表示される
- この方法は、すべてのブラウザが同じように動作したり、画面上で同一に表示されたりすることを保証するものではない
順序リスト: <ol>
- 特定の順序で読む必要がある項目の集まりには
<ol> を使う
- 項目の横に数字を表示したいかどうかではなく、項目の順序を入れ替えると リストの意味が変わるかどうかが基準になる
<ol> に適したコレクションは、アルゴリズム、出来事の連続、増加または減少する連続体上の項目、レシピ、アルファベット順の一覧である
- バナナブレッドのレシピ例では、オーブンの予熱と型への油塗り、材料を混ぜる、生地を流し込む、60 分焼くまたはつまようじがきれいに出てくるまで焼く、ワイヤーラックで冷ますという順序を
<ol> で表現する
- アルファベット順の材料一覧もアルファベットという連続体に従うため
<ol> に該当し、baking soda、bananas、brown sugar、butter、eggs、flour、salt の順に並べられる
-
順序リストと非順序リストのネスト
- よく構造化された順序リストは、ブラウザでのレンダリングを見なくても、何がどの順で起こるべきかを読み取れる
- レシピの例では、上位のステップに
<ol> を使い、ステップ内で順序が重要でない項目は <ul>、再び順序が重要な下位ステップは <ol> としてネストする
- 構造としては
Prepare、Mix、Pour、Bake、Cool の上位順序を保ちつつ、Prepare と Bake の中の並列項目は <ul> で、Mix と Cool の中の手順は <ol> で表現する
-
reversed
reversed 属性は番号付けの順序を昇順から降順へ変える
- 実際のリスト項目の順序は変えない
most to least のように、多いものから少ないものへ見せる材料と分量の一覧に使える
- 例では
<ol reversed> に eggs (2)、flour (2 cups)、bananas (2) (mashed)、brown sugar (¾ cup)、butter (½ cup)、baking soda (1 teaspoon)、salt (¼ teaspoon) を入れる
-
JavaScript で実際の項目順を逆にする
- リストを実際に逆順にするには、JavaScript で
li の子要素を逆順に再配置し、reversed 属性をトグルできる
- 例の関数では
list.querySelectorAll('li') の結果を配列にし、.reverse() した後、list.innerHTML = '' で空にして list.append(...children) で再び追加する
- 最後に
list.toggleAttribute('reversed') を呼び出す
- 例のイベントでは
orderedList.addEventListener('dblclick', (evt) => { reverseList(orderedList) }) により、ダブルクリック時に反転を実行する
-
start
start 属性は、1 つの巨大なリストの代わりに複数の順序付きリストへ分割するとき、番号の連続性を維持するために使う
- バナナブレッドのレシピ例では、
Prepare は ul のままにし、Mix は <ol start=2>、Pour は <ol start=5>、Cool は <ol start=7> のように、続きのステップ番号を各リストに指定する
- 途中に
6: Bake のような非順序リストで表現されたセクションがあっても、その後の Cool の ol を start=7 で始めれば、手順全体の番号の流れを維持できる
説明リスト: <dl>、<dt>、<dd>
- 説明リスト(description list) は、すべての内容を
ol や ul に無理やり押し込まなくても済むようにしてくれるリスト型である
-
HTML 4 の定義リスト
- HTML 4 では
description list ではなく 定義リスト(definition list) と呼ばれており、定義を提供する狭い用途に合わせられていた
- 構造は、定義する用語である
<dt> と、定義内容である <dd> から成り、意味的に正確に使うには定義される用語を <dfn> で囲む
- 例では
throw、yeet を同じ定義に結び付け、no cap、bet にはそれぞれ定義を付けている
<dl>
<dt><dfn>throw</dfn></dt>
<dt><dfn>yeet</dfn></dt>
<dd>Verb. To discard at a high velocity</dd>
<dt><dfn>no cap</dfn></dt>
<dd>Interjection. Expresses authenticity and truthfulness, sometimes surprise.</dd>
<dt><dfn>bet</dfn></dt>
<dd>Interjection. Expresses agreement and affirmation.</dd>
</dl>
-
HTML5 で広がった意味
- HTML5 では定義だけに限定されない 説明リスト となり、「用語と値の集合」があるときに使えるようになった
- HTML5 では、関連する
<dt> と <dd> をまとめるために、非意味的ラッパーである <div> が許可されている
- ブラウザエンジンの例では、
Chrome、Opera、Brave、Edge を Blink-based browsers にまとめ、Firefox、Tor、Librewolf を Gecko-based browsers にまとめている
<dl>
<div class="dl-item">
<dt>Chrome</dt>
<dt>Opera</dt>
<dt>Brave</dt>
<dt>Edge</dt>
<dd>Blink-based browsers</dd>
</div>
<div class="dl-item">
<dt>Firefox</dt>
<dt>Tor</dt>
<dt>Librewolf</dt>
<dd>Gecko-based browsers</dd>
</div>
</dl>
-
メタデータと JSON デバッグ
- 事実とラベルの連なりであれば、メタデータの表示に説明リストを使うのが適している
- ユーザープロフィールも
<dl> に入れられ、例では First Frank、Last Taylor、Age 44、Job Writer、Handle Paceaux を <dt> と <dd> の組で表現している
- 単一ページアプリケーションにおける JSON デバッグ 用途でも説明リストを使える
DebugJson の例では、オブジェクトの各 key, value を Object.entries(obj) で走査し、キーは <dt><var>...</var></dt>、値は <dd><code>...</code></dd> としてレンダリングする
- 値がオブジェクトで配列でない場合は
DebugJson.createDl(value) を再度呼び出してネストされた <dl> を作り、それ以外の値は value.toString() を <code> の中に入れる
const debugJson = new DebugJson({foo: 'bar', arr: ['a', 'b'], car: 1}, '.container')
debugJson.render();
- 説明リストは キー・バリューの組 という要件に幅広く適合する
メニュー: <menu>
menu 要素はコマンドの一覧を表し、コンテンツのレンダリングよりもインタラクティブな Web により近い
menu は、リッチテキストエディタのテキスト修正コントロールのような「ツール」に当たるボタンの一覧に向いている
- リッチテキストエディタの例では、
Strong、Emphasize、Strike ボタンを menu 内の li に配置する
<menu>
<li><button onclick="strong()">Strong</button></li>
<li><button onclick="emphasize()">Emphasize</button></li>
<li><button onclick="strike()">Strike</button></li>
</menu>
- HTML 仕様上、これは ツールバー(toolbar) 用途であり、インタラクティブなページでツールボタンがある場所は
menu に入る可能性が高い
- 動画コントロールの例も
menu に入り、button に commandfor="vid-123" と command="--play"、--mute、--fullscreen を持たせる
<div class="player player--video">
<video source="whatever.mp4" id="vid-123"></video>
<menu>
<li><button commandfor="vid-123" command="--play">Play</button></li>
<li><button commandfor="vid-123" command="--mute">Mute</button></li>
<li><button commandfor="vid-123" command="--fullscreen">Fullscreen</button></li>
</menu>
</div>
非順序リスト: <ul>
ul は、ほかのリスト型や nav では解決できない残りのリスト要件を受け持つ 包括的なリストである
- 以前の HTML では、順序付きリストと非順序リストの違いは、数字か箇条書きかといった視覚的差異に近かった
- 現在はアクセシビリティ、スクリーンリーダー、検索エンジン最適化を考慮するため、視覚的表示よりも 順序が意味を持つかどうか に注目すべきである
- バンドメンバーの一覧は順序が重要ではないため
ul に該当する
<h3>Beatles</h3>
<ul>
<li>John Lennon</li>
<li>Paul McCartney</li>
<li>Ringo Star</li>
<li>George Harrison</li>
</ul>
<ul>
<li>Beatles</li>
<li>Rolling Stones</li>
<li>Van Halen</li>
<li>Foo Fighters</li>
</ul>
1件のコメント
Hacker Newsのコメント
例を試しただけでも、datalist は Mobile Safari ではあまりうまく動かないように見える
これほど大きな市場で互換性の問題があるなら、実際のところ使う価値のあるシナリオはほとんどないと言えてしまうかもしれない
望んではいなかったけれど、まさに必要だった 現実の冷や水 のような情報だ
10年以上前に、UI でかなり攻めた入力候補ウィジェットを使うプロジェクトをやったことがあり、そのときは jQuery プラグインを使っていた
フロントエンドで最も複雑な部分で、実質的にそのプロジェクトで jQuery を使う主な理由だった
この記事を読みながら、そのフロントエンドを軽量な最小 JS 版として作り直すのは朝飯前だろうと思ったが、ユーザーの端末に自分の環境をそのまま配送できるわけではない以上、現実はそうではない
それでも最近の HTML 仕様 に含まれている機能はかなり印象的だ
高校のときに XHTML を読んで以来、仕様の変化をほとんど追えていなかったので、ときどき何が変わったのか見直したほうがよさそうだ
ただ、ブラウザ互換性は当時も今も相変わらず頭痛の種だ
datalist の例 は自分の iPhone では確かに動く
ネイティブ iOS キーボード上のオートコンプリート候補領域に統合される
ただし、すべての候補を見て回る方法はなく、そもそもそれが datalist の想定された使い方でもない気がする
ただ、
groupのdisabled属性は確実に動いていないAndroid の Firefox でも動かない
初めての職場で働いていたずいぶん前には、Firefox で datalist が動かず、そのせいで Firefox が対応ブラウザ一覧から外された
Chrome 以外のブラウザをサポートしようとすると、長いこと問題になってきた機能だ
iOS の GBoard と一緒に使うとうまく動かない
optgroupにdisabled属性を追加して一部のオプションを選べなくする例は、Mobile Safari では壊れているようだ実際には無効化されず、無効項目もそのまま選択できてしまう
最新の Safari では動くはずなので、壊れているというより妙な状態に近い
https://caniuse.com/mdn-html_elements_optgroup_disabled
Safari のバグ の可能性がありそうだ
基本的なこと以上に ネイティブ HTML を使いにくい理由はこういうところにある
十分に読み込んで確信を持ってこういう記事を書けるだけ知っていても、コメント欄には結局ブラウザやデバイスの組み合わせごとの奇妙な挙動、制限、未対応情報が付いて回る
divまみれは反対側に振り切りすぎた選択かもしれないが、それでも少なくとも奇妙な挙動や限界はかなり一貫していて目に見えやすい自分で書いたものやフレームワークが生成したものと、より一貫して噛み合うからだ
面白くて包括的な記事だ
残念ながら今では HTML を学ばずに React に直行する開発者 が多く、今や LLM まであるので、HTML をまったく学ばない可能性も高い
そのため、単純な HTML で十分な場面でもまず React コンポーネントを探すことになる
それでも問題ないと思う
最初に XML を使わなければならなかった頃は、XML 仕様 を学んで自分で出力しなければならなかった
シリアライズライブラリが事実上なかった時代だ
その後の世代が XML、のちには JSON を交換形式として使いながらも完全には学んでいないのを見てきたが、特に問題は起きなかった
AJAX も最先端の流行語から、略語の意味を誰も知らない段階へ移り、今ではその用語自体をあまり認識していない人がほとんどだ
AJAX は死んだのではなく、あまりに当たり前になって、もはや別の言葉が要らなくなっただけだ
自分の問題は、20年前に HTML を徹底的に学び、その後どう変わり改善されたのかは偶然少しずつ知るだけだったことだ
CSS はなおさらそうだ
正直、HTML は面倒だ
たとえばコントロールの一部をスタイルする HTML 的なアプローチは 疑似クラス を使うことだが、ブラウザごとにセレクタが違うこともある
そうなると本当に正しく動くのか分からないので、ブラウザ別テストをしなければならない
React のほうが簡単なだけでなく、より信頼できる
React と
div群で作れば、すべてのブラウザで同じように動くだろうと見込める良い内容だが、
datalistに期待しすぎてはいけない実用的に使うには 接続ポイント が足りず、小さなプロトタイプ以上の用途には向いていない
HTML リンターが本当にこうした違いを見分けるのに役立つのか気になる
この種の セマンティックタグの選択 を強制できるリンターがあるのかも知りたい
セマンティックタグを強制するという感覚はあまりしっくりこない
HTML は何よりも作者が創造的に使えるよう作られているもので、特定のタグを別のタグより強制するのはおかしいと思う
アクセシビリティは重要だが、制約はすでに十分多い
自分が知るいちばん近いものは https://github.com/kristoff-it/superhtml#diagnostics だ
SuperHTML は文法だけでなく、要素のネストや属性値も検証する
HTML 仕様全体を検証コードに実装した他の言語サーバーはない
今日初めて知った
どうしてもっと多くのフレームワークがこれを活用しないのか不思議だ
ユーザー体験の観点では、普通のリストと同じだからだ
コードを理解する助けになるなら使ってもよいが、ブラウザの アクセシビリティツリー やその他すべての面では、ただの順序なしリストにすぎない
それがアクションの一覧だと伝えるには ARIA 属性を追加しなければならない
記事では
role=menuに触れているが、それだけでは不十分で、各項目にもmenuitemロールが必要だWAI Authoring Practices Guide はロールと期待されるインタラクションを説明しているが、コード例をそのままコピーしてはいけないし、とくにナビゲーションメニューにこのロールを使ってはいけない
https://www.w3.org/WAI/ARIA/apg/patterns/menubar/
賢い人は難しい HTML なんて学ばず、
divをいくつも並べて解決する最近の HTML には面白い機能がたくさんある
ある要素が何をすると思うか人に尋ねるのが好きだ
自分も最初はまったく当てられなかった
数年間フロントエンドリードをしていたのに知らなかった有用な情報が多い
会社でも確実に使い始めることになりそうだ
デザイナーたちが datalist のデフォルト外観 を気に入ってくれさえすればよかったのに
ブログ記事自体はとても良いのに、ブログ内の全記事一覧を一度に見る方法がどうしても見つからない
https://blog.frankmtaylor.com/archive ではないし
https://blog.frankmtaylor.com/archives でもなく
https://blog.frankmtaylor.com/posts も違う
https://blog.frankmtaylor.com/all もない
https://blog.frankmtaylor.com/blog でもない
ブックマークが1万件を超える人も多いのだから、説明や本文抜きでこれまで書いた全記事を並べる 単一の一覧ページ があると本当に便利だと思う
言っているのは サイトマップ のことではないだろうか
たいていのブログには、すべての記事を列挙した
sitemap.xmlがあるそれに、235本の記事を全部ざっと見たい理由も少し気になる