JavaScript

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

JavaScript

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

// 何も起きない(´・ω・`)
document.body.innerHTML = '<script>console.log('test')</script>';
Element: innerHTML プロパティ - Web API | MDN
Element オブジェクトの innerHTML プロパティは、要素内の HTML または XML のマークアップを取得したり設定したりします。

さてどうするか。

スポンサーリンク

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

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

で、どうする?

jQueryではscript要素を抽出して、新規生成したscript要素にコードをコピーし、一時的にDOMに追加することでコードを実行しています。

jquery/src/manipulation.js at main · jquery/jquery
jQuery JavaScript Library. Contribute to jquery/jquery development by creating an account on GitHub.

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

/**
 * @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

以上!

スポンサーリンク
記事を書いた人

システムえんじにゃー🐈
趣味はエレキギター、自転車など。作曲したい。
World of Warshipsやってます。
記事に関する質問はお気軽にどうぞ。

surface0 (さーふぇす)をフォローする

コメント

タイトルとURLをコピーしました