はじめに
最近プロジェクトでウェブサイトのリニューアルを行っています。
その中で、 <a><div>...</div></a>
のように、<a>
の中に <div>
が入っているソースがありました。
インライン要素の中にブロックレベル要素を入れるのってダメじゃん。。と思ったのですが、
そういえば HTML5 からはインライン、ブロックレベルという分け方は無くなったと聞いたことがあります。
そこで調べてみたところ、思いのほかヘビーな仕様に変わっていました。
ここでは <a><div>...</div></a>
の是非とその理由ついて記載します。
結論:HTML5 では条件付きで OK
HTML5 では親要素によっては <a>
に <div>
を入れることができます。
例えば <div>
<section>
<article>
などを親に持っている場合は <a>
に <div>
を入れることができます。
どのような要素を入れることができるのか知るためには、HTML5 でどのように要素が扱われているかを知る必要があります。
HTML5 の要素の扱い
HTML4 では2種類
HTML4 では要素は インライン要素とブロックレベル要素に分類されていました。
インライン要素の中にブロックレベル要素を含めることはできないので、
<a><div>...</div></a>
は明確にルール違反です。
HTML5 ではカテゴリーに分類
コンテンツカテゴリ
次バージョンの HTML5 では、インライン要素とブロックレベル要素という分類が無くなりました。
代わりに要素はコンテンツカテゴリと呼ばれるものに分類されます。
主に以下のカテゴリが存在します。
- メタデータコンテンツ Metadata content
- フローコンテンツ Flow content
- 区分コンテンツ Sectioning content
- 見出しコンテンツ Heading content
- 記述コンテンツ Phrasing content
- 埋め込みコンテンツ Embedded content
- インタラクティブコンテンツ Interactive content
すべての要素は1つ以上のコンテンツカテゴリに所属しています。
例えば <div>
はフローコンテンツに分類されます。
また、<h1>
はフローコンテンツと見出しコンテンツに分類されます。
コンテンツカテゴリは要素を分類するだけではなく、その要素が存在できる場所を表す場合にも利用します。
例えば <p>
はフローコンテンツを親に持つことができ、記述コンテンツを子孫に持つことができます。
コンテンツカテゴリの詳細は HTML Standard にあります。Kinds of content の図を見るとカテゴリー間の関係がわかりやすいです。
すでにインライン、ブロックレベルの2種類しかなかった頃にくらべてだいぶ複雑になったのがわかります。。
コンテンツモデル
自身のコンテンツカテゴリや、親や子孫に持てるコンテンツカテゴリなどをまとめてコンテンツモデルと呼びます。
例えば <p>
はフローコンテンツに所属していて、子要素には記述コンテンツを持つことができます。
これらをまとめて <p>
のコンテンツモデルと呼びます。
また、コンテンツモデルは「持つ」と表現することがあります。
例えば、<pre>
は <p>
と似たコンテンツモデルを持つ、などと表現します。
透過的コンテンツモデル
ここで1つ特殊なコンテンツモデルを紹介します。
**透過的コンテンツモデル (transparent content model)**というものです。
透過的コンテンツモデルは親要素のコンテンツモデルを引き継ぐ特殊なコンテンツモデルです。
この透過的コンテンツモデルがポイントになります。
<a>
は透過的コンテンツモデルを持つ要素なのです。
コ ン テ ン ツ モ デ ル がゲシュタルト崩壊してきましたが、ついに本題に入る準備が整いました!!
<a>
に <div>
を入れていい条件
<a>
は透過的コンテンツモデルを持っているので、親のコンテンツモデルを引き継ぎます。
<p>
を親に持つ <a>
は <p>
と同じコンテンツモデルとなるということです。
その結果、<p>
が子孫に持てる要素は <a>
も子孫に持てるようになります。
つまり、<a>
に <div>
を入れていい条件とは、<a>
の親要素が <div>
を子孫に持てることです。
もう少し抽象的な言い方をすると、
<a>
の親要素のコンテンツモデルで、子孫に持てるとされているコンテンツモデルを持つ要素は <a>
に入れることができます。
実例
実際のソースを見たほうがわかりやすいです。
以下の例は正しい html です。
<!DOCTYPE html>
<div>
<a href="https://example.com">
<div>こんてんつ</div>
</a>
</div>
<div>
はフローコンテンツに属する要素のみ子孫に持つことができます。
そして <a>
も親である <div>
のコンテンツモデルを引き継ぎ、フローコンテンツのみを子孫に持てるようになります。
<a>
の子は <div>
、つまりフローコンテンツなので上記は正しい html となります。
一方、以下の例は誤りです。
<!DOCTYPE html>
<p>
<a href="https://example.com">
<div>こんてんつ</div>
</a>
</p>
<p>
は記述コンテンツに属する要素のみ子孫に持つことができます。
<a>
も同様に記述コンテンツに属する要素のみ子孫に持つことができるようになります。
しかし <div>
はフローコンテンツに属する要素で、記述コンテンツには属しません。
結果、上記は誤った html となります。
最後に、以下の例は誤りです。
<!DOCTYPE html>
<div>
<span>
<div>でぃぶ いん すぱん</div>
</span>
</div>
HTML4 では <a>
と同じインライン要素に分類される <span>
を使ってみます。
<span>
は透過的コンテンツモデルを持たないので、親の <div>
のコンテンツモデルを引き継ぎません。
つまり、フローコンテンツを持てるようにはなりません。
また、 <sapn>
は記述コンテンツのみを子孫に持つことができます。
結果、記述コンテンツしか持てない <span>
にフローコンテンツである <div>
を持たせていることになるので、誤った html となります。
どんなコンテンツモデルを持っているのかは調べなければわからない
結局のところ、ある要素がどんなコンテンツモデルを持っているのかは要素ごとに調べなければわかりません。
↓のサイトで愚直に調べましょう。
https://developer.mozilla.org/ja/docs/Web/Guide/HTML/Content_categories
<a>
の親と子の要素について、その親が子を持つのが妥当であれば、子を <a>
で囲んでも妥当です。
まとめ
<a>
の中に <div>
って入れていいの?と軽い気持ちで調べたらなかなかヘビーなのでした。
頻発するパターンとして div > a > div
は OK と覚えておくだけでもいいかもしれません。
ルールは厳格に決まっているので linter なども探せばあるでしょう。
html なんて自分の中で使い慣れた技術だと思っていましたが、まだまだ知らないこともあるものです。
それをアウトプットする前提で調べ始めると一気に知識が深まりますね。
やはりアウトプットは最強の勉強法です
参考
補足
透過的 = ないのと同じ
便器上引き継ぐという表現をしましたが、透過的とはあってもなくても同じという意味です。
要素が透過的コンテンツモデル (transparent content model) を持っている場合、透過的な要素が削除されたり、子要素で置き換えられたりしても、それ自身のコンテンツが必ず妥当な HTML 5 として構造化されているものです。
コンテンツカテゴリー - 開発者ガイド | MDN #透過的コンテンツモデル
そもそも誤った html だと何がいけないのか
SEO の観点から不利という意見を見かけますが、誤った html が検索結果の順位に影響を与えることはありません。
以下の動画では Google で検索エンジンの最適化に関わっている Matt Cutts 氏が
Googlebot は正しい HTML であることを考慮するか?というお話をされています。
https://www.youtube.com/watch?list=UUWf2ZlNsCGDS89VBF_awNvA&v=j3KgrbiB1pc
要約すれば以下のようなことを言っています。
多くのウェブサイトには構文エラーや無効な html がある。
html が正しいというだけでランキングの順位を上げると検索の質が下がってしまう。
Google はユーザーが必要とする情報を得ているかということを重要視しているので、
無効な html にペナルティを課すことはない。
もちろんメンテナンスなどを考えれば正しい html であるほうがよい。
しかし、検索順位を上げるために修正するは必要ない。
つまり html に誤りは検索順位に影響しないとのことです。
もちろん閉じタグを忘れると表示が崩れたりしますし、タイトルを間違えれば検索結果に正しく表示されません。
そして、Googlebot をはじめ検索エンジンのボットにウェブサイトの内容を正しく伝える上で、
<article>
や <section>
<aside>
などを使って適切な意味合いでマークアップすることは大切です。
またメンテナンスなど将来的に手を入れることを考えれば、正しい html であるほうが負担は少ないでしょう。