The what and why of Portals
A naive Implentation
A better implementation
Available Solutions
Caveats
A Portal moves its children from their original location to another location in the DOM
<Layout />
<Profile />
<Settings />
<EditButton />
<EditModal v-if="editButtonClicked" />
but some ancestor element is already relatively position
We move that Modal somewhere else in the DOM
...with a Portal
<portal to="#some-element">
<Modal>
<form>
<input v-model="username"/>
<button @click="save">
Save
</button>
</form>
</Modal>
</portal>
const Portal = Vue.extend({
props: ['to'],
template: `
<div ref="root">
<slot />
</div>
`,
mounted() {
const kids = [...this.$refs.root.childNodes]
const target = document.querySelector(this.to)
kids.forEach(node => {
target.append(node)
})
}
})
Instead of moving the DOM elements...
...we mount a small component...
...and pass it the content of our portal's slot.
<portal to="#some-element">
<Modal>
<form>
<input v-model="username"/>
<button @click="save">
Save
</button>
</form>
</Modal>
</portal>
const Portal = Vue.extend({
props: ['to'],
render(h) { return h() },
mounted() {
const mountEl = document.createElement('div')
document.querySelector(this.to).append(mountEl)
const content = this.$slots.default
this.target = new Wrapper({
el: mountEl,
propsData: {
content,
},
})
}
})
Codesandbox: https://codesandbox.io/s/m53x34vl08
Live on Netlify:
The simple one:
The big one: