Preferable to use vm.$nextTick or Vue.nextTick (We don't need to care about nextTick even actually uses setTimeout, Vue will guarantee nextTick will do its job, even in future nextTick may use other approaches implement same goal).
As Vue API: nextTick says,
Defer the callback to be executed after the next DOM update cycle. Use
it immediately after you’ve changed some data to wait for the DOM
update.
Also you can check Vue Guide: Async Update Queue
For your case, when click the button to show the input, it will execute this.showtext=true, then execute element.focus. But actually the Dom element is still invisible (VNode is changed, but Vue hasn't re-render&patch).
So you have to use vm.$nextTick or Vue.nextTick to execute .focus after Vue re-render that input out.
Check the demo below:
Vue.config.productionTip = false
var vm = new Vue({
el: "#app",
data:{
showtext: false
},
methods: {
showTxt(ev){
this.showtext = true
var vm = this;
console.log('Current:', this.$el.innerHTML)
this.$nextTick(()=>{
vm.$refs.textbox.focus()
console.log('Nexttick:', this.$el.innerHTML)
})
}
}
})
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="app">
<button @click="showTxt">
Show Textbox and SetFocus on it
</button>
<div v-show="showtext">
<input ref="textbox" type="text" />
</div>
</div>
Why setTimeout works:
because setTimeout(,0) adds one task to the task-queue. But it will be executed at next event loop, but render will be exeucted after execute micro-tasks in current event loop.
Check HTML SPEC: event loop processing model (please look into Step 7), after current task (including this.showtext to true, data reactivity triggers re-render) already executed (it will be removed from task queue), the system will render first before pop one task (probably is setTimeout(,0) if setTimeout(,0) task is the oldest task) from the queue.
But Promise is micro-task, it will not work because it will be executed before render (please look into above event loop processing model: step 6).
Vue.config.productionTip = false
var vm = new Vue({
el: "#app",
data:{
showtext: false
},
methods: {
showTxt(ev){
this.showtext = true
var vm = this;
new Promise((resolve, reject)=>{
vm.$refs.textbox.focus()
resolve()
}).then(()=>{
vm.$refs.textbox.focus()
})
}
}
})
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="app">
<button @click="showTxt">
Show Textbox and SetFocus on it
</button>
<div v-show="showtext">
<input ref="textbox" type="text" />
</div>
</div>
Or you can look into this Youtube Video, it will decribe better than mine.