DOM element to corresponding vue.js component
Asked Answered
U

12

90

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)
Unkennel answered 13/11, 2014 at 17:50 Comment(2)
There is no way to do that as far as I know.Mcnutt
Do you mind accepting my answer?Esta
E
52

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

Esta answered 3/3, 2015 at 13:31 Comment(3)
In Vue 2, the v-el directive seems not to be there anymore.Emileeemili
In Vue 2 I added ref="myid" to the elements but had to reference it in JavaScript with this.$refs["myid"].Holt
A cool thing about ref is that it can be dynamically defined: :ref="'item' + item.id". However, this will seldom be neccesary because refs defined in loops automatically gets gets into an array this.$refs['combo-inside-loop'][index]Siobhan
P
79

Just by this (in your method in "methods"):

element = this.$el;

:)

Propitiatory answered 11/4, 2016 at 19:28 Comment(4)
This is not what the question asks. The question is if you have a reference to a node, how can you get a reference to the Vue component that rendered it, not what's the root element of the component. If you are in the methods you already have a reference to the component via this.Emileeemili
The Question title deceived me - but because some persons (also deceived by question title) find answer here I will left this answer.Millennial
Hint for others: After rendering, in my environment with Vue 1.x, this.$elis just an HTML comment object, not even the root object.Stadler
make sure you depend on this at least after mounted(). for example on created() this is undefinedHysteria
H
55

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__;

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>
Hodgkin answered 14/3, 2018 at 22:4 Comment(8)
github.com/vuejs/vue/blob/dev/src/core/instance/lifecycle.js Given a "Vue" instance vm: vm._vnode = vnode With that, you can cross-walk the DOM Node tree, the VNode (VDOM) tree and the Vue tree.Scorpius
where is vnode.elm documented?Plastic
what's the difference between vnode.context and vnode.componentInstanceElnoraelnore
@Elnoraelnore context is the Vue instance (the root component). componentInstance is the component instance (may or may not be the root component. It won't be the root component if it is an instance of a custom component you created -- via Vue.component('my-comp', { ...}), for instance).Hodgkin
@Hodgkin let me put it this way, imagine we have a Root.vue file,and its template is <Parent><Child v-custom-directive><Child></Parent> so vnode.context is the Root component instance ,and vnode.componentInstance is Child component instance ? right ?Elnoraelnore
@Elnoraelnore actually, in your example, the context seems to be the parent component (not the root component). I have update the answer to make it clearer. See if this fiddle helps you: jsfiddle.net/acdcjunior/ukweftav let me know!Hodgkin
@Hodgkin thanks a lot ! but what I actually means is jsfiddle.net/s7jem8oa so I think "Parent Component" may make me confused, thinking about slots.Elnoraelnore
@Elnoraelnore perhaps vnode.context.$el (jsfiddle.net/acdcjunior/9emvr5az/2) is of use to you. It appears that vnode.context points to the element the component was declared in. In the case of that fiddle, the <Child> is declared in the root ("slotted" into <Parent>)Hodgkin
E
52

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

Esta answered 3/3, 2015 at 13:31 Comment(3)
In Vue 2, the v-el directive seems not to be there anymore.Emileeemili
In Vue 2 I added ref="myid" to the elements but had to reference it in JavaScript with this.$refs["myid"].Holt
A cool thing about ref is that it can be dynamically defined: :ref="'item' + item.id". However, this will seldom be neccesary because refs defined in loops automatically gets gets into an array this.$refs['combo-inside-loop'][index]Siobhan
C
15

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!

Chancellery answered 3/3, 2015 at 13:35 Comment(1)
Pro tip: Find the element in "Elements" tab of Developer Console, select that, and then type c = $0.__vue__ in console. Now c is your Vue component and you can inspect all its properties. :)Kinlaw
H
8
  • this.$el - points to the root element of the component
  • this.$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>

enter image description here

Hysteria answered 27/8, 2019 at 18:22 Comment(0)
O
6

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]);
    }
  }
});
Outer answered 24/10, 2017 at 16:19 Comment(0)
N
6

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.

Night answered 8/11, 2017 at 16:41 Comment(0)
O
1

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:

Usage in Chrome

Outgoing answered 29/5, 2019 at 6:36 Comment(0)
B
0

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.

Basle answered 23/5, 2021 at 17:10 Comment(0)
W
-2

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
  }
 }
})
Wickiup answered 13/11, 2014 at 17:58 Comment(1)
Starting from a dom element I'm trying to understand what is the vue component controlling that element (if any).Unkennel
I
-3

Exactly what Kamil said,

element = this.$el

But make sure you don't have fragment instances.

Intumesce answered 9/5, 2016 at 5:15 Comment(2)
Exactly what I commented to Kamil. This is not what the question asked.Emileeemili
if Kamil already answered that, then why the heck you provided the same answer?Cholent
E
-4

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
    }
}
Emileeemili answered 28/10, 2016 at 15:16 Comment(1)
In vue 2 you use refsEsta

© 2022 - 2024 — McMap. All rights reserved.