innerHTML の基本と注意点(XSS対策を含む) — el.innerHTML = '<b>hi</b>'
innerHTML は「要素の中身をHTML文字列として読み書きする」ためのプロパティです。便利ですが、外部やユーザー入力由来の文字列をそのまま入れると XSS(スクリプト混入)につながるため、使い所と注意点をしっかり押さえましょう。
基本の使い方
<div id="box"></div>
<script>
const box = document.getElementById("box");
// HTMLとして挿入(太字やリンクなどの構造を含められる)
box.innerHTML = "<b>こんにちは</b> <a href='/help'>ヘルプ</a>";
// 現在のHTMLを文字列として取得
console.log(box.innerHTML); // "<b>こんにちは</b> <a href='/help'>ヘルプ</a>"
</script>
HTML- 役割: 要素内のHTMLを「文字列」で設定・取得する。
- 利点: 強調、リンク、複数要素の一括生成などが簡単。
- 重要: 未検証の文字列(ユーザー入力や外部データ)をそのまま代入しない。
よく使うテンプレート集
ラベル: 静的で安全が確実なHTMLを表示
const dialog = document.getElementById("dialog");
// 静的(自分で書いた、信頼できる)HTMLならOK
dialog.innerHTML = `
<h2>お知らせ</h2>
<p><strong>明日</strong>は休館日です。</p>
`;
JavaScriptラベル: ユーザー入力は textContent を使う(安全側)
const name = "<script>alert(1)</script>"; // 入力例(危険)
const p = document.createElement("p");
p.textContent = `ようこそ、${name}さん`; // テキストとして表示(スクリプトは無効)
document.body.appendChild(p);
JavaScriptラベル: 要素を組み立てる(DOM APIで安全に)
function renderUser({ name, url }) {
const card = document.createElement("div");
const b = document.createElement("b");
b.textContent = name; // テキストとして安全に挿入
const a = document.createElement("a");
a.href = url; // 属性として設定
a.textContent = "プロフィール";
card.append(b, " ", a);
return card;
}
document.getElementById("app").appendChild(renderUser({ name: "Aki", url: "/u/aki" }));
JavaScriptラベル: 部分的に装飾だけを innerHTML で
const msg = document.getElementById("msg");
const text = "重要なお知らせ"; // 信頼できる固定文字列
msg.innerHTML = `<span class="badge">NEW</span> ${text}`; // バッジだけHTML、文字列は固定
JavaScriptinnerHTML を使う際の注意(XSSの観点)
- ラベル: 未検証の文字列を直接入れない
- ユーザー入力や外部APIの応答をそのまま
innerHTMLに代入すると、スクリプトが実行される危険がある。
- ユーザー入力や外部APIの応答をそのまま
- ラベル: テキストは textContent を基本に
- 動的テキスト表示は
textContentを使う。HTML構造が必要なら、DOMの createElement/append で組み立てる。
- 動的テキスト表示は
- ラベル: 必要なら安全に加工してから
- どうしてもHTML文字列を表示する場合は、信頼できるソースのみ、または適切なエスケープ/サニタイズを通す(ライブラリやフレームワークの安全APIなど)。
- ラベル: innerHTML 再代入でイベントは消える
- 既存要素の中身を
innerHTMLで置き換えると、下位のノードとイベントリスナーが失われる。必要な場合は再登録が要る。
- 既存要素の中身を
よくあるハマりポイントと対策
- ラベル: 要素が存在せず「nullに代入」
- 対策:
const el = document.querySelector("#id"); if (!el) return;で存在確認。
- 対策:
- ラベル: innerHTML の乱用で保守性低下
- 対策: 複雑なUIは DOM API で部品化。イベント付与や再利用が楽になる。
- ラベル: 入力のエスケープ忘れ
- 対策: ユーザー入力は必ず
textContentに。どうしてもHTMLが必要なら、信頼できるフォーマット限定+サニタイズ。
- 対策: ユーザー入力は必ず
- ラベル: パフォーマンス影響
- 対策: 大量更新は
DocumentFragmentや差分更新を使う。innerHTMLの全置換は負荷が高い。
- 対策: 大量更新は
練習問題(安全な置き換えと危険な例を区別する)
<div id="safe"></div>
<div id="danger"></div>
<script>
const safe = document.getElementById("safe");
const danger = document.getElementById("danger");
// 1) 安全な静的HTML
safe.innerHTML = "<b>ようこそ</b> <span>ゲストさん</span>";
// 2) ユーザー入力を表示(安全版)
const userInput = "<img src=x onerror=alert(1)>"; // 危険な入力例
const p = document.createElement("p");
p.textContent = `入力: ${userInput}`; // スクリプト/タグは無効化されて単なる文字に
safe.appendChild(p);
// 3) 危険な使用例(やってはいけない)
// danger.innerHTML = userInput; // ← 外部入力をそのまま入れるのは危険
// 4) DOMで安全に装飾
const title = document.createElement("h3");
title.textContent = "お知らせ";
const badge = document.createElement("span");
badge.className = "badge";
badge.textContent = "NEW";
danger.append(title, badge);
</script>
HTML直感的な指針
- HTMLを丸ごと入れるなら、信頼できる静的文字列だけ。
- 動的テキストは textContent、構造は DOM API で安全に組み立てる。
- innerHTMLの再代入はイベント消失や保守性低下につながるため、使い過ぎない。
