Wait until an element is rerendered (v-if) in Vue

Let's assume that you have a conditional rendered input in Vue:

vue
<template>
    <input v-if = "hamburger">Do you want ketchup?</input>
    <button @click = "setBurger">I want a hamburger</button>
</template>

<script>
export default{
    data(){
        return{
            hamburger: false
        }
    },
    methods:{
        setBurger(){
            this.hamburger = true
        }
    } 
}
</script>

Suppose that you want to focus the input just after clicking on the button.

Option 1: Manipulate the DOM (not the best option)

The first option is modifying the DOM, for example, by getting the input element using JavaScript and using the focus() function.

Example:

vue
<template>
    <input v-if = "hamburger" id = "input">Do you want ketchup?</input>
    <button @click = "setBurger">I want a hamburger</button>
</template>

<script>
export default{
    data(){
        return{
            hamburger: false
        }
    },
    methods:{
        setBurger(){

            this.hamburger = true

            // ❌ This won't work
            document.getElementById('input').focus()

        }
    } 
}
</script>

The problem with this option is that you don't let time to Vue for rerendering the input, so you'll get an error (input isn't found). You could solve it by waiting for a few milliseconds:

vue
<template>
    <input v-if = "hamburger" id = "input">Do you want ketchup?</input>
    <button @click = "setBurger">I want a hamburger</button>
</template>

<script>
export default{
    data(){
        return{
            hamburger: false
        }
    },
    methods:{
        setBurger(){

            this.hamburger = true

            // ✅ This will work but it's not nice to manipulate the DOM
            setTimeout(() => document.getElementById('input').focus(), 50)

        }
    } 
}
</script>

Option 2: Use ref and nextTick()

In Vue, you can use the ref attribute instead of using an id. To guarantee that the component is rerendered before focusing, instead of waiting for a random timeout, you can use the nextTick() function:

vue
<template>
    <input v-if = "hamburger" ref = "input">Do you want ketchup?</input>
    <button @click = "setBurger">I want a hamburger</button>
</template>

<script>
import { nextTick } from vue

export default{
    data(){
        return{
            hamburger: false
        }
    },
    methods:{
        async setBurger(){

            this.hamburger = true

            // ✅ This will work and you don't manipulate the DOM
            await nextTick()
            this.$refs.input.focus()

        }
    } 
}
</script>

Hi, I'm Erik, an engineer from Barcelona. If you like the post or have any comments, say hi.