An Introduction To Vue $refs

Feb 3, 2020

The $refs property in Vue is used to reference DOM elements in the Vue instance's templates.

A common use case for $refs is focusing on a DOM element when a certain event happens. The autofocus property works on page loads. But what if you want to give focus back to the username input if login failed?

If you give the username input a ref attribute in your template, you can then access the username input using this.$refs.username as shown below. You can then call the built-in Element#focus() function to give focus to the username input.

  const app = new Vue({
    data: () => ({ username: '', password: '', failed: false }),
    methods: {
      login: async function() {
        // Simulate that login always fails, just for this example
        this.failed = true;

        // Give focus back to `username` input. If you change the
        // 'ref' attribute in the template to 'usernameRef', you
        // would do `this.$refs.usernameRef` here.
        this.$refs.username.focus();
      }
    },
    template: `
      <div>
        <input type="text" v-model="username" ref="username" id="username">
        <input type="password" v-model="password">
        <button v-on:click="login()">Login</button>
        <div v-if="failed" id="failed">
          Login Failed!
        </div>
      </div>
    `
  });

With v-for

When you use ref with the v-for directive, Vue gives you a native JavaScript array of elements, not just a single element.

For example, suppose you have a list of <input> tags, and you want users to be able to navigate between inputs using the up and down arrow keys. You can access the individual <input> elements using $refs and call focus() whenever the user presses up or down:

  const app = new Vue({
    data: () => ({ cells: ['foo', 'bar', 'baz'].map(val => ({ val })) }),
    mounted: function() {
      let cur = 0;
      this.$refs.inputs[0].focus();

      document.addEventListener('keyup', ev => {
        console.log('Got event', ev)
        cur = this.$refs.inputs.findIndex(el => document.activeElement === el);
        if (cur === -1) {
          cur = 0;
        }

        const numEls = this.cells.length;
        if (ev.keyCode === 38) { // Up arrow
          cur = (numEls + cur - 1) % numEls; 

          this.$refs.inputs[cur].focus();
        } else if (ev.keyCode === 40) { // Down arrow
          cur = (cur + 1) % numEls;

          this.$refs.inputs[cur].focus();
        }
      });
    },
    template: `
      <div>
        <div v-for="cell in cells">
          <input v-model="cell.val" ref="inputs">
        </div>
      </div>
    `
  });

More Vue Tutorials