Skip to content

innerHTMLに代わるHTMLの差し込み方がそろってきた

JavaScriptで画面の一部を書き換えるとき、多くの現場ではいまも innerHTML に文字列を代入する書き方が使われています。手軽で、どのブラウザでも動き、昔のサンプルコードもほとんどがこの形です。ところがこの秋、この「HTMLの文字列を差し込む」という基本動作のまわりで、ブラウザ側の整備が一段進みました。

9月16日にベータ版となったChrome 155では、HTMLを差し込む新しいメソッド群と、差し込みながら少しずつ流し込むストリーミング用のメソッドが入っています。Firefoxは今年2月の148で、差し込むときに危険な部分を取り除く setHTML() を先に出荷しました。安全性と速さという別々の課題に、同じ入り口から手が入りつつある、というのが今回の見立てです。

innerHTMLが抱えてきた二つの弱点

innerHTML の弱点は、大きく分けて二つあります。一つ目は安全性です。ユーザーが入力した文字列や外部から取ってきたデータをそのまま代入すると、紛れ込んだイベント属性などから任意のスクリプトが動く、いわゆるクロスサイトスクリプティング(XSS)の入り口になります。Mozillaの解説では、XSSは10年近くにわたってウェブの脆弱性の上位三つに入り続けているとされています。

二つ目は、同じような目的のメソッドが乱立していることです。Chromeのドキュメントでは、innerHTML、outerHTML、insertAdjacentHTML、createContextualFragment、setHTMLUnsafe などを並べたうえで、上書きなのか追記なのか、危険なタグを取り除くのか、スクリプトは実行されるのか、といった違いを即答できる開発者は少ないだろうと率直に書いています。確かに、制作の現場でもこの違いを意識して使い分けている例はあまり見かけません。

さらにもう一点、これらはすべて「差し込むHTMLが最初から全部そろっている」ことが前提です。サーバーから届く途中のHTMLを順に表示していく、というHTML本来の強みを、JavaScriptからの差し込みでは活かせませんでした。

危険な部分を取り除いてから入れるsetHTML

安全性の側の答えが、HTML Sanitizer APIと、その中心にある setHTML() です。使い方は innerHTML への代入をメソッド呼び出しに置き換えるだけで、差し込む前にブラウザが危険な要素と属性を取り除きます。MDNによると、既定の設定では script、iframe、embed、object などの要素や、onclick のようなイベント属性がすべて落とされ、さらにクリックジャッキングやなりすましに使われうる要素、コメント、data- 属性まで取り除かれます。

許可する要素を自分で指定することもできます。たとえば段落と太字だけを通す、といった「許可リスト型」の設定を作れば、想定外のタグは一切入りません。MDNは、危険なものを列挙して外す方式より、必要なものだけを列挙して通す方式のほうが安全だと説明しています。将来新しい危険な要素が見つかっても、許可リストに入っていなければ最初から通らないためです。

これまでこの役割は、DOMPurifyのような外部ライブラリが担ってきました。MDNは、ブラウザに組み込まれた方式のほうが、解析の文脈や実行されうるコードをより正確に把握できるため、安全な方のメソッドを使うなら外部ライブラリは不要になるという立場です。ただし、この機能はまだ主要ブラウザすべてでは使えない段階にあり、特にSafariが未対応である点は押さえておく必要があります。

名前をそろえた差し込み用メソッド群

Chrome 155で入るのは、差し込む位置ごとに名前をそろえたメソッド群です。要素の中身を置き換える setHTML() を軸に、要素ごと置き換える replaceWithHTML()、前後に足す beforeHTML() と afterHTML()、子要素の先頭と末尾に足す prependHTML() と appendHTML() が用意されています。ベータ版の告知では、これらが insertAdjacentHTML() の実質的な置き換えになると位置づけられています。

それぞれには、危険な部分の除去をしない「Unsafe」付きの版もあります。名前だけ見ると使ってはいけないもののようですが、Chromeのドキュメントは、これは入力の信頼度に応じてリスクを意識してほしいという注意書きであり、使うなという意味ではないと説明しています。Unsafe版では、オプションを指定すれば差し込んだHTML内のスクリプトを実行させることもでき、初期値は実行しない設定です。

地味な変更に見えますが、名前の規則が一つにまとまることの効果は小さくありません。コードを読んだ人が「このメソッドは安全側か」「どこに入るか」を名前だけで判断できるようになれば、レビューや引き継ぎのときの見落としが減ります。複数の人が触るサイトほど、こうした読みやすさは効いてきます。

届いた分から順に流し込むストリーミング

