除了属性之外,元素还可以有指令,它们以某种方式控制元素的行为。
on:eventname永久链接
on:eventname={handler}
on:eventname|modifiers={handler}
使用 on:
指令来监听 DOM 事件。
<script>
let count = 0;
/** @param {MouseEvent} event */
function handleClick(event) {
count += 1;
}
</script>
<button on:click={handleClick}>
count: {count}
</button>
<script lang="ts">
let count = 0;
function handleClick(event: MouseEvent) {
count += 1;
}
</script>
<button on:click={handleClick}>
count: {count}
</button>
处理程序可以内联声明,而不会造成性能损失。与属性一样,为了语法高亮,指令值可以加引号。
<button on:click={() => (count += 1)}>
count: {count}
</button>
使用 |
字符将修饰符添加到 DOM 事件。
<form on:submit|preventDefault={handleSubmit}>
<!-- the `submit` event's default is prevented,
so the page won't reload -->
</form>
可以使用以下修饰符
preventDefault
— 在运行处理程序之前调用event.preventDefault()
stopPropagation
— 调用event.stopPropagation()
,防止事件到达下一个元素stopImmediatePropagation
- 调用event.stopImmediatePropagation()
,防止触发同一事件的其他侦听器。passive
— 提高触控/滚轮事件的滚动性能(Svelte 会在安全的情况下自动添加它)nonpassive
— 明确设置passive: false
capture
— 在捕获阶段而不是冒泡阶段触发处理程序once
— 在首次运行后移除处理程序self
— 仅当event.target
是元素本身时才触发处理程序trusted
— 仅当event.isTrusted
为true
时才触发处理程序。即,如果事件是由用户操作触发的。
修饰符可以链接在一起,例如on:click|once|capture={...}
。
如果on:
指令在没有值的情况下使用,组件将转发事件,这意味着组件的使用者可以监听它。
<button on:click> The component itself will emit the click event </button>
可以为同一事件有多个事件监听器
<script>
let counter = 0;
function increment() {
counter = counter + 1;
}
/** @param {MouseEvent} event */
function track(event) {
trackEvent(event);
}
</script>
<button on:click={increment} on:click={track}>Click me!</button>
bind:property永久链接
bind:property={variable}
数据通常从父级向下流向子级。bind:
指令允许数据从子级向上流向父级。大多数绑定特定于特定元素。
最简单的绑定反映属性的值,例如input.value
。
<input bind:value={name} />
<textarea bind:value={text} />
<input type="checkbox" bind:checked={yes} />
如果名称与值匹配,可以使用简写。
<input bind:value />
<!-- equivalent to
<input bind:value={value} />
-->
数字输入值被强制转换;即使input.value
在 DOM 中是字符串,Svelte 也会将其视为数字。如果输入为空或无效(在type="number"
的情况下),则值为null
。
<input type="number" bind:value={num} />
<input type="range" bind:value={num} />
在具有type="file"
的<input>
元素上,可以使用bind:files
获取所选文件的FileList
。它是只读的。
<label for="avatar">Upload a picture:</label>
<input accept="image/png, image/jpeg" bind:files id="avatar" name="avatar" type="file" />
如果将bind:
指令与on:
指令一起使用,则它们在定义中的顺序会影响调用事件处理程序时绑定的变量的值。
<script>
let value = 'Hello World';
</script>
<input
on:input={() => console.log('Old value:', value)}
bind:value
on:input={() => console.log('New value:', value)}
/>
在这里,我们绑定到文本输入的值,它使用input
事件。其他元素上的绑定可能使用不同的事件,例如change
。
绑定 <select> 值永久链接
<select>
值绑定对应于所选<option>
上的value
属性,该属性可以是任何值(不仅仅是字符串,如 DOM 中通常的情况)。
<select bind:value={selected}>
<option value={a}>a</option>
<option value={b}>b</option>
<option value={c}>c</option>
</select>
<select multiple>
元素的行为类似于复选框组。绑定的变量是一个数组,其中每个条目对应于每个所选<option>
的value
属性。
<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>
当<option>
的值与其文本内容匹配时,可以省略该属性。
<select multiple bind:value={fillings}>
<option>Rice</option>
<option>Beans</option>
<option>Cheese</option>
<option>Guac (extra)</option>
</select>
具有contenteditable
属性的元素支持以下绑定
它们之间有细微的差别,可以在这里了解更多信息。
<div contenteditable="true" bind:innerHTML={html} />
<details>
元素支持绑定到open
属性。
<details bind:open={isOpen}>
<summary>Details</summary>
<p>Something small enough to escape casual notice.</p>
</details>
媒体元素绑定永久链接
媒体元素(<audio>
和 <video>
)有自己的一组绑定——七个只读绑定...
duration
(只读)——视频的总时长,单位为秒buffered
(只读)——{start, end}
对象的数组played
(只读)——同上seekable
(只读)——同上seeking
(只读)——布尔值ended
(只读)——布尔值readyState
(只读)——0 到 4(包括 0 和 4)之间的数字
...和五个双向绑定
currentTime
——视频中当前的播放时间,单位为秒playbackRate
——播放视频的速度,其中 1 为“正常”paused
——这个应该不言自明volume
——0 到 1 之间的值muted
——表示播放器是否静音的布尔值
视频还具有只读的 videoWidth
和 videoHeight
绑定。
<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
/>
图像元素绑定永久链接
图像元素(<img>
)有两个只读绑定
naturalWidth
(只读)——图像的原始宽度,在图像加载后可用naturalHeight
(只读)——图像的原始高度,在图像加载后可用
<img
bind:naturalWidth
bind:naturalHeight
></img>
块级元素绑定永久链接
块级元素有 4 个只读绑定,使用类似于 此方法 进行测量
clientWidth
clientHeight
offsetWidth
offsetHeight
<div bind:offsetWidth={width} bind:offsetHeight={height}>
<Chart {width} {height} />
</div>
bind:group永久链接
bind:group={variable}
一起工作的输入可以使用 bind:group
。
<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
仅在输入位于同一 Svelte 组件中时才有效。
bind:this永久链接
bind:this={dom_node}
要获取对 DOM 节点的引用,请使用 bind:this
。
<script>
import { onMount } from 'svelte';
/** @type {HTMLCanvasElement} */
let canvasElement;
onMount(() => {
const ctx = canvasElement.getContext('2d');
drawStuff(ctx);
});
</script>
<canvas bind:this={canvasElement} />
class:name永久链接
class:name={value}
class:name
class:
指令提供了一种更简便的方法来切换元素上的类。
<!-- These are equivalent -->
<div class={isActive ? 'active' : ''}>...</div>
<div class:active={isActive}>...</div>
<!-- Shorthand, for when name and value match -->
<div class:active>...</div>
<!-- Multiple class toggles can be included -->
<div class:active class:inactive={!active} class:isAdmin>...</div>
style:property永久链接
style:property={value}
style:property="value"
style:property
style:
指令提供了一种简写,用于在元素上设置多个样式。
<!-- These are equivalent -->
<div style:color="red">...</div>
<div style="color: red;">...</div>
<!-- Variables can be used -->
<div style:color={myColor}>...</div>
<!-- Shorthand, for when property and variable name match -->
<div style:color>...</div>
<!-- Multiple styles can be included -->
<div style:color style:width="12rem" style:background-color={darkMode ? 'black' : 'white'}>...</div>
<!-- Styles can be marked as important -->
<div style:color|important="red">...</div>
当 style:
指令与 style
属性结合使用时,指令将优先。
<div style="color: blue;" style:color="red">This will be red</div>
use:action永久链接
use:action
use:action={parameters}
ts
action = (node :HTMLElement ,parameters : any) => {update ?: ( parameters : any) => void,destroy ?: () => void }
动作是在创建元素时调用的函数。它们可以返回一个带有 destroy
方法的对象,该方法在卸载元素后调用
<script>
/** @type {import('svelte/action').Action} */
function foo(node) {
// the node has been mounted in the DOM
return {
destroy() {
// the node has been removed from the DOM
}
};
}
</script>
<div use:foo />
动作可以有一个参数。如果返回值有 update
方法,则每当该参数更改时都会调用该方法,即 Svelte 将更新应用到标记后立即调用。
不要担心我们为每个组件实例重新声明
foo
函数——Svelte 会将不依赖于本地状态的任何函数提升到组件定义之外。
<script>
export let bar;
/** @type {import('svelte/action').Action} */
function foo(node, bar) {
// the node has been mounted in the DOM
return {
update(bar) {
// the value of `bar` has changed
},
destroy() {
// the node has been removed from the DOM
}
};
}
</script>
<div use:foo={bar} />
在 svelte/action
页面中阅读更多内容。
transition:fn永久链接
transition:fn
transition:fn={params}
transition:fn|global
transition:fn|global={params}
transition:fn|local
transition:fn|local={params}
ts
transition = (node :HTMLElement ,params : any,options : {direction : 'in' | 'out' | 'both' }) => {delay ?: number ,duration ?: number ,easing ?: ( t : number) =>number ,css ?: ( t : number,u : number) =>string ,tick ?: ( t : number,u : number) => void}
当元素进入或离开 DOM(由于状态更改)时,会触发过渡。
当一个块正在过渡时,块内的所有元素(包括那些没有自己的过渡的元素)都会保留在 DOM 中,直到该块中的所有过渡都已完成。
transition:
指令表示一个双向过渡,这意味着它可以在过渡进行时平滑地逆转。
{#if visible}
<div transition:fade>fades in and out</div>
{/if}
默认情况下,过渡是本地的(在 Svelte 3 中,它们默认是全局的)。本地过渡仅在它们所属的块被创建或销毁时播放,而不是在父块被创建或销毁时播放。
{#if x}
{#if y}
<!-- Svelte 3: <p transition:fade|local> -->
<p transition:fade>fades in and out only when y changes</p>
<!-- Svelte 3: <p transition:fade> -->
<p transition:fade|global>fades in and out when x or y change</p>
{/if}
{/if}
默认情况下,首次渲染时不会播放引入过渡。你可以通过在 创建组件 时设置
intro: true
并将过渡标记为global
来修改此行为。
过渡参数永久链接
与动作一样,过渡也可以有参数。
(双重 {{大括号}}
不是特殊语法;这是一个表达式标记内的对象字面量。)
{#if visible}
<div transition:fade={{ duration: 2000 }}>fades in and out over two seconds</div>
{/if}
自定义过渡函数永久链接
过渡可以使用自定义函数。如果返回的对象具有 css
函数,Svelte 将创建一个在元素上播放的 CSS 动画。
传递给 css
的 t
参数是在应用 easing
函数后介于 0
和 1
之间的值。进入 过渡从 0
运行到 1
,退出 过渡从 1
运行到 0
— 换句话说,1
是元素的自然状态,就好像没有应用过渡一样。u
参数等于 1 - t
。
该函数在过渡开始之前被重复调用,带有不同的 t
和 u
参数。
<script>
import { elasticOut } from 'svelte/easing';
/** @type {boolean} */
export let visible;
/**
* @param {HTMLElement} node
* @param {{ delay?: number, duration?: number, easing?: (t: number) => number }} params
*/
function whoosh(node, params) {
const existingTransform = getComputedStyle(node).transform.replace('none', '');
return {
delay: params.delay || 0,
duration: params.duration || 400,
easing: params.easing || elasticOut,
css: (t, u) => `transform: ${existingTransform} scale(${t})`
};
}
</script>
{#if visible}
<div in:whoosh>whooshes in</div>
{/if}
自定义过渡函数还可以返回一个 tick
函数,该函数在过渡期间使用相同的 t
和 u
参数调用。
如果可以使用
css
代替tick
,请这样做 — CSS 动画可以在主线程之外运行,防止在较慢的设备上出现卡顿。
<script>
export let visible = false;
/**
* @param {HTMLElement} node
* @param {{ speed?: number }} params
*/
function typewriter(node, { speed = 1 }) {
const valid = node.childNodes.length === 1 && node.childNodes[0].nodeType === Node.TEXT_NODE;
if (!valid) {
throw new Error(`This transition only works on elements with a single text node child`);
}
const text = node.textContent;
const duration = text.length / (speed * 0.01);
return {
duration,
tick: (t) => {
const i = ~~(text.length * t);
node.textContent = text.slice(0, i);
}
};
}
</script>
{#if visible}
<p in:typewriter={{ speed: 1 }}>The quick brown fox jumps over the lazy dog</p>
{/if}
<script lang="ts">
export let visible = false;
function typewriter(node: HTMLElement, { speed = 1 }: { speed?: number }) {
const valid = node.childNodes.length === 1 && node.childNodes[0].nodeType === Node.TEXT_NODE;
if (!valid) {
throw new Error(`This transition only works on elements with a single text node child`);
}
const text = node.textContent;
const duration = text.length / (speed * 0.01);
return {
duration,
tick: (t) => {
const i = ~~(text.length * t);
node.textContent = text.slice(0, i);
},
};
}
</script>
{#if visible}
<p in:typewriter={{ speed: 1 }}>The quick brown fox jumps over the lazy dog</p>
{/if}
如果一个过渡返回一个函数而不是一个过渡对象,那么该函数将在下一个微任务中被调用。这允许多个过渡进行协调,从而实现交叉淡化效果。
过渡函数还接收第三个参数options
,其中包含有关过渡的信息。
options
对象中可用的值是
direction
-in
、out
或both
之一,具体取决于过渡的类型
过渡事件永久链接
除了任何标准 DOM 事件外,具有过渡的元素还将分派以下事件
introstart
introend
outrostart
outroend
{#if visible}
<p
transition:fly={{ y: 200, duration: 2000 }}
on:introstart={() => (status = 'intro started')}
on:outrostart={() => (status = 'outro started')}
on:introend={() => (status = 'intro ended')}
on:outroend={() => (status = 'outro ended')}
>
Flies in and out
</p>
{/if}
in:fn/out:fn永久链接
in:fn
in:fn={params}
in:fn|global
in:fn|global={params}
in:fn|local
in:fn|local={params}
out:fn
out:fn={params}
out:fn|global
out:fn|global={params}
out:fn|local
out:fn|local={params}
类似于transition:
,但仅适用于进入 (in:
) 或离开 (out:
) DOM 的元素。
与transition:
不同,使用in:
和out:
应用的过渡不是双向的——如果在过渡进行中 outro 了块,则进入过渡将继续与退出过渡“一起播放”,而不是反向。如果中止了退出过渡,过渡将从头开始重新启动。
{#if visible}
<div in:fly out:fade>flies in, fades out</div>
{/if}
animate:fn永久链接
animate:name
animate:name={params}
ts
animation = (node :HTMLElement , {from :DOMRect ,to :DOMRect } ,params : any) => {delay ?: number ,duration ?: number ,easing ?: ( t : number) =>number ,css ?: ( t : number,u : number) =>string ,tick ?: ( t : number,u : number) => void}
ts
DOMRect {bottom:number ,height :number ,left :number ,right :number ,top :number ,width :number ,x :number ,y :number }
当键控 each 块的内容重新排序时,将触发动画。当添加或删除元素时,动画不会运行,仅当 each 块中现有数据项的索引更改时才会运行。Animate 指令必须位于键控 each 块的直接子元素上。
动画可与 Svelte 的内置动画函数或自定义动画函数一起使用。
<!-- When `list` is reordered the animation will run-->
{#each list as item, index (item)}
<li animate:flip>{item}</li>
{/each}
动画参数永久链接
与动作和过渡一样,动画也可以有参数。
(双重 {{大括号}}
不是特殊语法;这是一个表达式标记内的对象字面量。)
{#each list as item, index (item)}
<li animate:flip={{ delay: 500 }}>{item}</li>
{/each}
自定义动画函数永久链接
动画可以使用自定义函数,这些函数提供node
、animation
对象和任何parameters
作为参数。animation
参数是一个包含from
和to
属性的对象,每个属性都包含一个DOMRect,描述了元素在其start
和end
位置的几何形状。from
属性是在其起始位置的元素的 DOMRect,而to
属性是在列表重新排序并更新 DOM 后的最终位置的元素的 DOMRect。
如果返回的对象具有 css
方法,Svelte 将创建一个在元素上播放的 CSS 动画。
传递给 css
的 t
参数是一个值,在应用 easing
函数后从 0
和 1
开始。u
参数等于 1 - t
。
该函数在动画开始之前重复调用,并带有不同的 t
和 u
参数。
<script>
import { cubicOut } from 'svelte/easing';
/**
* @param {HTMLElement} node
* @param {{ from: DOMRect; to: DOMRect }} states
* @param {any} params
*/
function whizz(node, { from, to }, params) {
const dx = from.left - to.left;
const dy = from.top - to.top;
const d = Math.sqrt(dx * dx + dy * dy);
return {
delay: 0,
duration: Math.sqrt(d) * 120,
easing: cubicOut,
css: (t, u) => `transform: translate(${u * dx}px, ${u * dy}px) rotate(${t * 360}deg);`
};
}
</script>
{#each list as item, index (item)}
<div animate:whizz>{item}</div>
{/each}
自定义动画函数还可以返回一个 tick
函数,该函数在动画期间使用相同的 t
和 u
参数调用。
如果可以使用
css
代替tick
,请这样做 — CSS 动画可以在主线程之外运行,防止在较慢的设备上出现卡顿。
<script>
import { cubicOut } from 'svelte/easing';
/**
* @param {HTMLElement} node
* @param {{ from: DOMRect; to: DOMRect }} states
* @param {any} params
*/
function whizz(node, { from, to }, params) {
const dx = from.left - to.left;
const dy = from.top - to.top;
const d = Math.sqrt(dx * dx + dy * dy);
return {
delay: 0,
duration: Math.sqrt(d) * 120,
easing: cubicOut,
tick: (t, u) => Object.assign(node.style, { color: t > 0.5 ? 'Pink' : 'Blue' })
};
}
</script>
{#each list as item, index (item)}
<div animate:whizz>{item}</div>
{/each}