Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

vue.js put focus on input

HTML

<span :style="{ display : displayTitle }" @dblclick="showInput()">
  {{ node.title }}
</span>
<input :style="{ display : displayTitleInput }" type="text" 
       @blur="hideInput1" @keydown="hideInput2" 
       @input="changeTitle(node.id , $event.target.value)" 
       :value="node.title">

JS

data() {
  return {
      displayTitle: "inline-block",
      displayTitleInput: "none"
    };
},
showInput() {
    this.displayTitle = "none"
    this.displayTitleInput = "inline-block"
},
hideInput1() {
   this.displayTitle = "inline-block"
   this.displayTitleInput = "none"
},
hideInput2(event) {
    if (event.keyCode === 13) {
        this.hideInput1()
    }
},

I am a beginner Japanese web developer. I am not good at English, sorry.

HTML is in "v-for" (v-for="node in list").

When double click text, it turns to <input>.

I want to make it possible to focus on input when it appears.

I tried this but it didn't work.

HTML

<span :style="{ display : displayTitle }" @dblclick="showInput(node.id)">
  {{ node.title }}
</span>
<input :ref='"input_" + node.id' :style="{display:displayTitleInput}" type="text" 
       @blur="hideInput1" @keydown="hideInput2" 
       @input="changeTitle(node.id , $event.target.value)" 
       :value="node.title">

JS

showInput(id) {
    this.displayTitle = "none"
    this.displayTitleInput = "inline-block"

    this.$nextTick(this.$refs["input_" + id][0].focus())
},

There was no error on console, but didn't work.

like image 935
Kuru Avatar asked Aug 30 '18 03:08

Kuru


People also ask

What is focus () JavaScript?

JavaScript | Focus() JavaScript focus method is used to give focus to a html element. It sets the element as the active element in the current document. It can be applied to one html element at a single time in a current document. The element can either be a button or a text field or a window etc.

How do I ref a component in Vue?

Referencing DOM Elements in Vue # If we want to reference a DOM element, we can use the ref attribute in Vue. Vue can store references to DOM elements in a property called $ref . The first thing we have to do, is add a ref attribute to the element we want to reference in our Javascript.

What is ref Vue 3?

ref is a special attribute, similar to the key attribute discussed in the v-for chapter. It allows us to obtain a direct reference to a specific DOM element or child component instance after it's mounted.


2 Answers

Your primary problem is that $nextTick takes a callback function but you are executing

this.$refs["input_" + id][0].focus()

immediately. You could get your code working correctly with

this.$nextTick(() => {
  this.$refs["input_" + id][0].focus()
})

However, I think you'll run in to further problems and your code can be made much simpler.

One problem you'll find is that all your node inputs will become visible when double-clicking on any of them due to your style rules.

You could instead store an "editing" flag somewhere either on the node or in a separate object.

Below is an example that simplifies your code by...

  1. Using the array-like nature of ref when used within a v-for loop, and
  2. Using the enter modifier on your @keydown event binding

new Vue({
  el: '#app',
  data: {
    list: [
      {id: 1, title: 'Node #1'},
      {id: 2, title: 'Node #2'}
    ],
    editing: {}
  },
  methods: {
    showInput(id, index) {
      this.$set(this.editing, id, true)
      
      this.$nextTick(() => {
        this.$refs.input[index].focus()
      })
    },
    hideInput(id) {
      this.editing[id] = false
    }
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js"></script>
<ul id="app">
  <li v-for="(node, index) in list">
    <span v-show="!editing[node.id]" @dblclick="showInput(node.id, index)">
      {{ node.title }}
    </span>
    <input v-show="editing[node.id]" type="text"
           ref="input" :value="node.title"
           @blur="hideInput(node.id)" @keydown.enter="hideInput(node.id)">
  </li>
</ul>
like image 92
Phil Avatar answered Oct 10 '22 00:10

Phil


The way you use this.$nextTick(); is incorrect. You should pass it a callback function.

this.$nextTick(function () {
    this.$refs["input_" + id].focus()
})

https://jsfiddle.net/un65e9oc/7/


I'm not however sure how that array access is working for you, because as I notice, $refs is an object with the keys referring to the ref name.

[Edit: Thanks to @Phil's comment, above is clear.]


The above is the correct solution for your problem. Since you have already got that answer, I'll add something other than that.

The reason why you see this behavior is that because the reference you hold in $refs doesn't get updated when you change the visibility of the text box in your showInput() method. So when you call this.$refs["input_" + id].focus();, it's actually trying to set focus on a hidden element (because the current reference is not updated).

That's why you need to call the $nextTick() to update it. But if you wanted a quick fix to your problem, without calling $nextTick(), you could update it manually like this:

this.displayTitleInput = "inline-block"
this.$refs["input_" + id].style.display = this.displayTitleInput

this.$refs["input_" + id].focus();

This would also work :) Hope it helps!!

like image 3
Nimeshka Srimal Avatar answered Oct 10 '22 00:10

Nimeshka Srimal