Search code examples
lifecyclerefsetfocussvelte-3

How to put focus after update in svelte?


I'd like to make the custom 4 digit code input component. By the way to avoid abnormal action when press key faster I hope to set focus on other element on keypress action after updated the value. But it doesn't act.

<script>
  let value:string[] = new Array(4).fill("")
  let elemIdx:number = 0;
  function handleChange(event: any, element: number): void {
    elemIdx = element;
    value[element] = event.target.value;
  }

  afterUpdate(() => {
    if (elemIdx < 3) {
      input[elemIdx + 1].focus();
    }
  });
</script>

      <input
        bind:this={input[i]}
        on:keypress={(e) => handleChange(e, i)}
        autocomplete="off"
      />

Solution

  • I would change a couple of things in your code:

    • use the on:input event instead, this will trigger whenever the value changes
    • use nexElementSibling to identify the next input

    This will vastly simplify your code:

    <script>
        const value = new Array(4).fill("") 
        const handleInput = (ev, i) => i < value.length - 1 && ev.target.nextSibling.focus()
    </script>
    
    {#each value as v, i}
        <input bind:value={v} autocomplete="off" maxlength="1" 
               on:input={(ev) => handleInput(ev, i)} 
        />
    {/each}
    

    To understand what we have here:

    1. we start of with a 4 element array of spaces
    2. we loop over this array, binding each input to the corresponding position in the array, this will keep the array synchronized with the input elements
    3. when the value of an input element changes, we trigger a function that takes the current input element and it's index
    4. this function compares the element's index to value array's length, if it is not at the end, it peeks at the next element and sets the focus on it.

    as a bonus, each input gets maxlength="1" this will prevent the user from typing more than one character. and another bonus is that this is all purely based on how many characters you need, if you want 10 characters simply create an array with 10 items instead.