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ロールを活用しましょう

参考