HTML Living Standard を確認する
閉じタグの省略については感覚的に実施しているかもしれませんが、</p> の省略については、まどろこしいルールがあります。まずは HTML Living Standard から当該部分を引用します。
A
pelement's end tag may be omitted if thepelement is immediately followed by anaddress,article,aside,blockquote,details,dialog,div,dl,fieldset,figcaption,figure,footer,form,h1,h2,h3,h4,h5,h6,header,hgroup,hr,main,menu,nav,ol,p,pre,search,section,table, orulelement, or if there is no more content in the parent element and the parent element is an HTML element that is not ana,audio,del,ins,map,noscript, orvideoelement, or an autonomous custom element.
これを copilot に訳して貰ったものが次です。日本語版はこちら。
p要素の終了タグは、以下の場合に省略可能です:
p要素の直後にaddress,article,aside,blockquote,details,dialog,div,dl,fieldset,figcaption,figure,footer,form,h1,h2,h3,h4,h5,h6,header,hgroup,hr,main,menu,nav,ol,p,pre,search,section,table, またはul要素が続く場合。または、親要素にこれ以上のコンテンツがなく、かつその親要素が
a,audio,del,ins,map,noscript,videoのいずれでもない HTML 要素、または自律型のカスタム要素である場合。
「親要素にこれ以上のコンテンツがなく
」とは、<p> が lastChild であり、nextSibling が存在しない、と言い換えることが出来ます。
</p> 省略の仕様を言い換える
次のいずれかの場合、</p> を省略できる。
<p>のnextSiblingがaddress,article,aside,blockquote,details,dialog,div,dl,fieldset,figcaption,figure,footer,form,h1,h2,h3,h4,h5,h6,header,hgroup,hr,main,menu,nav,ol,p,pre,search,section,table,ulの場合に</p>を省略できる<p>のnextSiblingが存在しない場合、親がa,audio,del,ins,map,noscript,videoではないなら省略できる。
nextSibling がコメントノードの場合を考慮出来ていません。レガシーなウェブページならばコメントノードが p に含まれても、除外されても問題は起こりませんが、最近のリアクティブ・ライブラリではコメントをディレクティブのアンカーにしていて、ツリー上のコメントの位置がシビアなのですよね。一旦忘れて先に進みます。
</p> 省略の仕様をコードにしてみる
文章では一読して分かりにくいかもしれません。</p> を省略できるか? をザックリと関数にすると次の通りです。
function pEndTagOmittable(pElement) {
return pElement.nextSibling ?
[
'ADDRESS', 'ARTICLE', 'ASIDE', 'BLOCKQUOTE', 'DETAILS', 'DIALOG', 'DIV',
'DL', 'FIELDSET', 'FIGCAPTION', 'FIGURE', 'FOOTER', 'FORM', 'H1', 'H2',
'H3', 'H4', 'H5', 'H6', 'HEADER', 'HGROUP', 'HR', 'MAIN', 'MENU', 'NAV',
'OL', 'P', 'PRE', 'SEARCH', 'SECTION', 'TABLE', 'UL'
].includes(pElement.nextSibling.nodeType === 1 ? pElement.nextSibling.tagName : null)
:
![
'A', 'AUDIO', 'DEL', 'INS', 'MAP', 'NOSCRIPT', 'VIDEO'
].includes(pElement.parentElement.tagName)
};
</p> 省略をサンプルで見ていく
次のマークアップの場合、最初の </p> を省略できます。
<audio>
<p>1
<p>2</p>
</audio>
実は、copilot には最初の </p> も省略できない、と言われてしまいました。
そこで適当な HTML Minifier を探して検証したところ、上記の通り minify してくれました。閉じタグの省略を全く考慮しない Minifier もあって、少しだけ難儀しました。
HTML Minifier Next (v4.1.1) の注意点としては、不適切な閉じタグの省略があった場合に、閉じタグを追加してくれません。(Include auto-generated tags にチェックを付けると必要な閉じタグが追加されました)
また、次のマークアップの場合、<p> は <audio> の子にならないので、全ての </p> を省略できます。
<audio>
<div>
<p>1
<p>2
</div>
</audio>
閉じタグ省略の実際
さて、ここまでは仕様の話で、最新のブラウザに限るならいざ知らず、レガシーなブラウザもサポートしておきたい場合には、ブラウザの実装状況に即した追加のルールが必要です。
<a>の子孫要素の閉じタグは一切省略しない(Firefox 3.5~3.6)<p>のnextSiblingが<table>の場合、</p>を省略しない(IE 5)- Void Element の
<source>は<source></source>と書いておかないとツリーが崩れる(Opera 9.x)
という秘伝のタレを発見しています。Wii のインターネットチャンネル(Opera 9.3)に表示する HTML を書く場合に覚えておきましょう。