もう一つの柱が、ストリーミング用のメソッドです。上の各メソッドには streamHTML() や streamAppendHTML() のような対応版があり、Streams APIの書き込み口を返します。fetch() で取ってきた応答をそのままつなげば、サーバーから届いた分から順に画面へ反映できます。Chrome 151からは、応答を文字列の流れとして取り出す textStream() という便利なメソッドも加わり、中間の変換処理を書かずに済むようになりました。

これまで、ページの一部をJavaScriptで差し替える作りでは、応答をすべて受け取ってから一気に入れるのが普通でした。Chromeのドキュメントは、単一ページアプリケーションが最初の読み込み以外ではHTMLの逐次表示の恩恵を受けられないことを大きな弱点として挙げ、この新しいメソッドでそこを埋められるとしています。共通のフッターのような部品を別ファイルから取り込み、キャッシュを効かせる使い方も例に挙がっています。ただし、JavaScriptが動かないと表示されない以上、最初の画面で見える部分に使うのは避けるべきだという注意も添えられています。

同じ取り組みの一部として、HTMLの中で後から中身を差し替える仕組みも進んでいます。場所の目印を置いておき、ページの後半で template 要素に for 属性を付けて中身を送ると、目印の位置にはめ込まれる、という方式です。重い処理を待つ部分だけを後回しにして、先に骨組みを表示できます。こちらはChrome 150から使えるとされています。

ブラウザごとの足並みと当面の付き合い方

ここまでの話を整理すると、各ブラウザの足並みはまだそろっていません。setHTML() による安全な差し込みはFirefoxが先行し、Chromeは差し込み位置ごとのメソッドとストリーミングを前に進めています。一方でSafariはSanitizer APIに対応しておらず、MDNでも主要ブラウザで広く使える段階にはないと表示されています。

Chromeのチームは、新しいメソッドの形をそのまま使えるようにする補助ライブラリをnpmで公開しています。ただしドキュメント自身が、この補助ライブラリは実際には流し込まず、全部受け取ってからまとめて差し込むと明記しています。安全な差し込みの部分も、ブラウザ側のSanitizer APIがなければ成り立ちません。つまり補助ライブラリは、書き方を先取りするためのもので、機能そのものを全ブラウザで再現するものではない、と理解しておくのがよさそうです。

もう一つの論点は、Trusted Typesとの関係です。これは危険になりうる差し込み口に、決められた変換処理を必ず通させる仕組みです。Mozillaは、setHTML() を採用したあとなら、setHTML() だけを許して他の危険な差し込み方を禁じる厳しい設定にしやすくなり、今後の書き間違いによるXSSの再発を防げると説明しています。Content Security Policyが大がかりな改修を必要としたため広く普及しきらなかった、という反省も同じ記事で触れられており、今回の仕組みは「コードの置き換えを小さく済ませる」ことに重きが置かれています。

中小企業のサイト運用で見ておきたいところ

では、日々のサイト制作や保守で何をすればよいか。すぐに全面的な書き換えが必要な話ではありません。ただ、お知らせの一覧をJavaScriptで読み込んで表示する部分や、問い合わせフォームの入力内容を確認画面に出す部分など、外から来た文字列を innerHTML で差し込んでいる箇所は、どのサイトにも意外と残っています。まずはそうした箇所を把握しておき、Safariを含む主要ブラウザで使えるようになった段階で、setHTML() への置き換えを検討する、という順番が現実的です。

WordPressのサイトでも、テーマやプラグインのJavaScriptに同じような書き方が含まれていることがあります。自社で書いた追加のスクリプトであれば、置き換えの候補として一覧にしておくだけでも、将来の改修の見積もりがしやすくなります。外部のプラグインについては、作者側の対応を待つことになりますが、どの部分が外から来たHTMLを扱っているかを知っておくことには意味があります。

ストリーミングのほうは、表示速度を気にする規模の大きなサイトや、画面の一部を頻繁に差し替える管理画面のような作りで先に効いてくるはずです。普通のコーポレートサイトでは急いで取り入れる理由は少ないものの、HTMLを後から流し込めるようになると、ページの組み立て方の選択肢が確実に増えます。長く制作を続けてきた立場から見ても、innerHTML ひとつで何でも済ませていた時代から、目的に応じて安全な道具を選べる時代へと、基本の部分が静かに入れ替わり始めているように感じます。

[1994-2002]
ITベンチャーの幹部として、8年間で数名の企業を500名以上の企業に成長させることに貢献。95年より独学でwebデザインを学ぶ。

[2002-2023]
米国法人のwebデザイン会社のCEOを務め数々の賞を受賞。

[2023〜]
AI事業開始に伴い、つくば市を拠点として株式会社RESONIXを起業。