16

I only started coding with vue.js yesterday, and I don't know how to "focus" on a textbox without using the "traditional" JS way, which is document.getElementById('myTextBox').focus().

Initially, my textbox is hidden. I have a "Start" button, and when the user clicks on it, the textbox is then displayed, and I want to set the focus there, so to speak. I already tried using ref, but to no avail (see code below).

HTML:

<input id="typeBox" ref="typeBox" placeholder="Type here..." />

Javascript

export default {
  name: 'game',

  methods: {
    startTimer () {
      setTimeout(function () { /* .focus() won't work without this */

        /* ugly and not recommended */
        // document.getElementById('typeBox').focus()

        /* Throws the error: Cannot read property 'typeBox' of undefined */
        this.$refs.typeBox.focus()

        // ... any other options?
          // ...

      }, 1)
    }
  } /* END methods */

} /* END export default */

Does anyone know how to do this? Please help.

UPDATE:

Adding autofocus on input does the trick of focusing right after the page is loaded. But in my app, there is a need to "refocus" on the input field several times without reloading the page, that's why I need a way to call .focus().

ITWitch
  • 1,729
  • 5
  • 20
  • 38
  • 1
    UPDATE: a senior dev has just helped me figure this out. I'm posting the code below as an answer, just in case someone else comes here for the same problem. Thanks for all the help, guys. – ITWitch Mar 10 '17 at 03:30
  • Can you answer to this question according to the composition API – Mishen Thakshana Jun 18 '22 at 13:06

3 Answers3

19

Sharing the solution here just in case someone encounters the same problem...

I finally figured this out with the help of a senior programmer. I was also able to eliminate setTimeout along the way, using its vue version nextTick().

The correct JS code:

startTimer () {
    this.$nextTick(() => {

        // this won't work because `this.$refs.typeBox` returns an array
        // this.$refs.typeBox.focus()

        //this one works perfectly
        this.$refs.typeBox[0].focus()

    })
} /* END startTimer */

Explanation:

When I used console.log(this.$refs.typeBox), it returned this array:

enter image description here

That's why for the code to work, it had to be typeBox[0].focus() instead of typeBox.focus().

ITWitch
  • 1,729
  • 5
  • 20
  • 38
  • 1
    Can confirm I've just had to use this very solution for my work too. Using $nextTick is the way to go with this and forget using setTimeout – munkee Aug 07 '17 at 09:04
  • 2
    Could your array be caused by v-for? "When used on elements/components with v-for, the registered reference will be an Array containing DOM nodes or component instances." – mix3d Sep 05 '17 at 19:05
  • I don't know if it has to do with the vue version or the HTML tag of the object that u're using, but in my situation it returns object and not array I had even a bigger task, coz the input field i focus is inside another component so in my case i had to use the following: `this.$nextTick(() => { this.$refs.FormComponent.$refs.InputField.focus()})}` – HenryAveMedi May 21 '22 at 00:25
5

The value of this in the setTimeout function will be set to window object since it is callback function executing after a period of time and it had lost the scope of this keyword which is dynamically set from where the function is being called.

Arrow functions doesn't bind it's own value of this.

startTimer () {
  setTimeout(() => {
    this.$refs.typeBox.focus()
  }, 1)
}

OR

startTimer () {
  const self = this;
  setTimeout(function () {
    self.$refs.typeBox.focus()
  }, 1)
}
Srinivas Damam
  • 2,893
  • 2
  • 17
  • 26
  • 1
    Thanks, but it still won't work. The error was now changed to `_this.$refs.typeBox.focus is not a function`. I tried removing `()` from `focus()`, and it did not throw an error, but still would not focus. The second option also throws the original error I mentioned above. – ITWitch Mar 09 '17 at 12:56
  • thanks, but removing the dollar sign from `$refs` doesn't do the trick either. – ITWitch Mar 10 '17 at 02:35
  • Are you calling `startTimer ` function in the `created` hook ? If yes, then it's wrong because the element is not yet put in the DOM, call the `startTimer ` function in the `mounted` hook, the DOM will be ready and focus happens. @ITWitch – Srinivas Damam Mar 10 '17 at 02:50
  • there is nothing wrong with the `startTimer`. If you haven't noticed yet, I placed `setTimeout` inside to "wait" for the element to be placed in DOM. And I've already proved that using `document.getElementById` there does the trick and allows focusing on the `input` element exactly the way I want it. What I need is to get rid of `document.getElementById` and replace it with its equivalent in `vue.js` – ITWitch Mar 10 '17 at 02:56
  • Can you check this fiddle ? Change `this.refs` to `this.$refs` https://jsfiddle.net/50wL7mdz/19718/ @ITWitch – Srinivas Damam Mar 10 '17 at 03:04
3

Finally solved the problem without setTimeout, thanks to window.requestAnimationFrame (I don't know why):

startTimer () {
    window.requestAnimationFrame(() => this.$refs.typeBox.focus())
}

It works even for custom component focusing.

mcmimik
  • 1,389
  • 15
  • 32