如何在显示的DOM元素之前使focus()工作

时间:2018-09-07 21:46:04

标签: javascript html vue.js

更新:事实证明,在DOM渲染完成之前无法将焦点直接应用于文本框


我想知道为什么focus()函数不能在隐藏元素上起作用。

例如(我正在使用Vue.js):

var vm = new Vue({
  el: "#app",
  data:{
    showtext: false
  },
  methods: {
    showTxt(ev){
      this.showtext = true
      var vm = this;
      // if I uncomment setTimeout, then the textbox can set focus
      //setTimeout(function(){
        vm.$refs.textbox.focus()
      //}, 0)
    }
  }
})
<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>

我想做的是单击该按钮并显示文本框并将焦点放在文本框中,但是当前,如果我直接调用.focus(),则文本框无法获得焦点。它仅在将setTimeout包裹起来时才起作用(我猜它会在下一个事件循环中运行)。我想知道是否有任何方法可以使焦点在没有setTimeout的情况下工作?

谢谢

3 个答案:

答案 0 :(得分:4)

首选以使用vm.$nextTickVue.nextTick(即使实际使用nextTick,我们也不必担心setTimeout,Vue会保证nextTick会继续努力,甚至将来nextTick可能会使用其他实现相同目标的方法。

正如Vue API: nextTick所说,

  

推迟下一个DOM更新周期后执行的回调。采用   在您更改了一些数据以等待DOM之后   更新。

您还可以检查Vue Guide: Async Update Queue

对于您的情况,单击按钮显示输入时,它将执行this.showtext=true,然后执行element.focus。但是实际上Dom元素仍然不可见( VNode已更改,但Vue尚未重新渲染并修补)。

因此,您必须使用vm.$nextTickVue.nextTick来执行.focus 之后,Vue会重新渲染输入内容。

查看以下演示:

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>

setTimeout 为何起作用:

因为setTimeout(,0)将一个任务添加到任务队列。但是它将在下一个事件循环中执行,但是在当前事件循环中执行微任务后,将执行渲染。

检查HTML SPEC: event loop processing model (请查看第7步),在当前任务(包括this.showtext为true,数据反应性触发器重新呈现)执行后(将从任务队列),系统将先渲染,然后再从队列中弹出一个任务(如果setTimeout(,0)任务是最早的任务,则可能是setTimeout(,0)

但是 Promise是微任务,它将不起作用,因为它将在渲染之前执行(请在上方查看)事件循环处理模型第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>

或者您可以查看此Youtube Video,它会比我的描述更好。

答案 1 :(得分:2)

仅当您使用其他设置焦点的方法(例如setTimeout属性)时,才能摆脱autofocus。没有它,您将不得不单独设置showtext标志,并要求focus进入单独的事件“滴答声”。

var vm = new Vue({
  el: "#app",
  data:{
    showtext: false
  },
  methods: {
    showTxt(ev){
      this.showtext = true
      this.$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" autofocus type="text" />
    </div>
</div>

答案 2 :(得分:2)

隐藏元素无法通过焦点实体概念的性质来获得焦点。仅可见的和启用的。在任何UI系统中,都包含HTML / CSS DOM。