DOM 元素到相应的 vue.js 组件

如何找到对应于 DOM 元素的 vue.js 组件?

如果有的话

element = document.getElementById(id);

是否存在与 jQuery 等价的 vue 方法

$(element)
162533 次浏览

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
}
}
})

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

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!

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

element = this.$el;

:)

Exactly what Kamil said,

element = this.$el

But make sure you don't have fragment instances.

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
}
}

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]);
}
}
});

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.

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/vue@2.5.15/dist/vue.min.js"></script>


<h1>Open the browser's console</h1>
<div id="app">
<my-component v-customdirective=""></my-component>
</div>

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

  • 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

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.