HTML の属性値とテキストのパース結果の違い2021年12月14日 23時24分

この記事は HTML アドベントカレンダーの 14 日目の分です。


HTML の属性値のパース規則は、(一般的な要素の内容として出現する) テキストのパース規則とは少し異なります。

わかりやすいところでは、属性値においては小なり記号 (<) をそのまま書けます。以下の HTML コードにおいて、span 要素の title 属性の値も内容のテキストも結果的には " < " という値になりますが、テキストに関しては内部的にパースエラー (invalid-first-character-of-tag-name; タグの名前の 1 文字目が無効) が発生しています。

<span title=" < "> < </span>

文字参照の扱いにも差異があります。以下のように amp という文字参照の名前の後に ASCII の英数字が続く場合、属性値においては "&ampm" という値に解釈されます。一方、テキストにおいては &amp が不完全な文字参照とみなされ、内部的にパースエラー (missing-semicolon-after-character-reference; 文字参照の後にセミコロンがない) が発生しつつ "&m" という値に解釈されます。

<span title="&ampm">&ampm</span>

この解釈の違いは、文字参照の名前の後に等号 (=) が来る場合も同様です。以下の例で、属性値は "&amp=" という値と解釈され、内容のテキストは内部的にパースエラーが発生しつつ "&=" という値に解釈されます。

<span title="&amp=">&amp=</span>

文字参照の名前の後に ASCII の英数字でも等号でもない文字が来る場合は、属性値でもテキストでも内部的にパースエラーが発生しつつ文字参照として扱われます。以下の例では共に "&*" という値に解釈されます。

<span title="&amp*">&amp*</span>

このような属性値におけるアンパサンドの特殊な扱いにより、クエリパラメータ ampampm を含むような URL https://www.example.com/foo?bar=42&amp=23&ampm=12a 要素の href 属性の値にそのまま (& を文字参照にせずに) 書いたとしても、リンク先 URL は (https://www.example.com/foo?bar&=23&m=12 ではなく) https://www.example.com/foo?bar=42&amp=23&ampm=12 になります。

<a href="https://www.example.com/foo?bar=42&amp=23&ampm=12">...</a>

しかしながら、このような特殊な扱いに依存した記述は意図しない挙動をもたらすことがあるため (属性値の URL をテキスト部分にコピペしたら値が変わってしまうなど)、属性値だろうがテキストだろうが & を記述したいときは文字参照 &amp; を使うことをお勧めします。