Render Component in iframe using vuejs without src attribute

The easiest way for me is to use srcdoc attribute. It loads raw html overriding src attribute.

<template>
    <iframe :srcdoc="html"></iframe>
</template>

Update: More detail example: Consider a textarea for a user html input and want to display in an iframe.


<template>
  <div id="app">
    <textarea v-model="html"></textarea>
    <iframe :srcdoc="html"></iframe>
  </div>
</template>

<script>
export default {
  name: "App",
  data(){
    return {
      html:"<h1>Hello I am H1 block</h1>"
    }
  }
};
</script>

I've tried and haven't found a way to mount vue directly on #iframe.

Yet, you can add div to #iframe and mount to that:

// create iframe element
var iframe = document.createElement('iframe')
iframe.id = 'iframe'
// place iframe inside page html
document.documentElement.insertBefore(iframe, document.querySelector('html').firstChild)

// append div to iframe
var container = document.createElement('div')
container.id = 'container'
iframe.contentWindow.document.body.appendChild(container)

// render vue component inside iframe on #container
new Vue({
el: container,
render: h => h(component)
})

Result:

<html>
  <iframe id="iframe">
    #document
    <html>
      <head>
      </head>
      <body><!-- <-- here was <div id="container"></div> -->
        <div class="message" style="color: orange;">Hello from Vue component!</div>
      </body>
    </html>
  </iframe>
  <head>
  </head>
  <body>
  </body>
</html>

P.s. I've used this code in chrome extension content scripts (javascript injected into pages). If you're going to use it elsewhere make sure not to break Same-origin policy.


You can refer below link That helped me a lot. Here is the link and the code snippets.

Vue.component('i-frame', {
  render(h) {
    return  h('iframe', {
      on: { load: this.renderChildren }
    })
  },
  beforeUpdate() {
    //freezing to prevent unnessessary Reactifiation of vNodes
    this.iApp.children = Object.freeze(this.$slots.default)
  },  
  methods: {
    renderChildren() {
      const children = this.$slots.default
      const body = this.$el.contentDocument.body      
      const el = document.createElement('DIV') // we will mount or nested app to this element
      body.appendChild(el)
      
      const iApp = new Vue({
        name: 'iApp',
        //freezing to prevent unnessessary Reactifiation of vNodes
        data: { children: Object.freeze(children) }, 
        render(h) {
          return h('div', this.children)
        },
      })
      
      iApp.$mount(el) // mount into iframe
      
      this.iApp = iApp // cache instance for later updates
      
      
    }
  }
})

Vue.component('test-child', {
  template: `<div>
  <h3>{{ title }}</h3>
  <p>
  <slot/>
  </p>
  </div>`,
  props: ['title'],
  methods: {
    log:  _.debounce(function() {
      console.log('resize!')
    }, 200)
  },
  mounted() {
    this.$nextTick(() => {
      const doc = this.$el.ownerDocument
      const win = doc.defaultView
      win.addEventListener('resize', this.log)
    })
  },
  beforeDestroy() {
    const doc = this.$el.ownerDocument
    const win = doc.defaultView
    win.removeEventListener('resize', this.log)
  }  
})

new Vue({
  el: '#app',
  data: {
    dynamicPart: 'InputContent',
    show: false,
  }
})

https://jsfiddle.net/Linusborg/ohznser9/