Every now and then you'll need to programmatically focus on an input (or other element).

This is often needed for accessibility, or to make the app more convenient to use.

Setting focus

All browsers have a built-in method that let us focus on a specific element. But we need to get a hold of that element first.

In vanilla Javascript you would grab your element using something like this:

<form>
  <input id="email" />
</form>
const input = document.getElementById('email');

But Vue gives a better way:

<template>
  <input ref="email" />
</template>
const input = this.$refs.email;

Here we are using $refs, which are a much more reliable way of grabbing your elements. The docs explain more on how to use them.

Once we have our element, we can call focus on it:

<template>
  <input ref="email" />
</template>
export default {
  methods: {
    focusInput() {
      this.$refs.email.focus();
    }
  }
}

If you're using a custom component, the $ref gives us the component, but not the underlying element. So if you try calling focus on it, you will get an error saying that, "focus is not a function".

To get this root element of the custom component we can access $el:

<template>
  <CustomInput ref="email" />
</template>
import CustomInput from './CustomInput.vue';

export default {
  components: {
    CustomInput,
  },
  methods: {
    focusInput() {
      this.$refs.email.$el.focus();
    }
  }
}

You can also check out this awesome demo to see it in action.

But what if you want to focus immediately when the component loads?

Set focus on page load

To do this, you'll want to call the focus method as soon as the component loads. This means we'll place the call in our mounted lifecycle hook:

<template>
  <CustomInput ref="email" />
</template>
import CustomInput from './CustomInput.vue';

export default {
  components: {
    CustomInput,
  },
  mounted() {
    this.focusInput();
  },
  methods: {
    focusInput() {
      this.$refs.email.$el.focus();
    }
  }
}

You may still run into some issues though...

Waiting for re-rendering

There are some cases where you may need to wait for Vue to finish re-rendering the app.

For example, if you toggle the input from being hidden to being displayed.

You'll need to wait for the input to be rendered before you can grab it and focus on it:

<template>
  <div>
    <CustomInput v-if="inputIsVisible" ref="email" />
  </div>
</template>
import CustomInput from './CustomInput.vue';

export default {
  components: {
    CustomInput,
  },
  data() {
    return {
      inputIsVisible: false,
    };
  },
  mounted() {
    this.focusInput();
  },
  methods: {
    showInput() {
      // Show the input component
      this.inputIsVisible = true;

      // Focus the component, but we have to wait
      // so that it will be showing first.
      this.nextTick(() => {
        this.focusInput();
      });
    },
    focusInput() {
      this.$refs.email.$el.focus();
    }
  }
}

Here, we wrap our call to focusInput inside of a nextTick. Using nextTick will allow us to wait until the input is rendered in the DOM. Once it's there, then we can grab it, and focus on it.

You can read more about using nextTick on the docs.