ボタンを使おう
(gomakethings.com)- Webインターフェースでは、
<div>の代わりに<button>を使うことが、アクセシビリティと機能の両面で正しい選択 <div>は スクリーンリーダーにインタラクティブ要素として認識されず、キーボードフォーカスやEnter、Spacebar入力にも反応しない[role="button"]や[tabindex="0"]属性を追加しても、フォーカス順序の乱れやキーボードイベント処理の問題が残る- こうした問題を解決するために 複数のイベントリスナーや条件分岐を追加することは不要な複雑さを招く
<button>は アクセシビリティ、フォーカス、キーボード入力処理機能を標準で提供するため、シンプルで標準的な解決策である
間違ったアプローチ: <div> でボタンを作る
- React や HTMX の利用者の間では、
<div onclick="...">の形で モーダルを開くなどのインタラクションを実装する例が多い- 例のコード:
<div onclick="showSignIn()">Open Modal</div>
- 例のコード:
- この方式の問題点
- スクリーンリーダーがその要素を インタラクティブ要素として認識しない
- キーボードで フォーカス移動できない
clickイベントしか動作せず、EnterやSpacebar入力には反応しない
アクセシビリティを「修正」しようとする試みの限界
[role="button"]属性を追加すると スクリーンリーダー認識の問題は解決するが、
フォーカス可能性やキーボード入力処理の問題は依然として残る[tabindex="0"]を追加すればフォーカス可能にできるが、
フォーカス順序が乱れたり予期しない移動が発生したりするリスクがある- キーボード入力を処理するには
keydownイベントをグローバル (document) に登録し、
Enterまたは' '(space) キーを検知して フォーカスされた要素を見つけて実行しなければならないdocument.addEventListener('keydown', (event) => { if (event.key !== 'Enter' && event.key !== ' ') return; const notRealBtn = document.activeElement.closest('[onclick]'); if (!notRealBtn) return; // 実行コード }); - 結果として、
<button>が本来標準で提供している機能を複雑に再実装しているだけになる
<button> が提供する標準機能
<button>要素は次を 自動的にサポートする- 暗黙の役割 (
[role="button"]) - 自動的にフォーカス可能
- フォーカス状態で
EnterとSpacebar入力時にclickイベントを発火
- 暗黙の役割 (
- 同じ動作を
<div>で実装するには 複数の属性やスクリプトが必要だが、
<button>ならたった1行で解決できる<button onclick="showSignIn()">Open Modal</button>
結論: シンプルさが最善
<button>は アクセシビリティ標準を満たしつつコード量を減らす最もシンプルな方法- 不要なイベント処理や属性追加なしに 標準の HTML 要素を使うことが、保守性と効率の面で有利
- 「怠け者の開発者ほど正しい要素を使え」というメッセージとして、
不要な複雑さを避け、標準機能を活用する開発習慣の重要性を強調している
7件のコメント
とても良い文章ですね。本文の要旨は「
htmlタグを意味のある形で使おう」に要約できると思います。div(またはその他の)タグでクリックイベントを提供するのであれば、昔tableタグでレイアウトを組んでいた時代と、まったく変わっていないと思います。もちろん
aria-*属性を入れれば明確にはなるかもしれませんが、そんな苦労をするくらいなら、むしろ適切なタグを使うべきですwwww懐かしいですね(笑)
うちの国の官公庁サイトは
<a>をよく使っていたけど…Hacker Newsの意見
私の不満のひとつは、Webサイトが
onclickハンドラ でナビゲーションを実装していること単に
<a>タグを使えば、新しいタブで開く、アクセシビリティ機器との統合、右クリックメニューなど、すべてが自動でうまく動くナビゲーションなら、JavaScriptスープではなくリンクを使うべき
おそらくフレームワークの影響か、無関心のせいだろう
それでも 従来のやり方 のほうが、UXの面ではほぼ常に優れている
<a>タグを置き換えようとする人たちには、少しくらい不便があってほしいそういう人たちが誤ったパターンを作り、その後の開発者がそのまま真似する
<div>をボタンのように装わなければならなかった場面は、ごくまれだった私はマウスの中ボタンでスクロールすることが多いが、多くのサイトがこれを壊している
左クリックすると安全性確認ページが出るのに、中クリックだとそのまま移動する
onClickになっていた実質リンクである要素まで全部クリックハンドラで処理していて、理解できない
ほとんどのボタンには
type="button"を明示すべきデフォルト値は
submitなので、フォーム内にあると自動的に送信されるおそらく一部の開発者はこれを知らず、それで
<div>を使っているのだと思うデフォルトタイプのボタンは妙な動きをして、JSハンドラを飛ばすこともある
<input type="submit">に相当し、<button>とは異なる<div>を使えばtype="submit"問題は避けられる<div>は最初から空なので、必要な機能だけ追加でき、後から修正もしやすい一方
<button>は、デフォルト動作を理解するためにドキュメントを見る必要がある結局は 明示的な制御 vs 組み込み機能 の選択ということだ
「その目的のために作られた HTML要素をそのまま使おう」という方向に記事を広げてほしい
多くのSPA開発者はHTML要素の意味をよく知らず、毎回車輪の再発明をしている
たとえば標準のdate pickerはあまりに見た目が悪く、JSベースで置き換えたくなる
そのためカスタムボタンが生まれた背景がある
最近は、クリックできる領域を探して画面をあちこち押す世代が生まれている
10年前に誰かが リンクのドラッグ をテキスト選択より優先させたせいで、今ではテキスト選択がほとんど不可能になっている
これを直すにはブラウザをforkしないといけないかもしれない
Alt(またはOption)キーを押せば、リンク内のテキストを選択できる
本当に望んでいない動作だ
macOSの TextSniper アプリを使えば、範囲を選んでOCRでテキストをコピーできる
おかげでGoogle Analyticsも少しは使い物になる
こういう問題はもっと頻繁に語られるべきだ
昔は Select Like A Boss、今は Drag-Select Link Text という名前だ
Chromeのデフォルトスタイルシートに
button {align-items: flex-start}があって、flexboxのサイズ不具合 で長いことハマったそれでも、できる限り正しいHTML要素を使おうとしているが、小さなサイドプロジェクトでは
<div>のほうが楽なときもあるappearance: noneプロパティは、ボタンスタイルの初期化に便利私は
.unbuttonifyクラスを作って、ボタンのように動くが別の見た目にしているできるだけ要素を 本来の意図に沿って使う べき
ボタンについては不満が2つある
ひとつは、どうせ スタイルを当て直さなければならない こと
もうひとつは、ボタンを入れ子にできないという警告だ
これは現実にはかなりよく遭遇する
LLMはこうした 誤ったパターン をよく生成する
ブラウザの標準機能を無視して複雑に実装することが多い
私はClaudeに、こういうコードを単純化しろとよく指示している
TypeScriptでもエラー処理のやり方を妙にしてしまう傾向がある
私はできるだけ ボタンをデフォルトで使う
ただし、実際にはリンクのように振る舞うべき場合は
<a>タグを使う<a>タグだなぜ
<div>を使おうという意見が出るのか不思議だった<div>のほうが 妙な見た目のカスタマイズ に向いているからだろうその結果、ボタンらしくも、ボタンとしても機能しなくなる
<div onclick>で実装していたボタンを使ってください
background, border, outline, appearance, -webkit-appearance, cursor
上書きしなければならないデフォルトのスタイルシートが多すぎます……
だからCSS Resetがあるんですよ。