Well formedなXHTMLを書くために気を付けるべき7つのこと


2012年 08月 15日

えっ、世間ではHTML5の話題でいっぱいなのに、今更XHTMLの話?
XHTML 2.0は頓死したって聞いたよ?

まあ、その点についてはその通り。

しかし未だにIE6は爆発せず、
SafariにもOperaにもChromeにもFirefoxにも対応する必要があり、
IEに至っては何故か4バージョン(!)分も対応しなきゃならない現状を鑑みれば、
まだあと数年、場合によっては十数年はXHTMLも現役であることは十分に想定されるわけで、
そういう意味では正しいXHMLの知識というのは無駄にはならないはずだ。
それに、より正則なXHTMLの方が、HTML5に移行するときも楽に違いない。

さて、このトピックで扱うのは、要素の包含関係についてだ。
ただのXMLとしてみればきちんと入れ子になり、閉じタグがあって問題ないとしても、
XHTMLとしては不整なことはよくある。
良いことなのか余計なお世話なのか、
殆どのブラウザでは多少不整であってもどうにか「それっぽく」表示してしまうので、
開発中は気付かないことが多いのだ。
そして、後でこうなる。

「なんかさ、IE8で見るとこの画面オカシイんだけどどうなってんの?」

あーあ。他のブラウザだとちゃんと(?)表示されるのにね。困ったことだ。

XHTMLのValidatorは世に存在しているので、本トピックで扱うようなことは本来それで十分検出できるはずなのだが、
実際にはなにかと「XHTMLっぽいがXHTMLではなく、テンプレートエンジンを通すと最終的にはXHTMLが出てくるハズの何か」
になっていて、上手くValidatorを通せないことも多いのがWeb開発の現実だ。
もちろん、理想を言えば、テンプレートエンジンを通した後の成果物に対してはValidatorを掛けておくべきなのだが…。

そんなわけで、出来得る限り書く時点で気を付けるに越したことはないのだ。
本トピックでは、実際に見かけた不整なパターン 7 個を紹介する。
細かい点や使用頻度の低いタグについては、直接仕様書にあたられたい。

<p> 内にブロック要素を含めることはできない

これは良くある間違い。

<p>タグ内にブロック要素を含めることはできないので、
<p>内部に<div><form>を置くことはできないし、
もちろん<p>自身もブロック要素なので、入れ子にすることもできない。

意外と見るのが<p>内に<form>を置いてしまっているパターンだ。
正しくは<form>の中に<p>が入るべきである。

<body> 内に直接インライン要素やテキストを置くことはできない

XHTML1.1では、<body>内に直接インライン要素やテキストを置くことはできなくなった。
昔はできていたので戸惑う人も居るかもしれない。

NG: <body>本文です。</body>
OK: <body><p>本文です。</p></body>

うっかり<br />なんか置いちゃったりするので要注意だ。

<blockquote> に直接インライン要素やテキストを置くことはできない

<body>同様、<blockquote>にもインライン要素は置けない。

NG: <blockquote>吾輩は猫である。</blockquote>
OK: <blockquote><p>吾輩は猫である。</p></blockquote>

これ意外と守られてないところも多い気がする。
もっとも、構造的な問題はさておきそもそも「引用」として正しく使われていない例も多かったように思うが。

<form> に直接テキストを置くことはできない

前述の制限と類似の項目だが、<form>要素には直接テキストを置くことはできない。
また、「基本的には」インライン要素も含められない。
基本的には、と言っているのは、form要素である<input>等が例外になっているからだ。

いやまあ、そりゃあそうだ。

<h1> から <h6> 要素内にブロック要素を含めることはできない

似たような項目が続くが、ヘッダ要素にはブロック要素を含むことはできない。
従って、<p>要素なんかを含めてしまうのは誤りだ。
ついでに、ヘッダ要素自身もブロック要素扱いなので、逆に<p>要素の中に<hN>要素を入れることもできなかったりもする。

<table><ul><dl> 等には限定された要素しか含めることはできない

<table>には、<caption><col><colgroup><thead><tbody><tfoot><tr>のいずれかしか含められない。
そして、<tr>には<th>または<td>しか含められない。

ちなみに細かいことを言うと、順序や最低必要数も定められている。
たとえば<caption><thead>要素よりも先に出現する必要があるし、
空の<table><tr>は不整である。また、<col><colgroup>はどちらかしか出現できない。

でまあ細かいことはさておき、なんでこれに言及しているかというと、実際に次のような不整なHTMLを何度か見たからである。

<table>
  <tr><td> ... </td><td> ... </td></tr>
  <form ...>
  <tr><td> ... </td><td> ... </td></tr>
  <tr><td> ... </td><td> ... </td></tr>
  </form>
  <tr><td> ... </td><td> ... </td></tr>
</table>

まあ、気持ちはわかるよ。テーブルの一部を入力フォームにしたかったんだよね? でもそれはしちゃいけないのです。

正しくするは、次のように<from><table>全体を囲まなければならない。

<form ...>
<table>
  <tr><td> ... </td><td> ... </td></tr>
  <tr><td> ... </td><td> ... </td></tr>
  <tr><td> ... </td><td> ... </td></tr>
  <tr><td> ... </td><td> ... </td></tr>
</table>
</form>

ところで、何故<tr>の隙間にformを挟むのか。
それは次のようなことをしたいひとがいるらしいからである。

<table>
  <form action="A">
  <tr><td> ... </td><td> ... </td></tr>
  <tr><td> ... </td><td> ... </td></tr>
  </form>
  <form action="B">
  <tr><td> ... </td><td> ... </td></tr>
  <tr><td> ... </td><td> ... </td></tr>
  </form>
</table>

残念。それはできないのだよ。これを正しくするためには、テーブル自体を分けなければらない。

<form> を入れ子にすることはできない

先の項目に追加して、次のようなこともできない。

<form action="EDIT">
<table>
  <tr><td><input type="text" value="項目A" /></td>
      <td><form action="DEL"><input type="submit" value="削除" /></form></td></tr>
  <tr><td><input type="text" value="項目B" /></td>
      <td><form action="DEL"><input type="submit" value="削除" /></form></td></tr>
  <tr><td><input type="text" value="項目C" /></td>
      <td><form action="DEL"><input type="submit" value="削除" /></form></td></tr>
</table>
<input type="submit" value="更新" />
</form>

このHTMLを見てどんなインターフェースにしようとしているか想像できるだろうか?

各項目を一覧の中で直接編集して全体を一気に書き換えちゃおう! というインターフェースだ。
そんな、Visual Basic じゃあるまいに、と言いたいところだが、
編集対象によっては実はそんなに悪くないインターフェースだったりはするので一概に形式を責めることはできない。
が、問題は行の削除もできるようにしようとしてしまっているところにある。
意図はわかるのだが、やはりそれはできないのだよ。
これを正しくするためには、削除をリンクにするか、 Javascript を用いて別所に設けた form を利用しなければならない。

そして HTML5 へ

HTML5 では「実情に合わせて」ここに挙げた NG が OK になっているものもいくつかあるようだが、元々 XHTML も考えなしに制定されたわけではない。文章の意味や構造を正しくするという点で、HTML5 においてもこれらの点を守るのは良い習慣だ。

多少間違っていても問題ないことが多い HTML ではあるが、ここはひとつ謙虚になって気を付けてみてほしい。