mirror of https://github.com/sveltejs/svelte
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
306 lines
8.7 KiB
306 lines
8.7 KiB
6 months ago
|
---
|
||
|
title: Bindings
|
||
|
---
|
||
|
|
||
|
- how for dom elements
|
||
|
- list of all bindings
|
||
|
- how for components
|
||
|
|
||
|
Most of the time a clear separation between data flowing down and events going up is worthwhile and results in more robust apps. But in some cases - especially when interacting with form elements - it's more ergonomic to declare a two way binding. Svelte provides many element bindings out of the box, and also allows component bindings.
|
||
|
|
||
|
## bind:_property_ for elements
|
||
|
|
||
|
```svelte
|
||
|
<!--- copy: false --->
|
||
|
bind:property={variable}
|
||
|
```
|
||
|
|
||
|
Data ordinarily flows down, from parent to child. The `bind:` directive allows data to flow the other way, from child to parent. Most bindings are specific to particular elements.
|
||
|
|
||
|
The simplest bindings reflect the value of a property, such as `input.value`.
|
||
|
|
||
|
```svelte
|
||
|
<input bind:value={name} />
|
||
|
<textarea bind:value={text} />
|
||
|
|
||
|
<input type="checkbox" bind:checked={yes} />
|
||
|
```
|
||
|
|
||
|
If the name matches the value, you can use a shorthand.
|
||
|
|
||
|
```svelte
|
||
|
<input bind:value />
|
||
|
<!-- equivalent to
|
||
|
<input bind:value={value} />
|
||
|
-->
|
||
|
```
|
||
|
|
||
|
Numeric input values are coerced; even though `input.value` is a string as far as the DOM is concerned, Svelte will treat it as a number. If the input is empty or invalid (in the case of `type="number"`), the value is `undefined`.
|
||
|
|
||
|
```svelte
|
||
|
<input type="number" bind:value={num} />
|
||
|
<input type="range" bind:value={num} />
|
||
|
```
|
||
|
|
||
|
On `<input>` elements with `type="file"`, you can use `bind:files` to get the [`FileList` of selected files](https://developer.mozilla.org/en-US/docs/Web/API/FileList). It is readonly.
|
||
|
|
||
|
```svelte
|
||
|
<label for="avatar">Upload a picture:</label>
|
||
|
<input accept="image/png, image/jpeg" bind:files id="avatar" name="avatar" type="file" />
|
||
|
```
|
||
|
|
||
|
If you're using `bind:` directives together with `on:` directives, the order that they're defined in affects the value of the bound variable when the event handler is called.
|
||
|
|
||
|
```svelte
|
||
|
<script>
|
||
|
let value = 'Hello World';
|
||
|
</script>
|
||
|
|
||
|
<input
|
||
|
on:input={() => console.log('Old value:', value)}
|
||
|
bind:value
|
||
|
on:input={() => console.log('New value:', value)}
|
||
|
/>
|
||
|
```
|
||
|
|
||
|
Here we were binding to the value of a text input, which uses the `input` event. Bindings on other elements may use different events such as `change`.
|
||
|
|
||
|
## Binding `<select>` value
|
||
|
|
||
|
A `<select>` value binding corresponds to the `value` property on the selected `<option>`, which can be any value (not just strings, as is normally the case in the DOM).
|
||
|
|
||
|
```svelte
|
||
|
<select bind:value={selected}>
|
||
|
<option value={a}>a</option>
|
||
|
<option value={b}>b</option>
|
||
|
<option value={c}>c</option>
|
||
|
</select>
|
||
|
```
|
||
|
|
||
|
A `<select multiple>` element behaves similarly to a checkbox group. The bound variable is an array with an entry corresponding to the `value` property of each selected `<option>`.
|
||
|
|
||
|
```svelte
|
||
|
<select multiple bind:value={fillings}>
|
||
|
<option value="Rice">Rice</option>
|
||
|
<option value="Beans">Beans</option>
|
||
|
<option value="Cheese">Cheese</option>
|
||
|
<option value="Guac (extra)">Guac (extra)</option>
|
||
|
</select>
|
||
|
```
|
||
|
|
||
|
When the value of an `<option>` matches its text content, the attribute can be omitted.
|
||
|
|
||
|
```svelte
|
||
|
<select multiple bind:value={fillings}>
|
||
|
<option>Rice</option>
|
||
|
<option>Beans</option>
|
||
|
<option>Cheese</option>
|
||
|
<option>Guac (extra)</option>
|
||
|
</select>
|
||
|
```
|
||
|
|
||
|
Elements with the `contenteditable` attribute support the following bindings:
|
||
|
|
||
|
- [`innerHTML`](https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML)
|
||
|
- [`innerText`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/innerText)
|
||
|
- [`textContent`](https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent)
|
||
|
|
||
|
There are slight differences between each of these, read more about them [here](https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent#Differences_from_innerText).
|
||
|
|
||
|
<!-- for some reason puts the comment and html on same line -->
|
||
|
<!-- prettier-ignore -->
|
||
|
```svelte
|
||
|
<div contenteditable="true" bind:innerHTML={html} />
|
||
|
```
|
||
|
|
||
|
`<details>` elements support binding to the `open` property.
|
||
|
|
||
|
```svelte
|
||
|
<details bind:open={isOpen}>
|
||
|
<summary>Details</summary>
|
||
|
<p>Something small enough to escape casual notice.</p>
|
||
|
</details>
|
||
|
```
|
||
|
|
||
|
## Media element bindings
|
||
|
|
||
|
Media elements (`<audio>` and `<video>`) have their own set of bindings — seven _readonly_ ones...
|
||
|
|
||
|
- `duration` (readonly) — the total duration of the video, in seconds
|
||
|
- `buffered` (readonly) — an array of `{start, end}` objects
|
||
|
- `played` (readonly) — ditto
|
||
|
- `seekable` (readonly) — ditto
|
||
|
- `seeking` (readonly) — boolean
|
||
|
- `ended` (readonly) — boolean
|
||
|
- `readyState` (readonly) — number between (and including) 0 and 4
|
||
|
|
||
|
...and five _two-way_ bindings:
|
||
|
|
||
|
- `currentTime` — the current playback time in the video, in seconds
|
||
|
- `playbackRate` — how fast or slow to play the video, where 1 is 'normal'
|
||
|
- `paused` — this one should be self-explanatory
|
||
|
- `volume` — a value between 0 and 1
|
||
|
- `muted` — a boolean value indicating whether the player is muted
|
||
|
|
||
|
Videos additionally have readonly `videoWidth` and `videoHeight` bindings.
|
||
|
|
||
|
```svelte
|
||
|
<video
|
||
|
src={clip}
|
||
|
bind:duration
|
||
|
bind:buffered
|
||
|
bind:played
|
||
|
bind:seekable
|
||
|
bind:seeking
|
||
|
bind:ended
|
||
|
bind:readyState
|
||
|
bind:currentTime
|
||
|
bind:playbackRate
|
||
|
bind:paused
|
||
|
bind:volume
|
||
|
bind:muted
|
||
|
bind:videoWidth
|
||
|
bind:videoHeight
|
||
|
/>
|
||
|
```
|
||
|
|
||
|
## Image element bindings
|
||
|
|
||
|
Image elements (`<img>`) have two readonly bindings:
|
||
|
|
||
|
- `naturalWidth` (readonly) — the original width of the image, available after the image has loaded
|
||
|
- `naturalHeight` (readonly) — the original height of the image, available after the image has loaded
|
||
|
|
||
|
```svelte
|
||
|
<img
|
||
|
bind:naturalWidth
|
||
|
bind:naturalHeight
|
||
|
></img>
|
||
|
```
|
||
|
|
||
|
## Block-level element bindings
|
||
|
|
||
|
Block-level elements have 4 read-only bindings, measured using a technique similar to [this one](http://www.backalleycoder.com/2013/03/18/cross-browser-event-based-element-resize-detection/):
|
||
|
|
||
|
- `clientWidth`
|
||
|
- `clientHeight`
|
||
|
- `offsetWidth`
|
||
|
- `offsetHeight`
|
||
|
|
||
|
```svelte
|
||
|
<div bind:offsetWidth={width} bind:offsetHeight={height}>
|
||
|
<Chart {width} {height} />
|
||
|
</div>
|
||
|
```
|
||
|
|
||
|
## bind:group
|
||
|
|
||
|
```svelte
|
||
|
<!--- copy: false --->
|
||
|
bind:group={variable}
|
||
|
```
|
||
|
|
||
|
Inputs that work together can use `bind:group`.
|
||
|
|
||
|
```svelte
|
||
|
<script>
|
||
|
let tortilla = 'Plain';
|
||
|
|
||
|
/** @type {Array<string>} */
|
||
|
let fillings = [];
|
||
|
</script>
|
||
|
|
||
|
<!-- grouped radio inputs are mutually exclusive -->
|
||
|
<input type="radio" bind:group={tortilla} value="Plain" />
|
||
|
<input type="radio" bind:group={tortilla} value="Whole wheat" />
|
||
|
<input type="radio" bind:group={tortilla} value="Spinach" />
|
||
|
|
||
|
<!-- grouped checkbox inputs populate an array -->
|
||
|
<input type="checkbox" bind:group={fillings} value="Rice" />
|
||
|
<input type="checkbox" bind:group={fillings} value="Beans" />
|
||
|
<input type="checkbox" bind:group={fillings} value="Cheese" />
|
||
|
<input type="checkbox" bind:group={fillings} value="Guac (extra)" />
|
||
|
```
|
||
|
|
||
|
> `bind:group` only works if the inputs are in the same Svelte component.
|
||
|
|
||
|
## bind:this
|
||
|
|
||
|
```svelte
|
||
|
<!--- copy: false --->
|
||
|
bind:this={dom_node}
|
||
|
```
|
||
|
|
||
|
To get a reference to a DOM node, use `bind:this`.
|
||
|
|
||
|
```svelte
|
||
|
<script>
|
||
|
import { onMount } from 'svelte';
|
||
|
|
||
|
/** @type {HTMLCanvasElement} */
|
||
|
let canvasElement;
|
||
|
|
||
|
onMount(() => {
|
||
|
const ctx = canvasElement.getContext('2d');
|
||
|
drawStuff(ctx);
|
||
|
});
|
||
|
</script>
|
||
|
|
||
|
<canvas bind:this={canvasElement} />
|
||
|
```
|
||
|
|
||
|
Components also support `bind:this`, allowing you to interact with component instances programmatically.
|
||
|
|
||
|
```svelte
|
||
|
<!--- App.svelte --->
|
||
|
<ShoppingCart bind:this={cart} />
|
||
|
|
||
|
<button onclick={() => cart.empty()}> Empty shopping cart </button>
|
||
|
```
|
||
|
|
||
|
```svelte
|
||
|
<!--- ShoppingCart.svelte --->
|
||
|
<script>
|
||
|
// All instance exports are available on the instance object
|
||
|
export function empty() {
|
||
|
// ...
|
||
|
}
|
||
|
</script>
|
||
|
```
|
||
|
|
||
|
> Note that we can't do `{cart.empty}` since `cart` is `undefined` when the button is first rendered and throws an error.
|
||
|
|
||
|
## bind:_property_ for components
|
||
|
|
||
|
```svelte
|
||
|
bind:property={variable}
|
||
|
```
|
||
|
|
||
|
You can bind to component props using the same syntax as for elements.
|
||
|
|
||
|
```svelte
|
||
|
<Keypad bind:value={pin} />
|
||
|
```
|
||
|
|
||
|
While Svelte props are reactive without binding, that reactivity only flows downward into the component by default. Using `bind:property` allows changes to the property from within the component to flow back up out of the component.
|
||
|
|
||
|
To mark a property as bindable, use the `$bindable` rune:
|
||
|
|
||
|
```svelte
|
||
|
<script>
|
||
|
let { readonlyProperty, bindableProperty = $bindable() } = $props();
|
||
|
</script>
|
||
|
```
|
||
|
|
||
|
Declaring a property as bindable means it _can_ be used using `bind:`, not that it _must_ be used using `bind:`.
|
||
|
|
||
|
Bindable properties can have a fallback value:
|
||
|
|
||
|
```svelte
|
||
|
<script>
|
||
|
let { bindableProperty = $bindable('fallback value') } = $props();
|
||
|
</script>
|
||
|
```
|
||
|
|
||
|
This fallback value _only_ applies when the property is _not_ bound. When the property is bound and a fallback value is present, the parent is expected to provide a value other than `undefined`, else a runtime error is thrown. This prevents hard-to-reason-about situations where it's unclear which value should apply.
|