0
0

More than 3 years have passed since last update.

aタグのクリック範囲を広げる方法とコンテンツ・モデルについて

Last updated at Posted at 2020-07-31

概要

aタグに高さや幅を持たせてボタンのように扱いたい時どのようにしていますか?
サンプルコードを用意しました。
example01の状態からどのようにしてaタグに高さや幅を持たせるかと言うのが今回のテーマです。

See the Pen aタグを任意の範囲に広げる by W-Muro (@w-muro) on CodePen.

サンプルコード解説

example01

通常のaタグです。
このままでは文字の上しかクリック出来ず、ホバーも文字の上だけでしか反応しません。

example02

CSSでaタグにdisplay: block;を指定する方法です。
「aタグを範囲いっぱいに広げる」というような検索をすると、この方法の記事が多数ヒットします。
なのでこの方法を使っている方が多いのかもしれません。
私もこの方法を使っていました。

しかし、先日コードレビューで「aタグにdisplayプロパティを指定する場合、HTML構造に問題を抱えている可能性を疑う必要がある」との指摘をいただきました。
その場では何を指摘されているのかすらわからなかったのですが、コンテンツ・モデルなどについて説明していただくと目から鱗の内容でした。
具体的な内容がexample03になります。

example03

まずは外側のdivタグに注目して欲しいのですが、class="style01"になっています。
これはexample01と同じスタイルです。

ここで私は「おぉ!」と思ったわけです。
example01と同じスタイルでなぜ望んだ挙動をしているのかと言うと、aタグの子要素にdivタグを入れ、そこに文字列を書いているからです。
divタグのデフォルトのdisplayプロパティはblockなので、aタグも一緒に枠いっぱいに押し広げられていると言うわけです。
ここで私は「aタグ内にdivタグを含めるのは文書構造として正しいのか?」と疑問に思い質問をしたのですが、回答としてコンテンツ・モデルについて教えていただきました。
コンテンツ・モデルについては「コンテンツ・モデル」の章で詳しく説明したいと思います。

example04

そもそもdivタグで囲まなければ、それでも望む形になります。
これもaタグがコンテンツ・モデルでトランスペアレント要素というものに含まれるからなのですが、詳しくは次章で。。。

コンテンツ・モデル

コンテンツ・モデルはHTML5より導入された定義です。
これにより「ブロックレベル要素」「インラインレベル要素」が廃止されているのですが、それすらも私は知りませんでした。
代わりにコンテンツ・モデルの要素カテゴリーと言うもので各要素は分類されています。

参考:
コンテンツ・モデル

入れ子のルール

コンテンツ・モデルでは要素カテゴリーで各要素が内包できるコンテンツが分けられています。
そして、このカテゴリーを元に親と子にとれる要素が決められています。

例えば、divタグとpタグはフローコンテンツにのみ属しています。
divタグは子要素として、自身を含むフローコンテンツを含めることが出来ます。
一方でpタグは子要素として、フレージングコンテンツしか含めることが出来ません。

それぞれの要素カテゴリーがどの要素を含めることができるのかを知るには以下のような便利なサイトがあります。
入れ子のルール早見サイト:
HTML5 入れ子チートシート
HTML5 コンテンツモデル ガイド

トランスペアレント

ここで前述した「aタグ内にdivタグを含めるのは文書構造として正しいのか?」と言う疑問なのですが、これは正しいが正解になります。
なぜかと言うと、aタグは要素カテゴリーでフレージング・コンテンツインタラクティブ・コンテンツに分類されているのですが、同時にトランスペアレントにも分類されています。
そして、トランスペアレントに含まれている要素は「透過」として扱われます。

どういうことかと言うと、トランスペアレントであるaタグは、親要素である要素の含めることが出来る要素は全て含むことが出来るというわけです。
そのためexample03の場合、aタグの親要素であるdivタグが含められる子要素はフローコンテンツなので、aタグも子要素にフローコンテンツを含めることが出来ると言うわけです。

なので、例えば次のような場合は、pタグが含めることが出来る要素はフレージングコンテンツだけなので、divタグをaタグに含めることはできません。

<div>
  <!-- 正しい -->
  <div>
    <a href="#"><div>文字列</div></a> 
  </div>

  <!-- 間違い -->
  <p>
    <a href="#"><div>文字列</div></a> 
  </p>
</div>

参考:
トランスペアレントについて

「ブロックレベル要素」「インラインレベル要素」はコンテンツ・モデルではどう分けられている?

コンテンツ・モデルの説明で「ブロックレベル要素」「インラインレベル要素」は廃止されているという話をしましたが、これらはCSSで幅や高さを考える時に便利な要素でした。
そのため現在でもこれらの名前で説明をしているサイトをよく見かけます。
では、これらはコンテンツ・モデル要素ではどのように分けられているのか疑問に思い調べてみました。
結果は以下のようになっていました。

  • フレージングコンテンツはほぼインライン要素
  • フレージングコンテンツ以外はほぼブロックレベル要素

tableタグがあったり、display: inline-block;になっているものがあったりしたので“ほぼ”という言葉を使いましたが、通常考える分にはイコールで考えても問題はないと思います。
正確に知りたい場合は要素ごとに調べるか、前述した「HTML5 コンテンツモデル ガイド」でも確認できます。

ちなみに、トランスペアレントに関しては「ブロックレベル要素」「インラインレベル要素」で分類されていた時には存在しなかった定義なので、別物として認識しておいてください。

参考:
[HTML5 入門]コンテンツ・モデルについて勘違いしそうなところ(6.ブロック要素とインライン要素)

あとがき

自分なりに理解をまとめてみましたがいかがだったでしょうか?
まだ知ったばかりの内容のため、間違いやご指摘、補足などがあれば是非コメントよろしくお願いいたします。

まだプログラミング学習を初めて日が浅いのですが、これまでの教材でもほぼ「ブロックレベル要素」「インラインレベル要素」で説明がされていたのですでに廃止されている定義だったということにただただ驚きました。
HTML&CSSはそこそこ自由に書けるようになってきて自信を持ち始めていただけに恥ずかしいです。
ブログなどの情報やサンプルコードを鵜呑みにせず、ソースを確認するべきという戒めとして覚えておこうと思います。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0