跳至主要內容

範本語法

元素指令

在 GitHub 上編輯此頁面

除了屬性之外,元素還可以有指令,用於控制元素的行為。

on:eventname

on:eventname={handler}
on:eventname|modifiers={handler}

使用 on: 指令來偵聽 DOM 事件。

App.svelte
<script>
	let count = 0;

	/** @param {MouseEvent} event */
	function handleClick(event) {
		count += 1;
	}
</script>

<button on:click={handleClick}>
	count: {count}
</button>
App.svelte
<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.isTrustedtrue 時才會觸發處理常式。也就是說,當事件是由使用者動作觸發時。

修飾詞可以串接在一起,例如 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(含)之間的數字

...以及五個雙向繫結

  • currentTime — 影片目前的播放時間,單位為秒
  • playbackRate — 影片播放的速度,其中 1 為「正常」
  • paused — 這個應該不言而喻
  • volume — 0 到 1 之間的值
  • muted — 布林值,表示播放器是否靜音

影片另外有唯讀的 videoWidthvideoHeight 繫結。

<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 轉換不會在第一次渲染時播放。您可以透過在建立元件時設定 intro: true,並將轉換標記為 global 來修改此行為。

轉換參數

與動作一樣,轉換可以有參數。

(雙重 {{大括號}} 不是特殊語法;這是一個表達式標籤內的物件文字。)

{#if visible}
	<div transition:fade={{ duration: 2000 }}>fades in and out over two seconds</div>
{/if}

自訂轉換函式

轉換可以使用自訂函式。如果回傳的物件有 css 函式,Svelte 將建立一個在元素上播放的 CSS 動畫。

傳遞給 csst 參數是在套用 easing 函式後介於 01 之間的值。進入轉換從 01 執行,離開轉換從 10 執行 — 換句話說,1 是元素的自然狀態,就像沒有套用任何轉換一樣。u 參數等於 1 - t

在轉換開始之前,會重複呼叫函式,並使用不同的 tu 參數。

<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 函式,該函式會在轉換期間使用相同的 tu 參數呼叫。

如果可以的話,使用 css 取代 tick,CSS 動畫可以在主執行緒之外執行,避免在較慢的裝置上出現卡頓。

App.svelte
<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}
App.svelte
<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 - 根據轉場類型,為 inoutboth 之一

轉場事件

具有轉場的元素除了任何標準 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,in 轉場將繼續與 out 轉場「播放」,而不是反轉。如果中斷 out 轉場,轉場將從頭開始重新啟動。

{#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
}

keyed each 區塊的內容重新排序時,會觸發動畫。當元素新增或移除時,動畫不會執行,只有當 each 區塊中現有資料項目的索引變更時,才會執行。Animate 指令必須位於 keyed 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}

自訂動畫函式

動畫可以使用提供nodeanimation物件和任何參數作為引數的自訂函式。animation參數是一個包含fromto屬性的物件,每個屬性都包含一個DOMRect,描述元素在其開始結束位置的幾何形狀。from屬性是在其開始位置的元素的 DOMRect,而to屬性是在清單重新排序且 DOM 更新後,元素在其最終位置的 DOMRect。

如果傳回的物件有css方法,Svelte 將會建立一個在元素上播放的 CSS 動畫。

傳遞給csst引數是一個在應用easing函式後從01的值。u引數等於1 - t

此函式會在動畫開始之前重複呼叫,並使用不同的tu引數。

<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 函數,它會在動畫 期間 以相同的 tu 參數呼叫。

如果可以的話,使用 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}
上一個 特殊標籤
下一個 元件指令