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
以上!
コメント