[JavaScript] innerHTMLに流し込んだscriptを活性化させる

  • このエントリーをはてなブックマークに追加

innerHTMLは仕様上、scriptタグの含まれるHTMLソースコードを代入しても、そのスクリプトコードは実行されません。

// 何も起きない(´・ω・`)
document.body.innerHTML = '<script>console.log('test')</script>';

element.innerHTML – Web API インターフェイス | MDN

さてどうするか。

Element.innerHTMLとjQuery.fn.html()は別物

Element.innerHTMLはjQueryのhtml()と機能的に似ているので同一されがちですが、jQueryの方はscriptがきっちり実行されるようになっており、似て非なるものです。
このあたりは近年のVirtualDOMを操作するタイプのフレームワークなどではほとんど利用する事はありませんが、
HTMLコードを整形するだけのようなシンプルなテンプレートエンジンの類では大抵突っかかるポイントではないでしょうか?
特に最近はES2015の普及により脱jQuery派も増えてると思うのでなおさらです。

で、どうする?

jQueryではscript要素を抽出して、新規生成したscript要素にコードをコピーし、一時的にDOMに追加することでコードを実行しています。
[参考] jquery/manipulation.js at master · jquery/jquery

これに対して、自分はちょっと違うアプローチでシンプルに実装してみました。

/**
 * @param {Element} Element
 * @param {String} html
 */
export default function replaceHTML(element, html) {
    element.innerHTML = html;
    element.querySelectorAll('script').forEach(scriptElement => {
        const _script = document.createElement('script');
        _script.textContent = scriptElement.textContent;
        scriptElement.replaceWith(_script);
    });
}

新規生成したscript要素にコードをコピーするところまでは一緒ですが、
最後は元の要素と置換しています。
一応これでもコードが活性化するのは確認できた(Chrome/Firefox/Edge)のでよしとします。
※上記コードでは属性無しのscriptタグにしか対応していません

使い方例

import replaceHTML from 'replace-html';

const html = '<script>console.log("test")</script>';

replaceHTML(document.body, html); //test

DocumentFragmentを経由しても大丈夫ぽいです。

import replaceHTML from 'replace-html';

const fragment = document.createDocumentFragment();
const div = document.createElement('div');

const html = '<script>console.log("test")</script>';
replaceHTML(div, html);
fragment.appendChild(div);

document.body.appendChild(fragment); //test

以上!

  • このエントリーをはてなブックマークに追加

SNSでもご購読できます。




コメントを残す