Search code examples
javascriptvue.jsvue-componentfocus

How can i set focus on a newly (automatically) rendered dom element?


I have an input field that gets replaced automatically with a textarea and same content depending on the number of characters the user has entered:

<textarea v-if="myString.length > 20" v-model="myString"/>
<input type="text" v-if="myString.length <= 20" v-model="myString"/>

The problem i have is that the focus gets lost when a user enters the 21st character. And thus the user gets irritated because when he types the 22nd character it does not appear in the textarea (no focus). How can i set the focus on the newly rendered textarea then? Problem here is that it gets rendered automatically. Otherwise i could set a ref on the textarea and call focus().

Another issue is the removal of the 21st character and the switch-back from textarea to the input elment.


Solution

  • You could wrap the textarea/input in a component, and use its mounted hook to call its focus(), as seen in this component:

    <!-- AutoFocusedInput.vue -->
    <script setup>
    import { ref, onMounted, computed, nextTick } from 'vue'
    
    const input = ref()
    
    onMounted(async () => {
      await nextTick()
      input.value.focus()
    })
    
    const props = defineProps({
      modelValue: String,
      textarea: Boolean,
    })
    
    const comp = computed(() => (props.textarea ? 'textarea' : 'input'))
    </script>
    
    <template>
      <component
        :is="comp"
        ref="input"
        v-bind="$attrs"
        :value="modelValue"
        @input="$emit('update:modelValue', $event.target.value)"
      />
    </template>
    
    <AutoFocusedInput textarea v-if="myString.length > 20" v-model="myString" />
    <AutoFocusedInput v-else v-model="myString" />
    

    demo

    While this is technically possible, this UX is probably not ideal, and you should consider other designs that don't require focusing input like this (as indicated by @kien_coi_1997).