DOM element to corresponding vue.js component
JavascriptDomvue.jsJavascript Problem Overview
How can I find the vue.js component corresponding to a DOM element?
If I have
element = document.getElementById(id);
Is there a vue method equivalent to the jQuery
$(element)
Javascript Solutions
Solution 1 - Javascript
Just by this (in your method in "methods"):
element = this.$el;
:)
Solution 2 - Javascript
The proper way to do with would be to use the v-el
directive to give it a reference. Then you can do this.$$[reference]
.
Update for vue 2
In Vue 2 refs are used for both elements and components: http://vuejs.org/guide/migration.html#v-el-and-v-ref-replaced
Solution 3 - Javascript
In Vue.js 2 Inside a Vue Instance or Component:
- Use
this.$el
to get the HTMLElement the instance/component was mounted to
From an HTMLElement
:
- Use
.__vue__
from the HTMLElement- E.g.
var vueInstance = document.getElementById('app').__vue__;
- E.g.
Having a VNode
in a variable called vnode
you can:
- use
vnode.elm
to get the element that VNode was rendered to - use
vnode.context
to get the VueComponent instance that VNode's component was declared (this usually returns the parent component, but may surprise you when using slots. - use
vnode.componentInstance
to get the Actual VueComponent instance that VNode is about
Source, literally: vue/flow/vnode.js.
Runnable Demo:
Vue.config.productionTip = false; // disable developer version warning
console.log('-------------------')
Vue.component('my-component', {
template: `<input>`,
mounted: function() {
console.log('[my-component] is mounted at element:', this.$el);
}
});
Vue.directive('customdirective', {
bind: function (el, binding, vnode) {
console.log('[DIRECTIVE] My Element is:', vnode.elm);
console.log('[DIRECTIVE] My componentInstance is:', vnode.componentInstance);
console.log('[DIRECTIVE] My context is:', vnode.context);
// some properties, such as $el, may take an extra tick to be set, thus you need to...
Vue.nextTick(() => console.log('[DIRECTIVE][AFTER TICK] My context is:', vnode.context.$el))
}
})
new Vue({
el: '#app',
mounted: function() {
console.log('[ROOT] This Vue instance is mounted at element:', this.$el);
console.log('[ROOT] From the element to the Vue instance:', document.getElementById('app').__vue__);
console.log('[ROOT] Vue component instance of my-component:', document.querySelector('input').__vue__);
}
})
<script src="https://unpkg.com/[email protected]/dist/vue.min.js"></script>
<h1>Open the browser's console</h1>
<div id="app">
<my-component v-customdirective=""></my-component>
</div>
Solution 4 - Javascript
If you're starting with a DOM element, check for a __vue__
property on that element. Any Vue View Models (components, VMs created by v-repeat
usage) will have this property.
You can use the "Inspect Element" feature in your browsers developer console (at least in Firefox and Chrome) to view the DOM properties.
Hope that helps!
Solution 5 - Javascript
this.$el
- points to the root element of the componentthis.$refs.<ref name>
+<div ref="<ref name>" ...
- points to nested element
> use $el
/$refs
only after mounted()
step of vue lifecycle
<template>
<div>
root element
<div ref="childElement">child element</div>
</div>
</template>
<script>
export default {
mounted() {
let rootElement = this.$el;
let childElement = this.$refs.childElement;
console.log(rootElement);
console.log(childElement);
}
}
</script>
<style scoped>
</style>
Solution 6 - Javascript
Since v-ref is no longer a directive, but a special attribute, it can also be dynamically defined. This is especially useful in combination with v-for.
For example:
<ul>
<li v-for="(item, key) in items" v-on:click="play(item,$event)">
<a v-bind:ref="'key' + item.id" v-bind:href="item.url">
<!-- content -->
</a>
</li>
</ul>
and in Vue component you can use
var recordingModel = new Vue({
el:'#rec-container',
data:{
items:[]
},
methods:{
play:function(item,e){
// it contains the bound reference
console.log(this.$refs['key'+item.id]);
}
}
});
Solution 7 - Javascript
So I figured $0.__vue__
doesn't work very well with HOCs (high order components).
// ListItem.vue
<template>
<vm-product-item/>
<template>
From the template above, if you have ListItem
component, that has ProductItem
as it's root, and you try $0.__vue__
in console the result unexpectedly would be the ListItem
instance.
Here I got a solution to select the lowest level component (ProductItem
in this case).
Plugin
// DomNodeToComponent.js
export default {
install: (Vue, options) => {
Vue.mixin({
mounted () {
this.$el.__vueComponent__ = this
},
})
},
}
Install
import DomNodeToComponent from'./plugins/DomNodeToComponent/DomNodeToComponent'
Vue.use(DomNodeToComponent)
Use
- In browser console click on dom element.
- Type
$0.__vueComponent__
. - Do whatever you want with component. Access data. Do changes. Run exposed methods from e2e.
Bonus feature
If you want more, you can just use $0.__vue__.$parent
. Meaning if 3 components share the same dom node, you'll have to write $0.__vue__.$parent.$parent
to get the main component. This approach is less laconic, but gives better control.
Solution 8 - Javascript
I found this snippet here. The idea is to go up the DOM node hierarchy until a __vue__
property is found.
function getVueFromElement(el) {
while (el) {
if (el.__vue__) {
return el.__vue__
} else {
el = el.parentNode
}
}
}
In Chrome:
Solution 9 - Javascript
Solution for Vue 3
I needed to create a navbar and collapse the menu item when clicked outside. I created a click listener on windows in mounted
life cycle hook as follows
mounted() {
window.addEventListener('click', (e)=>{
if(e.target !== this.$el)
this.showChild = false;
})
}
You can also check if the element is child of this.$el. However, in my case the children were all links and this didn't matter much.
Solution 10 - Javascript
If you want listen an event (i.e OnClick) on an input with "demo" id, you can use:
new Vue({
el: '#demo',
data: {
n: 0
},
methods: {
onClick: function (e) {
console.log(e.target.tagName) // "A"
console.log(e.targetVM === this) // true
}
}
})
Solution 11 - Javascript
Solution 12 - Javascript
Since in Vue 2.0, no solution seems available, a clean solution that I found is to create a vue-id
attribute, and also set it on the template. Then on created
and beforeDestroy
lifecycle these instances are updated on the global object.
Basically:
created: function() {
this._id = generateUid();
globalRepo[this._id] = this;
},
beforeDestroy: function() {
delete globalRepo[this._id]
},
data: function() {
return {
vueId: this._id
}
}