はじめに
今作ってるアプリケーションでCodePenとかで使われているような iframeを使った独立した環境を作る必要があった。
探しても手軽に使えるものが見つからなかったので自作した。
アプリケーション内に入っていたがせっかくなので独立したパッケージにした。
作ったもの
ほしかったもの
- vueのコンポーネント
- 任意のjsとcssがiframe内に追加できる
- jsなど追加時は再読み込みする
- body内に任意のコンテンツが差し込める
- コンテンツの再ロードが完了したタイミングをイベントで受け取りたい
技術的な話
iframeの操作
iframe内の要素は基本的に直接DOMをいじるしかない。
iframe内のdocumentには以下のようにしてアクセスできる
const iframe = getIframeElement() //任意の関数
const iframeDocument = iframe.contentWindow.document
documentが取れればあとはheaderやbodyも取れるので、好きな要素を追加すればいい
- 任意なスタイルの追加
const style = "p {color: red;}"
const styleElement = document.createElemnt("style")
styleElement.innerHTML = style
iframeDocument.head.appendChild(styleElement)
JSもほぼ同様
- 任意の要素の追加
const wrapperElement = document.createElemnt("div")
const p = document.createElemnt("p")
p.innerText = "hoge"
wrapperElement.appendChild(p)
iframeDocument.body.appendChild(wrapperElement)
JSの読み込み
JSを読み込んで、そのJSファイルからグローバルオブジェクトを利用したい場合は以下のようにする。
const script = document.createElement("script")
script.setAttribute("src", "https://code.jquery.com/jquery-3.4.0.slim.js") //jquery
const promise = new Promise(resolve => {
this.bodyElement().ifPresent(body => body.appendChild(scriptElement))
script.onload = () => resolve();
})
promise.then(()=>{
const iframe = getIframeElement()
const jquery = iframe.contentWindow.$
})
ロード待ちに注意する必要がある
コンポーネント使用例
ユーザの入力したHTMLを表示するなどして使うのでXSS対策などをコンポーネント内で行うことは適切でないと思った。
利用者側が責任を持ってエスケープなどすることを注意する。
終わりに
jestでユニットテストを書こうとしたがiframeをvdomがサポートしていないので素直な実装だと試験がしにくい。
今後この辺りを改善してテストを書いていくようにしたい。
今作ってるアプリケーションのコア部分とそうでない部分をパッケージレベルで分離することで、アプリケーションの構造を健全にすることも期待している。
このアプリケーションについても簡単だが記事を書いているので、興味があれば見てみてほしい。