Capability to add an existing DOM element in Vue component
What problem does this feature solve?
This feature request proposes a new built-in special element to add existing DOM element in Vue component
DOM interafce Element
provides prototype methods insertAdjacentText
, insertAdjacentHTML
and insertAdjacentElement
to insert text, html and element respectively. Currently Vue has {{xxx}}
, v-html="xxx"
directives to declare text node value and inner html, but no built-in directive to insert an existing element.
Assuming I have a canvas
element which is generated by a third-party lib. To add existing canvas element in Vue component, one approach is to manipulate the DOM, with code like this.$el.querySelector('.xxx').insertAdacentElement('afterend', el)
or this.$refs.xxx.replceWith(el)
, but it's not a good pratice unless one have no other way, see TIPS FOR VUE DEVELOPERS: Avoid directly manipulating the DOM and elegantly-inject-pre-rendered-element-into-vue-template, another approach is to convert element to HTML and use the v-html
directive, but it isn't always practicable, how about the graphics of canvas element?
What does the proposed API look like?
Vue currently provides two built-in special elements <slot>
and <component>
.
The
<slot>
element is a slot outlet that indicates where the parent-provided slot content should be rendered.
The
<component>
element is a "meta component" for rendering dynamic components or elements.
This feature request proposes a new built-in special element, which is
- something like
<slot>
but source content may come from component itself rather than only from parent, and the type isElement
rather thanVNode
. - something like
<component>
but we would to pass anElement
instance (similar to DOM methodHTMLSlotElement.prototype.assign
), rather than passing a tag name / a component name / a component definition. - something like
{{xxx}}
andv-html="xxx"
, but for DOM element.
e.g. <element-placeholder>
as defined below
// ElementPlaceholder.js
export default {
props: {
el: Element,
},
render(h) {
return [];
},
mounted(){
let el = this.$el;
let newEl = this.el;
if(newEl){
el.replaceWith(newEl);
this.currentEl = newEl;
}else{
this.currentEl = this.$el;
}
},
watch: {
el: function (newEl, oldEl) {
let el = this.currentEl;
if(newEl){
el.replaceWith(newEl);
this.currentEl = newEl;
}else{
let originalEl = this.$el;
el.replaceWith(originalEl);
this.currentEl = originalEl;
}
},
}
};
Example usage:
// App.js
import ElementPlaceholder from './ElementPlaceholder.js';
let App = {
components: {
'element-placeholder': ElementPlaceholder,
},
template: /*html*/ `<div id="app">
<element-placeholder :el="canvas"></element-placeholder>
<button @click="init" :disabled="canvas!=null">Init</button>
<button @click="reset" :disabled="canvas==null">Reset</button>
</div>`,
data(){
return {
canvas: null,
};
},
created(){
this.init();
},
methods: {
init(){
this.canvas = Object.assign(document.createElement('canvas'), {
width: 64,
height: 64
});
},
reset(){
this.canvas = null;
},
}
};
There are other concerns, e.g. would <element-placeholder>
work well with v-for
/ v-if
?
This is just a basic idea, I'm not a Vue expert.