Many data visualization and graphics library do DOM manipulation and most of them the DOMElement can be extracted.
const rect = SVG.adopt(this.$refs.rect.elm)
rect.node // this is the SVGElement and in chrome console will output <rect id="r1"></rect>
Let's say we want to build an abstraction by design it as a component that can draw a rectangle and after the drawing is done (by hold&drag mouse button) that rectangle will be placed in another section. The usage will be like this:
<annotator>
<img src="background" />
<rect slot="drawing" stroke="red" />
<rect slot="annotation" stroke="green" x="100" y="100" width="100" height="100" />
</annotator>
and <annotator> will be like this
<template>
<svg>
<foreignObject>
<slot></slot>
</foreignObject>
<g fill="black" transform="translate(100, 100)">
<slot name="annotation"></slot>
</g>
<slot name="drawing"></slot>
</svg>
</template>
After the drawing is done, the rectangle will be moved and fill it with black (or SVG texture for the more complex case). This can be done by clone the element in drawing slots to annotation slot.
const rect = SVG.adopt(this.$slots.drawing[0].elm)
rect.on('click', event => doDrawingLogic(event))
rect.on('drawfinish', event => {
// the reason to clone and move slot is so any logic in annotation slot can be applied
// the clone itself is to preserve `<rect stroke="red" slot="drawing" />` so that it can be referenced again
/** some logic here */
})
In that case there is need to make createElement accept DOMElement for duplicating or maybe also converting Node to VNode.
newRect = newRect.clone().animate().dmove(100, 100)
const clone = this.$createElement(newRect.node)
this.$slots.annotation.push(clone)
In above example, if createElement act as creating element then there will be 2 rectangles lol
Basic usage
const div = document.getElementById("myDiv")
this.$createElement(div)
Clone and move from 1 slots to another slot
const clone = this.$createElement(this.$slots.mySlot[0].elm)
this.$slots.default.push(clone)
Hi, and thanks for your suggestion.
I see the problem you ate trying to solve with this. However, I do't like the mixture of virtualdom and real DOM here.
Since the DOM element was created by the properties of its virtual DOM node counterpart, you should have all the information required to copy/clone it in the vnode. If you can be sure slots will only contain vnodes for "normal" elements (not component), this is super-simple:
function cloneVNode (vnode) {
const children: vnode.children
? vnode.children.map(x => cloneVNode(x))
: []
const clone = this.$createElement(vnode.tag, vnode.data, children)
}
If the slot can also contain components, it's a bit more complicated since the data and children on component vnodes are found in different properties and have to be stitched together:
function cloneVNode (vnode) {
const children: vnode.children
? vnode.children.map(x => cloneVNode(x))
: []
const isComponent = !!vnode.componentOptions
const tag = isComponent
? vnode.componentOptions.Ctor
: vnode.tag
const data = isComponent
? cloneComponentData(vnode)
: Object.assign({}, vnode.data)
const clone = this.$createElement(tag, data, children)
}
cloneComponentData(vnode) {
const data = Object.assign({}, vnode.data, { hooks: undefined })
data.props = vnode.componentOptions.propsData
data.on = Object.assign(data.on ||聽{}, vnode.componentOptions.listeners)
}
I realize this is not easy at all for someone not familiar with the VNode API, but the goal is to work in the virtualDOM where possible, so my take on this feature request is this:
Playing around with cloneVNode
https://github.com/DrSensor/test-cloneVNode/blob/master/src/Annotator.vue#L100
However, I found out it's strange that createElement doesn't produce VNode.elm.
The current work around for that by doing
const clone = this.$createElement(vnode.tag, vnode.data, vnode.children)
clone.elm = vnode.elm
But that's not right because elm only being referenced, not cloned. To actually clone it there is need to use cloneNode.
const clone = this.$createElement(vnode.tag, vnode.data, vnode.children)
clone.elm = vnode.elm.cloneNode()
However, this also caused problem because now there is 2 tag (one is from createElement, another from cloneNode).
For exposing VNode API, I propose to be expressed like this
import {
VNode,
cloneVNode,
deleteVNode,
toVNode, // convert normal Node to VNode
// fromVNode, // not necessary if `VNode.elm` can be accessed
} from 'vue'
to avoid vm.{anything} to be cluttered.
Closing since the original proposal is doable in userland. Implementation details is out of scope for issues.
I'm new to Vue so, I apologize if there's an obvious alternative, I wasn't able to find a solution in userland. The issue can stay closed, I could just use some clarification.
If I have a vanilla javascript node, such as
import calendarDomNode from 'a-vanilla-js-calendar'
Or more trivially a node such as
let aDiv = document.createElement('div')
And I wanted to use it inside of a Vue component, is there any way to do that?
let aComponent = Vue.extend({
render() {
return calendarDomNode
}
})
I looked into domPropsInnerHTML but it requires there be a wrapper element, and it also breaks the javascript references (e.g. anInputBox.value) when the node is converted into an HTML string. Since the render function is only VNodes, is there an alternative function for mixing VNodes and Dom Nodes? Or in general is Vue just not designed to work with vanilla JS internally?
My initial expectation was that Dom Nodes would be treated as immutable constants inside of the Virtual Dom, and they'd be allowed in render functions.
Most helpful comment
Hi, and thanks for your suggestion.
I see the problem you ate trying to solve with this. However, I do't like the mixture of virtualdom and real DOM here.
Since the DOM element was created by the properties of its virtual DOM node counterpart, you should have all the information required to copy/clone it in the vnode. If you can be sure slots will only contain vnodes for "normal" elements (not component), this is super-simple:
If the slot can also contain components, it's a bit more complicated since the data and children on component vnodes are found in different properties and have to be stitched together:
I realize this is not easy at all for someone not familiar with the VNode API, but the goal is to work in the virtualDOM where possible, so my take on this feature request is this: