Search code examples
vue.jsref

Vue focus on input field when it becomes visible


I've got a title (H1) and an edit button. When pressing the button I want to switch the h1 to an input field. Next I want to focus on the input field but that part isn't working because the reference of the input field is undefined. I've tried a settimeout as workaround but that didn't do the trick.

Anyone with a working solution?

<template>
    <h1 class="text-gray-900 text-2xl font-medium flex items-center">
        <input v-if="isEditing" ref="name" type="text" v-model="role" @keyup.esc="toggleEdit" />
        <span v-else>{{ role }}</span>
        <button @click="toggleEdit">Edit</button>
    </h1>
</template>

<script>
    export default {
        data() {
            return {
                role: 'Admin',
                isEditing: false,
            };
        },
        methods: {
            toggleEdit() {
                this.isEditing = !this.isEditing;
                this.$refs.name.focus();
            },
        },
    };
</script>

Code available: Codesandbox


Solution

  • You just have to wait for the DOM to update. This works:

    <template>
      <h1 class="text-gray-900 text-2xl font-medium flex items-center">
        <input
          v-if="isEditing"
          ref="roleName"
          class="text-gray-900 text-2xl font-medium"
          type="text"
          v-model="role"
          @keyup.esc="toggleEditRoleName"
        />
        <span v-else>{{ role }}</span>
        <button
          @click="toggleEditRoleName"
          class="ml-2 h-5 w-5 text-blue-300 cursor-pointer"
          aria-hidden="true"
        >
          Edit
        </button>
      </h1>
    </template>
    
    <script>
    export default {
      data() {
        return {
          role: "Admin",
          isEditing: false,
        };
      },
      methods: {
        toggleEditRoleName() {
          this.isEditing = !this.isEditing;
    
          this.$nextTick(() => {
            this.$refs.roleName.focus();
          });
        },
      },
    };
    </script>