💾

「HTMLでは環境依存文字(①や©など)を文字参照にしなければいけない」という誤解と、本当に置換すべき文字

2022/10/20に公開約4,700字

HTMLファイルで特殊記号を使う際、①©© のように置き換えて書かないといけないものだと思いこんでいないでしょうか。
現代ではそれは誤解です。

UTF-8では特殊記号の文字参照は不要

そもそも環境依存文字とは、データを扱う機種・ソフトウェアなどの違い(文字コードの割り当ての違い)により表示に違いが出てしまう文字のことでした。
例えばShift_JISには①などの丸数字は含まれておらず、Shift_JISの文字エンコーディングを使用している環境では正しく表示できませんでした。

しかし現在ではUnicodeによって文字コードは統一化されており、その問題はほとんど起きなくなっています。
近年では多くの場合 UTF-8 でファイルを記述すると思います。
HTMLファイルの文字エンコーディングが UTF-8 の場合、前述のような記号は問題なく表示できます。
そのため、UTF-8 で記述・表示されるHTMLファイルでは、特殊記号を文字参照に置き換える必要はありません。

主張したいのは、 文字参照にする必要があるのは「文字エンコーディングがその文字に対応していない場合」の話であって、「HTMLなら絶対」ではない ということです。

不要に文字参照に置き換えてしまうと、

  • ソースを見た際にわかりにくい
  • ファイル内の文字検索に引っかからない

など不便が生じるので、むやみに文字参照に置き換えないほうがいいでしょう。

HTML標準の文字エンコーディングはUTF-8

HTML Living Standardでは、 「HTMLファイルの文字エンコーディングはUTF-8でなければならない」 と明文化されています。
(Shift_JISなどは標準ではなくなり、互換として対応されている状態)
https://laboradian.com/utf8-became-standard-on-the-web/

なぜ、文字参照しないといけないと思われがちなのか?

「昔はそうだったから」 だと思います。

Shift_JIS(など、Unicode以外の文字エンコーディング)が多く使われていた時代は文字参照に置き換えないと文字化けが発生してしまうので文字参照にするのがルールになっており、
そして、その時代の知識・記事・ルールがいまだに残っているからだと思われます。

(※「そもそもとっくに知ってたよ」という方にはすみません。
ですが、自分の周りにはHTMLでは必ず文字参照にする必要があると思いこんでいる人が多かったです。)

本当に文字参照にしないといけない文字は?

前述の通り、UTF-8環境では文字エンコーディングが原因で表示できない文字は基本的にありません。
考慮する必要があるのは、 「HTMLの中で特別な意味を持つためエスケープする必要がある文字」 です。

文字参照に置き換えるべき文字は、以下の5種のみです。

  • < (→ &lt; )
  • > (→ &gt; )
  • & (→ &amp; )
  • " (→ &quot; )
  • ' (→ &apos; )

※厳密にはエスケープすべき条件がありますが、上記5種はエスケープするものと思っておいて問題ありません。詳しくは以降で説明します。

< (小なり記号)

< は、属性値以外の場所に記述する場合、タグの記述として解釈されないようにするため &lt; にエスケープする必要があります。

以下のように<br>を文字列として表示しようとしても、タグとして解釈されてしまうため表示できません。

HTML(❌NG)
<p>改行するときには<br>タグを記述します</p>
実際の表示
改行するときには
タグを記述します

表示するためには、 < をエスケープします。

HTML(✅OK)
<p>改行するときには&lt;br>タグを記述します</p>
実際の表示
改行するときには<br>タグを記述します

ただし、以下のようにタグとして解釈されない並びになっていれば、エスケープなしでも表示されます。

HTML
<p>次に小なりの記号を示します:<</p>
実際の表示
次に小なりの記号を示します:<

とはいえ、「ここはエスケープしなくても大丈夫」「ここはタグになっちゃうからエスケープ」と判断するのは漏れが発生しやすいので、必ずエスケープするのが安全でしょう。

>(大なり記号)

> は、クオートで囲われていない属性値の中に記述する場合、開始タグの記述の終わりとして解釈されないようにするため、 &gt; にエスケープする必要があります。

❌NG
<img src=inequation.png alt=2>1 >
✅OK
<img src=inequation.png alt=2&gt;1 >

ただし、上記のようにクオートで囲わずに記述することは通常しないため、> のエスケープが必要になるケースはほぼありません(が、<をエスケープする必要あるのでそれに合わせてエスケープされることが多い)。

&(アンパサンド)

& は、文字参照を記述するための記号のため、必ず &amp; にエスケープする必要があります。

忘れられやすいですが、 & については、属性値の中も置き換える必要があるので、属性値にURLを指定する際などの & もエスケープするのが正しいです。[1]

❌NG
<a href="/path?a=1&b=2">Computers & Accessories</a>
✅OK
<a href="/path?a=1&amp;b=2">Computers &amp; Accessories</a>

"(ダブルクオート)

" は、"で囲まれた属性値の中に記述する場合、クオートの終わりとして解釈されないようにするため、&quot; にエスケープする必要があります。

❌NG
<img src="fried-chicken.png" alt="It tastes "so good"">
✅OK
<img src="fried-chicken.png" alt="It tastes &quot;so good&quot;">

'(シングルクオート)

' は、'で囲まれた属性値の中に記述する場合、クオートの終わりとして解釈されないようにするため、&apos; にエスケープする必要があります。

❌NG
<img src='fried-chicken.png' alt='It's finger-lickin' good'>
✅OK
<img src='fried-chicken.png' alt='It&apos;s finger-lickin&apos; good'>

通常、属性値のクオートにはダブルクオートが使われるため、エスケープが必要になることはあまりありません。

なお、 &apos; はHTML4までは定義されていなかったため、昔の記述では &#39; と書かれていることも多いです。[2]

エスケープに関する補足1

JSで文字を出力する場合、 innerText または textContent を使って代入すると、エスケープすべき記号は自動的にエスケープされます。

参考:textContent, innerText, innerHTML の違い (2020年確認版) - Qiita

以下のようにtextContentを使って上記の記号を代入したあと、innerHTMLを出力してみると、エスケープする必要のある3つの文字がエスケープされていることがわかります("'は属性値の中以外ではエスケープする必要がないためされない)。

JS
const p = document.querySelector("p");
p.textContent = `<>&"'`;
console.log(p.innerHTML);
Console
&lt;&gt;&amp;"'

エスケープに関する補足2

innerText または textContent 以外の手段を経由してJSで出力するような場合(テンプレートエンジンの出力を自前定義する場合など)は、以下のような関数を定義しておくと便利です。

const escapeHtml = (unsafe) => {
    return unsafe
        .replace(/&/g, "&amp;")
        .replace(/</g, "&lt;")
        .replace(/>/g, "&gt;")
        .replace(/"/g, "&quot;")
        .replace(/'/g, "&apos;");
}

参考:Can I escape HTML special chars in JavaScript? - Stack Overflow

おわりに

色々と調べて確かめながら書いてはいますが、私自身が誤解をしていることもあろうかと思いますので、間違い等あればコメントにてご指摘ください!

参考文献

『HTML解体新書』(株式会社ボーンデジタル)76-81ページ
https://www.borndigital.co.jp/book/25999.html

脚注
  1. ただし、置き換えなくてもほとんどの場合問題なく表示されます ↩︎

  2. https://stackoverflow.com/questions/2083754/why-shouldnt-apos-be-used-to-escape-single-quotes ↩︎

GitHubで編集を提案

Discussion

ログインするとコメントできます