10 ポイント 投稿者 GN⁺ 2025-11-02 | 7件のコメント | WhatsAppで共有
  • Webインターフェースでは、<div> の代わりに <button> を使うことが、アクセシビリティと機能の両面で正しい選択
  • <div>スクリーンリーダーにインタラクティブ要素として認識されず、キーボードフォーカスや EnterSpacebar 入力にも反応しない
  • [role="button"][tabindex="0"] 属性を追加しても、フォーカス順序の乱れやキーボードイベント処理の問題が残る
  • こうした問題を解決するために 複数のイベントリスナーや条件分岐を追加することは不要な複雑さを招く
  • <button>アクセシビリティ、フォーカス、キーボード入力処理機能を標準で提供するため、シンプルで標準的な解決策である

間違ったアプローチ: <div> でボタンを作る

  • React や HTMX の利用者の間では、<div onclick="..."> の形で モーダルを開くなどのインタラクションを実装する例が多い
    • 例のコード:
      <div onclick="showSignIn()">Open Modal</div>
      
  • この方式の問題点
    • スクリーンリーダーがその要素を インタラクティブ要素として認識しない
    • キーボードで フォーカス移動できない
    • click イベントしか動作せず、EnterSpacebar 入力には反応しない

アクセシビリティを「修正」しようとする試みの限界

  • [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"])
    • 自動的にフォーカス可能
    • フォーカス状態で EnterSpacebar 入力時に click イベントを発火
  • 同じ動作を <div> で実装するには 複数の属性やスクリプトが必要だが、
    <button> ならたった1行で解決できる
    <button onclick="showSignIn()">Open Modal</button>
    

結論: シンプルさが最善

  • <button>アクセシビリティ標準を満たしつつコード量を減らす最もシンプルな方法
  • 不要なイベント処理や属性追加なしに 標準の HTML 要素を使うことが、保守性と効率の面で有利
  • 「怠け者の開発者ほど正しい要素を使え」というメッセージとして、
    不要な複雑さを避け、標準機能を活用する開発習慣の重要性を強調している

7件のコメント

 
come2mecome 2025-11-04

とても良い文章ですね。本文の要旨は「htmlタグを意味のある形で使おう」に要約できると思います。div(またはその他の)タグでクリックイベントを提供するのであれば、昔 table タグでレイアウトを組んでいた時代と、まったく変わっていないと思います。

 
carnoxen 2025-11-11

もちろん aria-* 属性を入れれば明確にはなるかもしれませんが、そんな苦労をするくらいなら、むしろ適切なタグを使うべきですwwww

 
roxie 2025-11-06

懐かしいですね(笑)

 
carnoxen 2025-11-02

うちの国の官公庁サイトは &lt;a&gt; をよく使っていたけど…

 
GN⁺ 2025-11-02
Hacker Newsの意見
  • 私の不満のひとつは、Webサイトが onclick ハンドラ でナビゲーションを実装していること
    単に <a> タグを使えば、新しいタブで開く、アクセシビリティ機器との統合、右クリックメニューなど、すべてが自動でうまく動く
    ナビゲーションなら、JavaScriptスープではなくリンクを使うべき

    • ここ数年、このやり方で実装される例が増えた
      おそらくフレームワークの影響か、無関心のせいだろう
      それでも 従来のやり方 のほうが、UXの面ではほぼ常に優れている
      <a> タグを置き換えようとする人たちには、少しくらい不便があってほしい
    • Reactで始めた開発者が、基本的なHTMLの概念 を学ばないまま、すぐに「面白いこと」へ飛びついたのが問題だと思う
      そういう人たちが誤ったパターンを作り、その後の開発者がそのまま真似する
      <div> をボタンのように装わなければならなかった場面は、ごくまれだった
    • JSベースのスクロールもなくすべき
      私はマウスの中ボタンでスクロールすることが多いが、多くのサイトがこれを壊している
    • この話を聞くと Microsoft Office 365のリンクチェッカー を思い出す
      左クリックすると安全性確認ページが出るのに、中クリックだとそのまま移動する
    • 最近参加したReactプロジェクトでも、ナビゲーションはすべて onClick になっていた
      実質リンクである要素まで全部クリックハンドラで処理していて、理解できない
  • ほとんどのボタンには type="button" を明示すべき
    デフォルト値は submit なので、フォーム内にあると自動的に送信される
    おそらく一部の開発者はこれを知らず、それで <div> を使っているのだと思う

    • OPの長文には、この 核心情報 が抜けていると思う
      デフォルトタイプのボタンは妙な動きをして、JSハンドラを飛ばすこともある
    • デフォルトは <input type="submit"> に相当し、<button> とは異なる
    • 私もこれを 身をもって学んだ
    • <div> を使えば type="submit" 問題は避けられる
      <div> は最初から空なので、必要な機能だけ追加でき、後から修正もしやすい
      一方 <button> は、デフォルト動作を理解するためにドキュメントを見る必要がある
      結局は 明示的な制御 vs 組み込み機能 の選択ということだ
  • 「その目的のために作られた HTML要素をそのまま使おう」という方向に記事を広げてほしい
    多くのSPA開発者はHTML要素の意味をよく知らず、毎回車輪の再発明をしている

    • 要素がもう少し スタイリングしやすければ と思う
      たとえば標準のdate pickerはあまりに見た目が悪く、JSベースで置き換えたくなる
    • プラットフォームをそのまま使え」という話はHTML5以降のフロントエンドでよく出るが、まだどこにでも浸透しているわけではない
    • 実際には、ほとんどの開発者がHTML要素をほとんど知らず、DIVひとつで何でも解決 しようとする
    • 2010年ごろはブラウザごとにボタンのスタイルが違っていて、自作しなければならなかった
      そのためカスタムボタンが生まれた背景がある
  • 最近は、クリックできる領域を探して画面をあちこち押す世代が生まれている
    10年前に誰かが リンクのドラッグ をテキスト選択より優先させたせいで、今ではテキスト選択がほとんど不可能になっている
    これを直すにはブラウザをforkしないといけないかもしれない

    • 私はリンクを ドラッグしてバックグラウンドタブで開く習慣 がある
      Alt(またはOption)キーを押せば、リンク内のテキストを選択できる
    • iOSで電話番号をコピーしようとすると自動で発信されるのも、同じくらい腹立たしい
      本当に望んでいない動作だ
    • 選択できないテキストは気が狂いそうになる
      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 クラスを作って、ボタンのように動くが別の見た目にしている
    • フロントエンド開発者なら CSSの基礎 を知っているべきだという点を強調したい
  • できるだけ要素を 本来の意図に沿って使う べき

  • ボタンについては不満が2つある
    ひとつは、どうせ スタイルを当て直さなければならない こと
    もうひとつは、ボタンを入れ子にできないという警告だ
    これは現実にはかなりよく遭遇する

  • LLMはこうした 誤ったパターン をよく生成する
    ブラウザの標準機能を無視して複雑に実装することが多い
    私はClaudeに、こういうコードを単純化しろとよく指示している
    TypeScriptでもエラー処理のやり方を妙にしてしまう傾向がある

    • LLMは コードを書く能力 は高いが、ソフトウェアエンジニアリングの勘所 は弱い
    • トークン予測という性質上、LLMは複雑なパターンをより選びがちだ
  • 私はできるだけ ボタンをデフォルトで使う
    ただし、実際にはリンクのように振る舞うべき場合は <a> タグを使う

    • URLが変わるならリンク、変わらないならボタンというふうに分けている
    • 「Webアプリ内で移動するハイパーリンク」なら、それは <a> タグだ
  • なぜ <div> を使おうという意見が出るのか不思議だった

    • おそらく <div> のほうが 妙な見た目のカスタマイズ に向いているからだろう
      その結果、ボタンらしくも、ボタンとしても機能しなくなる
    • たとえばTV Tropesのようなサイトは、長い一覧を「フォルダ」形式で折りたたんだり展開したりするが、これを <div onclick> で実装していた
    • 最もよくある理由は、デフォルトのボタンスタイルを上書きするのが面倒だから だと思う
 
nemorize 2025-11-02

ボタンを使ってください

background, border, outline, appearance, -webkit-appearance, cursor
上書きしなければならないデフォルトのスタイルシートが多すぎます……

 
rtyu1120 2025-11-03

だからCSS Resetがあるんですよ。