HOWTO · JavaScript

JavaScript を使用して HTML エンティティをデコードする

この記事では、JavaScript を使用して HTML エンティティをデコードする方法を説明します。

このページの内容

文字列内の HTML エンティティをデコードするには、ブラウザーで動作するコードならブラウザーの textarea 要素を、Node.js プロジェクトなら he パッケージを使います。どちらの方法でも、&nbsp;、&#39;、&amp; などの文字参照を対応する文字に変換できます。入力例にはリテラルの <h2> タグと <br> タグも含まれていますが、エンティティをデコードしてもこれらのタグは削除されません。

Vanilla JavaScript で HTML エンティティをデコードする

ブラウザーの textarea を使うと文字参照をデコードできますが、innerHTML セッターは HTML 構文を解析します。信頼できない入力を割り当てる前に、リテラルの小なり記号をエスケープしてください。そうすれば、</textarea><img src=x onerror=alert(1)> のような文字列で解析コンテキストを閉じたり、要素を作成したりできません。文字参照は引き続きデコードされ、リテラルのタグ文字列はテキストとして残ります。この方法には document が必要なため、Node.js で直接ではなくブラウザーで実行します。

以下は、ブラウザーで動作する完全なページの例です。サンプル文字列をデコードしたい値に置き換えてください。この関数は解析前にリテラルの < をエスケープし、ページでは返された値を textContent で表示します。

<!doctype html>
<meta charset="utf-8">
<textarea id="source"></textarea>
<pre id="result"></pre>
<script>
function decodeEntities(input) {
  const textarea = document.createElement('textarea');
  textarea.innerHTML = input.replace(/</g, '&lt;');
  return textarea.value;
}

const input = "Give&nbsp;us some&#39;<h2>time</h2>&#39; &amp; space <br> Please";
document.querySelector('#source').value = input;
document.querySelector('#result').textContent = decodeEntities(input);
</script>

リテラルの < を1つずつエスケープすると、入力によって textarea の終了タグやほかの要素のマークアップが解析器に渡されるのを防げます。&lt; のようなエンコード済みの文字参照はデコードされますが、結果の < は textarea の値の中でテキストとして扱われ、マークアップとして再解析されることはありません。textContent への代入でも、デコードされた <h2> と <br> はテキストとして表示されます。デコードしたデータを後で HTML として描画する場合は、その描画処理の段階で適切な HTML サニタイズ方針を適用してください。

セミコロンで終わる10進数の数値文字参照を扱う簡単な Node.js の例では、String.fromCodePoint() を使って、JavaScript が受け付ける一致値を変換できます。このヘルパーは HTML の数値文字参照の規則を適用しません。&#0; は U+0000 に、&#55296; は単独サロゲート U+D800 になりますが、HTML パーサーではどちらも U+FFFD に置き換えられます。また、不正な形式や範囲外の参照も検証しません。範囲外の値を渡すと String.fromCodePoint() が RangeError を投げることがあります。セミコロンのない参照はそのまま残ります。これは限定的な例で、完全な HTML エンティティデコーダーではありません。より幅広いエンティティ構文を扱う場合は、上記のブラウザー方式か、以下の he パッケージを使ってください。

この例を実行します。

function decodeDecimalReferences(input) {
  return input.replace(/&#(\d+);/g, (_, digits) =>
    String.fromCodePoint(Number(digits))
  );
}

console.log(decodeDecimalReferences("Give&#39; us time"));
Give' us time

he ライブラリで HTML エンティティをデコードする

Node.js では、npm install he で he パッケージをインストールし、const he = require('he'); で読み込んでから、he.decode(input) で文字列をデコードします。he は名前付きおよび数値形式の HTML 文字参照に対応しているため、さまざまなエンティティを含む入力には、手書きの置換リストより適しています。すべてのバージョンに対応すると決めつけず、パッケージが現在要求する Node.js のエンジン条件をプロジェクトの Node.js バージョンと照らし合わせてください。

decode() 関数では、HTML 文字列を第1引数として指定する必要があります。省略可能な第2引数はオプションオブジェクトです。isAttributeValue や strict などのプロパティは、そのオブジェクトに設定する真偽値であり、個別の値として直接渡すものではありません。通常のテキストなら、オプションを付けずに he.decode(input) を呼び出します。上記の入力例を使うと、デコード後の文字列は Give us some'<h2>time</h2>' & space <br> Please になります(Give の後の空白はノーブレークスペースです)。

すでにブラウザーで動作しているコードには textarea を、Node.js や幅広いエンティティに対応する単独のデコーダーが必要な場合には he を選びます。デコードで変換されるのは文字参照であり、サンプル内のリテラルなタグは削除されません。デコードと安全な描画は別の処理として扱ってください。