LoginSignup
2
7

More than 5 years have passed since last update.

CodePenみたいな独立した環境を実現するVueコンポーネントを作った話

Last updated at Posted at 2019-04-28

はじめに

今作ってるアプリケーションでCodePenとかで使われているような iframeを使った独立した環境を作る必要があった。
探しても手軽に使えるものが見つからなかったので自作した。
アプリケーション内に入っていたがせっかくなので独立したパッケージにした。

作ったもの

Github
npm
デモ (Storybook)

ほしかったもの

  • 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.$
})

ロード待ちに注意する必要がある

コンポーネント使用例

Storybook参照

ユーザの入力したHTMLを表示するなどして使うのでXSS対策などをコンポーネント内で行うことは適切でないと思った。
利用者側が責任を持ってエスケープなどすることを注意する。

終わりに

jestでユニットテストを書こうとしたがiframeをvdomがサポートしていないので素直な実装だと試験がしにくい。
今後この辺りを改善してテストを書いていくようにしたい。

今作ってるアプリケーションのコア部分とそうでない部分をパッケージレベルで分離することで、アプリケーションの構造を健全にすることも期待している。

このアプリケーションについても簡単だが記事を書いているので、興味があれば見てみてほしい。

2
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
7