40

How can I get reference to the element that fired the method in Vue.js? I have HTML like this:

 <input type="text" v-model="dataField" v-bind:class="dataFieldClass" />

And in my Vue.js viewmodel I have a method:

dataFieldClass: function () {
    // Here I need the element and get its ID
    // Pseudo code
    var elementId = $element.id;
}

I know that it's possible to get the element from event (v-on:click), but this is not an event, it's a simple method returning CSS class for the element according to few conditions of the viewmodel. It should be computable as well, but the problem is the same.

7
  • The dataFieldClass method will have no idea what element it was used for when binding to a property. Why would want to reference the element? What's the use case? Commented Jun 15, 2017 at 14:23
  • I use it to find a corresponding CSS to display and in similar scenario to determinte whether enable or disable the element (and not only this one - if one of many inputs are invalid, disable few other controls). There are 3-4 conditions that decides whether enable the element so I didn't want to put a line-long conditional statement into a HTML binding and wanted to encapsulate the functionality inside a method. And I use vee-validate that automatically collects errors by element name and I'm using the VeeValidate $errors collection during a decision process. Commented Jun 15, 2017 at 14:39
  • But are those conditions based off of the html element? Or the dataField object? Commented Jun 15, 2017 at 14:47
  • Off the dataFields, of course. But you have a vee-validate collection $errors where are names of elements that caused an error. And I need to search this collection to find out what css I need to set to the CURRENT element. Commented Jun 15, 2017 at 14:51
  • Then why not just pass the dataField variable as a param for dataFieldClass? Commented Jun 15, 2017 at 14:52

7 Answers 7

39

You can get the reference to your element in three ways

1. with Method Event Handlers (doc)

template:

<input type="text" v-model="dataField" v-bind:class="dataFieldClass" />

script:

dataFieldClass: function (e) {
    const element = e.target;
}

2. with Inline Handlers (doc)

template:

 <input type="text" v-model="dataField" v-bind:class="dataFieldClass($event, otherArgument)" />

script:

dataFieldClass: function (e, otherArgument) {
    const element = e.target;
}

3. with Refs (doc)

template:

 <input type="text" v-model="dataField" v-bind:class="dataFieldClass" ref="el"/>

script:

dataFieldClass: function () {
    const element = this.$refs.el;
}
Sign up to request clarification or add additional context in comments.

1 Comment

The first two solutions do not work. There is no event here.
13

Maybe you could use ref?

<input type="text" v-model="dataField" v-bind:class="dataFieldClass" ref="el" />

And use it like this:

dataFieldClass: function () {
    var elementId = this.$refs.el;
}

See documentation here: https://v2.vuejs.org/v2/api/#ref

5 Comments

Well, the dataFieldClass method is called from many inputs in the HTML. And I need to know which of them is the one who called. Then capture its name (or id) do some tests and return apropriate class.
Could you do <input id="your_id" v-bind:class="dataFieldClass('your_id')">? Then make dataFieldClass a method instead of a computed property.
Yes, I can. Actually it's a workaround I use now, but I wanted to know whether there is a more elegant way to get a calling element in a function. jQuery has it, another JS framework Knockout.js has it so I expect Vue.js to has it too. If theres a way not to hard-code the string with id but pass something like $attribute(id) - dynamically, it would suffice.
I think this is the only way. The "problem" is that Vue is only supposed to have one value per computed property. You could, in theory, return an object from dataFieldClass and access it according to your id with something like v-bind:class="dataFieldClass[your_id]"
The method I use is not even computed, you cannot pass parameters into a computed "method".
7

What about using the ref pattern. Put ref="someName" in your DOM element, and access it in your method with this.$refs["someName"] (you can pass 'someName' as parameter to your method).

Note that's not a very good pattern except if for some reason you really need the DOM element. Otherwise just pass a relevant parameter to your method.

It's not a good method mainly because it has a major drawback: there is no $refs the first time the vue is rendered (because the element is not present yet). So you should force the vue to render twice.

If you have multiple elements inside a v-for loop, then this.$refs["someName"] becomes an array. You can get it to work with some adaptation, here is an example:

new Vue({
  el: '#app',
  data() {
    return {
      fields: [{
          name: 'field1',
          value: 'value1'
        },
        {
          name: 'field2',
          value: 'value2'
        }
      ]
    };
  },
  methods: {
    dataFieldClass(index) {
      if (!this.$refs.fields) {
        // First render, the element is not there yet
        return '';
      } else {
        // Here is the element
        console.log(this.$refs.fields[index]);
      }
    }
  },
  mounted() {
    // Force the instance to render a second time
    this.$forceUpdate();
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.js"></script>

<div id="app">
  <label v-for="(field, index) in fields">
    {{ field.name }}:
    <input ref="fields" :value="field.value" v-bind:class="dataFieldClass(index)">
  </label>
</div>

Comments

3

You can get the reference from DOM event object. "event.currentTarget" is the property that references the element where the event listener(vuejs method) assigned.

This is standard DOM specification, but you can also use this property in Vuejs.

dataFieldClass: function (event) {
    var elementId = event.currentTarget.id;
}

1 Comment

This is not an event.
0

A straightforward solution is to pass a reference to the element in the method to be called.

Here's what worked for me (a pretty basic example to help understand):

new Vue({
  el: '#app',
  data: {
    msg: '',
  },
  methods: {
    // in order to access the HTML element,
    // add an argument (namely 'event') in the method definition,
    // and access the element's current value by `event.target.value`
    updateValue: function(event) {
      this.msg = event.target.value;
    }
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

<div id="app">
  <input :value="msg" @input="updateValue" autofocus>
  <br/>
  <h2>
    >>> {{ msg }}
  </h2>
</div>

Comments

0

This seem to work for me, using ref (if element is nested another element)

<div ref="element">

vm.$refs.element

or $el if targeted element is the outermost

<template><div class="targeted-element">

this.$el

Comments

0

You can use refs as mentioned in other answers here.

Remember, refs cannot apply to computed objects. So be careful when using refs

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.