Search code examples
typescriptvue.jsinputvuejs3vue-script-setup

How can I focus on an input that is dynamic in Vue 3?


I'm building an application in Vue 3 using Script Setup and Quasar where the user can add new input boxes by pressing the "New Space +" button. It's super annoying to have to click the button each time, so I wanted to put in functionality that when you press 'enter' it will add a new space and then will focus on that space. The button being pressed and 'Enter' being pressed are working just fine in adding a new input box, but I'm struggling to target the new input. The inputs work by adding an empty element to an array, so I'm not sure how to focus on this new element.

adding new spaces

I've seen a lot of answers with "this.$refs.input" but they all have declared refs (const input = ref(null)) and I can't do that because it's dynamic. Also, "this" throws an error.

I read This and thought it could help, but it didn't

HTML - Vue using Quasar

<div
    class="col-3 q-pa-md q-gutter-sm"
    v-for="(space, index) in selectedSpaces"
    :key="index"
>
    <div class="row">
        <div class="col-9">
            <q-input
                v-model="selectedSpaces[index]"
                v-on:keyup.enter="AddNewSpace()"
             />
        </div>
        <div class="col-2 q-pt-xs q-pl-sm">
            <q-btn ..../> 
        </div>
    </div>
</div>

//also tried
<q-input
    v-model="selectedSpaces[index]"
    v-on:keyup.enter="AddNewSpace($event)"
/>

Vue 3 using Script Setup

 <script setup lang="ts">
    const selectedSpaces = ref<string[]>([]);

    const AddNewSpace = (): void => {
       selectedSpaces.value.push('');
       const index = selectedSpaces.value.length - 1;

       //Here's where I'm struggling
       selectedSpaces.value[index].focus();
   };

   //also tried
    const AddNewSpace = ($event: any): void => {
       selectedSpaces.value.push('');
       $event.target.parentElement.nextElement.children[1].focus();
       //AND
       $event.target.parentElement.nextElementSibling .children[1].focus();
   };

 </script>

Again, just to recap, when a new space is added using the AddNewSpace() method, how can I also focus on that new space?


Solution

  • ref can be used here by assigning each <q-input> the same ref and declaring that ref to be an array. That ref will then contain the array of all current and future <q-input> elements. This is documented in the Vue 3 docs for Composition API (you may need to toggle the docs to show Composition API syntax on the lefthand side since Options API loads by default)

    <q-input
      v-model="selectedSpaces[index]"
      ref="spaceRefs"
      @keyup.enter="AddNewSpace()"
    />
    

    combined with nextTick, you can then use the ref with index value to focus the new element.

    <script setup lang="ts">
    import { ref, nextTick } from "vue";
    
    const selectedSpaces = ref<string[]>([]);
    
    const spaceRefs = ref([]);
    
    const AddNewSpace = (): void => {
      selectedSpaces.value.push("");
      const index = selectedSpaces.value.length - 1;
      nextTick().then(() => {
        spaceRefs.value[index].focus();
      });
    };
    </script>
    

    In the future if you're seeing the usage of this with Vue, you're looking at older Vue 2 docs (or Vue 3 with Vue 2's Options API)