9

After loading the component that has input filed inside it. How can I focus on that particular field?

TextField.svelte

<script>

  export let label = ''
  export let name = ''
  export let placeholder = ''
  export let value = ''

</script>

<div class="field">
  <label for={name}>{label}</label>
  <input {placeholder} type="text" {name} bind:value={value} >
  <slot></slot>
</div>

App.svelte

<script>
  import TextField from './TextField'
  import {onMount} from 'svete'

  onMount(() => {
    // This line is funny.. I know
    document.querySelector('[name="firstname"]').focus()
  })

</script>

<TextField label="First Name" name="firstname" />
Yousuf Iqbal Hashim
  • 925
  • 1
  • 11
  • 21

3 Answers3

17

You can get a reference to the input DOM node with bind:this and export it as a prop and use it in the parent component.

Example

<!-- TextField.svelte -->
<script>
  export let label = '';
  export let name = '';
  export let placeholder = '';
  export let value = '';
  export let ref = null;
</script>

<div class="field">
  <label for={name}>{label}</label>
  <input {placeholder} type="text" {name} bind:value={value} bind:this={ref} >
  <slot></slot>
</div>

<!-- App.svelte -->
<script>
  import TextField from './TextField.svelte';
  import { onMount } from 'svelte';
    
  let ref;
    
  onMount(() => {
    ref.focus(); 
  });      
</script>

<TextField label="First Name" name="firstname" bind:ref />
Tholle
  • 108,070
  • 19
  • 198
  • 189
  • 2
    Have you tried this code? ``ref.focus()`` doesn't seem to work for me whereas I'm able to change other properties of element using ``ref``. – Yousuf Iqbal Hashim Aug 08 '19 at 05:06
  • What is 'ref' short for? It might be clearer to use a better variable name. – mikemaccana Sep 10 '22 at 13:12
  • 1
    @mikemaccana - ref just short for reference, but it's commonly used in this context so for the sake of an example should be fine. – Alicia Sykes Jan 01 '23 at 00:37
  • @Lissy93 what is `ref` a reference to? An element (just guessing)? It would be clearer to use `textfield` or `textfieldElement` or some other full word. – mikemaccana Jan 01 '23 at 21:41
6

You have several typos actually in the App.svelte.

First, importing the component.

import TextField from './TextField'

That should be:

import TextField from './TextField.svelte';

Second, the Svelte package itself.

import {onMount} from 'svete'

That should be:

import { onMount } from 'svelte';

Okay, now we are ready to code.

Since autofocus attribute should be avoided, we may use Tholle's answer as reference.

In the TextField.svelte, you handle the autofocus.

<script>
    import { onMount } from 'svelte';

    export let focused = false;
    export let label = '';
    export let name = '';
    export let placeholder = '';
    export let value = '';

    let elm;

    onMount(function() {
        elm.focus();
    });
</script>

<div class="field">
    <label for={name}>{label}</label>
    <input {placeholder} type="text" {name} bind:value={value} bind:this={elm}/>
    <slot/>
</div>

In the App.svelte, you call the component.

<script>
  import TextField from './TextField.svelte';
</script>

<TextField label="First Name" name="firstname" focused/>
<TextField label="Last Name" name="lastname" focused/>

The demo available on the Svelte REPL.

The difference between my answer and Tholle's is that focus() should be executed in the TextField component since it is component specific functionality.

wisn
  • 974
  • 10
  • 17
  • Not editing my question for silly typos so that the precious lines you wrote remain relevant :) – Yousuf Iqbal Hashim Aug 06 '19 at 09:21
  • Notice that it's not the `autofocus` attribute that should be avoided, but rather _autofocus_ itself, which is what this answer does anyway. All this does is get rid of the warning. Also, you should have an `await tick()` before calling focus otherwise your element may not exist. Lastly, you probably wanted to check for `if (focused)` before calling `elm.focus()`, otherwise that prop is useless. – sleighty Aug 23 '23 at 16:51
2

You can use the autofocus attribute.

<script>

  export let label = ''
  export let name = ''
  export let placeholder = ''
  export let value = ''

</script>

<div class="field">
  <label for={name}>{label}</label>
  <input {placeholder} type="text" {name} bind:value={value} autofocus > // <-- here
  <slot></slot>
</div>

But as stated in this answer, it's probably not a good idea from an accessibility standpoint.

Morphyish
  • 3,932
  • 8
  • 26
  • 2
    Nowadays that'll trigger a warning: `A11y: Avoid using autofocussvelte(a11y-autofocus)`. So as the OP stated, probably not the way to go. – Greg Malcolm Jan 12 '20 at 16:23