HTML仕様から見るbuttonタグとaタグの併用について

2025-03-07

はじめに

ボタンの見た目を持ったリンクというのは Web アプリケーションでは広く知られた使われ方です。 よく目にする UI パターンの一つで、ユーザーの行動を促す重要な要素として活用されています。 このような実装において、以下のような HTML パターンを見かけることがあります。

<button>
  <a>button wrap a</a>
</button>
<a>
  <button>a wrap button</button>
</a>

一見すると自然に思えるこれらの実装方法ですが、実は HTML 仕様に違反しています。 本記事では、なぜこれらのパターンが問題なのか、そしてどのように適切に実装すべきかを説明します。

HTML 仕様から見る問題点

Content Model とは

まず、HTML 仕様における Content Model(コンテンツモデル)について理解しましょう。

Each element defined in this specification has a content model: a description of the element's expected contents.

HTML Living Standard - Content models

Content Model とは、各要素に対して「どのような要素を子要素として含むことができるか」を定義したものです。これにより、HTML 要素の構造的な制約が規定されています。

button タグ内の a タグ

この概念を踏まえた上で、<button> タグ内に <a> タグを配置するパターンについて、HTML Living Standard の仕様を見てみましょう。

Content model: Phrasing content, but there must be no interactive content descendant and no descendant with the tabindex attribute specified.

HTML Living Standard - The button element

この仕様は以下のことを示しています。

  1. button タグのコンテンツモデルは「Phrasing content」です
  2. ただし、以下の要素を子孫要素として含むことはできません
    • インタラクティブコンテンツ
    • tabindex 属性が指定された要素

a タグは以下のように Phrasing Content として定義されていますが

Categories: Flow content, phrasing content, interactive content, palpable content.

HTML Living Standard - The a element

同時に、HTML 仕様において以下のように定義されるインタラクティブコンテンツでもあります。

Interactive content is content that is specifically intended for user interaction.

=> a (if the href attribute is present),audio,(if the controls attribute is present),button,details,embed,iframe,img (if the usemap attribute is present),input (if the type attribute is not in the Hidden state)label,select,textarea,video (if the controls attribute is present)

HTML Living Standard - Interactive content

このため、a タグが href 属性を持つ場合はインタラクティブコンテンツとなり、button タグの「インタラクティブコンテンツを含んではならない」という制約に違反することになります。

a タグ内の button タグ

<a> タグ内に <button> タグを配置するケースについて、HTML 仕様では以下のように明確に定義されています。

Content model: Transparent, but there must be no interactive content descendant, a element descendant, or descendant with the tabindex attribute specified.

HTML Living Standard - The a element

この仕様は以下のことを示しています。

  1. a タグのコンテンツモデルは「Transparent(透過的)」です
  2. ただし、以下の要素を子孫要素として含むことはできません
    • インタラクティブコンテンツ
    • 他の a タグ要素
    • tabindex 属性が指定された要素

HTML 仕様において、button タグはインタラクティブコンテンツとして明確に定義されています。

Interactive content is content that is specifically intended for user interaction.

=> a (if the href attribute is present),audio,(if the controls attribute is present),button,details,embed,iframe,img (if the usemap attribute is present),input (if the type attribute is not in the Hidden state)label,select,textarea,video (if the controls attribute is present)

HTML Living Standard - Interactive content

この定義により、button タグはインタラクティブコンテンツとなるため、この仕様に基づくと、a タグ内に配置することは明確な仕様違反となります。

仕様違反による影響

このような仕様違反を行うと、次のような具体的な問題が発生する可能性があります。

  1. アクセシビリティに関する問題

    • スクリーンリーダーが要素の役割を正しく解釈できなくなる可能性があります
    • キーボードでの操作時に適切なフォーカスの順序が保てなくなる可能性があります
  2. ブラウザの挙動に関する問題

    • ブラウザによって動作が異なる場合があります
    • クリックなどのイベントが想定通りに処理されない場合があります
  3. 開発時の問題

    • W3C Markup Validation Serviceなどの HTML 検証ツールで警告やエラーが表示されます
    • 将来のブラウザアップデートによって予期せぬ不具合が発生するリスクがあります

このように HTML 仕様に違反する実装を行うと、アクセシビリティの低下、ブラウザ間の動作の違い、開発時の問題など、多岐にわたる影響が発生する可能性があります。 具体例を挙げると、スクリーンリーダーの場合、見た目上は一つの UI コンポーネントであるにもかかわらず、「リンク」と「ボタン」という 2 つの要素として読み上げられてしまい、ユーザーに混乱を招く結果となります。

正しい実装方法

では、リンクとボタンの機能を組み合わせたい場合、どのように実装すべきでしょうか。以下のパターンを検討してください。

  1. 単純なリンクとして実装
<a href="..." class="button">リンク</a>
  1. JavaScript での制御が必要な場合
<button type="button" onclick="handleClick()">ボタン</button>
  1. 見た目はボタンだがリンクとして機能させたい場合
<a href="..." class="button-style" role="button">リンク</a>

まとめ

  • button タグと a タグの入れ子は、HTML 仕様で明確に禁止されています
  • 両者ともに、インタラクティブコンテンツを子孫要素として含めることができないという明確な制約があります
  • 目的に応じて、適切な HTML 要素を選択し、必要に応じてスタイリングや ARIA ロールを活用しましょう

参考