on:
<div on:mousemove={handleMousemove}>
The mouse position is {m.x} x {m.y}
</div>
<div on:mousemove="{e => m = { x: e.clientX, y: e.clientY }}">
The mouse position is {m.x} x {m.y}
</div>
따옴표는 선택 사항으로 일부 환경에서는 구문 강조 표시로 사용됩니다.
💡 일부 프레임워크에서는 내부 성능상의 이유로 인라인 이벤트 핸들러 사용을 지양하는것이 좋으나 Svelte는 해당하지 않으며 어떤 형식이든 상관없다.<button on:click|once={handleClick}>
Click me
</button>
preventDefault
event.preventDefault()
: 핸들러를 실행하기 전에 호출하여 핸들러의 이벤트 동작을 중단시킵니다.
stopPropagation
event.stopPropagation()
다음 요소에 이벤트가 도달하지 못하도록 이벤트 전파를 막습니다.
passive
: 터치/휠 이벤트에서 스크롤 성능을 향상시킵니다.
nonpassivepassive: false
: 명시적으로 설정
capture
: 버블링 단계 대신 캡쳐링단계 에서 이벤트를 실행합니다. ( MDN 문서 ).
once
핸들러가 처음 실행된 후 핸들러를 제거하여 한번만 이벤트가 실행됩니다.
self
: event.target이 요소 자체(self)인 경우에만 핸들러를 실행합니다.
trusted
: event.isTrustedtrue 인 경우에만 핸들러를 트리거합니다. 즉, 이벤트가 사용자 작업에 의해 트리거되는 경우입니다.
on:click|once|capture={...}
⇒ 여러개의 수정자 연결
Svelte에서는 이벤트 디스패처(dispatcher)를 사용하여 이벤트를 전달할 수 있습니다. Svelte는 자식 컴포넌트에서 이벤트가 발생하면 해당 이벤트를 부모 컴포넌트에게 전달하고 부모 컴포넌트에서 구현된 이벤트 처리 함수를 실행합니다.
다음 예제는 자식 컴포넌트에서 click 이벤트가 발생하면 부모 컴포넌트에서 구현된 이벤트 처리 함수를 실행합니다.
import Inner from './Inner.svelte';
function handleMessage(event) {
alert(event.detail.text);
}
</script>
<Inner on:message={handleMessage}/>
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
function sayHello() {
dispatch('message', {
text: 'Hello!'
});
}
</script>
<button on:click={sayHello}>
Click to say hello
</button>
자식 컴포넌트는 svelte의 createEventDispatcher 함수를 호출하여 Displatcher를 생성합니다. Dispatcher는 첫번째 매개변수로 이벤트 이름을 작성하고, 두번째로는 이벤트 핸들러에 전달할 인자를 설정합니다.
부모 컴포넌트는 자식 컴포넌트에서 디스패치로 보낸 이벤트 이름인 message와 이벤트 핸들러 함수 (handleMessage)을 연결하여 사용합니다. 이벤트가 발생하면 event의 detail 프로퍼티에서 dispatch함수의 두번째 매개변수로 지정한 데이터 값을 확인할 수 있습니다.
import Inner from './Inner.svelte';
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
function forward(event) {
dispatch('message', event.detail);
}
</script>
<Inner on:message={forward}/>
import Inner from './Inner.svelte';
</script>
<Inner on:message/>
//svelte에서 지원하는 줄임말로 '모든 message 이벤트 전달' 의미
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
function sayHello() {
dispatch('message', {
text: 'Hello!'
});
}
</script>
<button on:click={sayHello}>
Click to say hello
</button>
import CustomButton from './CustomButton.svelte';
function handleClick() {
alert('Button Clicked');
}
</script>
<CustomButton on:click={handleClick}/>
<button on:click>
Click me
</button>
<style>
button {
background: #E2E8F0;
color: #64748B;
border: unset;
border-radius: 6px;
padding: .75rem 1.5rem;
cursor: pointer;
}
button:hover {
background: #CBD5E1;
color: #475569;
}
button:focus {
background: #94A3B8;
color: #F1F5F9;
}
</style>
DOM 이벤트에서 이벤트 전달을 할 때는 예제와 같이 <CustomButton>
클릭에 대한 알림을 받고 싶다면, CustomButton.svelte의 <button>
요소에 click
이벤트를 전달하면 됩니다.일반적인 Svelte의 데이터 흐름 방식은 하향식
으로 부모에서 자식의 방향으로 흘러갑니다. 그러나 bind
요소를 사용하게 되면 아래 예제의 input 값이 변경되게 되면, h1 태그 속 name 또한 즉각적으로 변경되어 반영됩니다.
let name = 'world';
</script>
<input bind:value={name}>
<h1>Hello {name}!</h1>
<label>
<input type=number bind:value={b} min=0 max=10>
<input type=range bind:value={b} min=0 max=10>
</label>
<input type=checkbox bind:checked={yes}>
{#if yes}
...
{:else}
...
{/if}
input.value
에 바인딩 하는 대신 input.checked
에 바인딩 합니다. bind:group
특성을 사용하여 묶을 수 있습니다.<label>
<input type=radio bind:group={scoops} name="scoops" value={1}>
One scoop
</label>
<label>
<input type=radio bind:group={scoops} name="scoops" value={2}>
Two scoops
</label>
<label>
<input type=radio bind:group={scoops} name="scoops" value={3}>
Three scoops
</label>
import { marked } from 'marked';
let text= `Some words are *italic*, some are **bold**`;
</script>
{@html marked(text)}
<textarea bind:value={text}></textarea>
textarea요소는 input과 유사하게 bind:value
를 사용합니다. 만약 이름이 일치하는 경우 단축 형식을 사용할 수 있습니다.<textarea bind:value></textarea>
⇒ svelte에서 지원하는 단축 형식으로 모든 바인딩에 적용됩니다.bind:value
를 사용할 수 있습니다.<select bind:value={selected} on:change="{() => answer = ''}">
{#each questions as question}
<option value={question}>
{question.text}
</option>
{/each}
</select>
multiple
속성을 가질 수 있으며 이 경우 단일 값을 선택하는 대신 배열을 채우게 됩니다.Each
블록 속성에 bindingeach
블록 내부의 속성에도 바인딩 할 수 있습니다.
{#each todos as todo}
<div class:done={todo.done}>
<input
type=checkbox
bind:checked={todo.done}
>
<input
placeholder="What needs to be done?"
bind:value={todo.text}
>
</div>
{/each}
그러나 이러한 input 요소와 사용하게 되면, 배열이 변경되기 때문에 변경할 수 없는 데이터로 작업하는 경우 이벤트 핸들러를 사용하는 것이 좋습니다.
<audio>
및 <video>
요소에는 바인딩할 수 있는 여러 속성이 있습니다 .
<video
poster="https://sveltejs.github.io/assets/caminandes-llamigos.jpg"
src="https://sveltejs.github.io/assets/caminandes-llamigos.mp4"
on:mousemove={handleMove}
on:touchmove|preventDefault={handleMove}
on:mousedown={handleMousedown}
on:mouseup={handleMouseup}
bind:currentTime={time}
bind:duration
bind:paused>
<track kind="captions">
</video>
// These values are bound to properties of the video
let time = 0;
let duration;
let paused = true;
let showControls = true;
let showControlsTimeout;
// Used to track time of last mouse down event
let lastMouseDown;
function handleMove(e) {
// Make the controls visible, but fade out after
// 2.5 seconds of inactivity
clearTimeout(showControlsTimeout);
showControlsTimeout = setTimeout(() => showControls = false, 2500);
showControls = true;
if (!duration) return; // video not loaded yet
if (e.type !== 'touchmove' && !(e.buttons & 1)) return; // mouse not down
const clientX = e.type === 'touchmove' ? e.touches[0].clientX : e.clientX;
const { left, right } = this.getBoundingClientRect();
time = duration * (clientX - left) / (right - left);
}
// we can't rely on the built-in click event, because it fires
// after a drag — we have to listen for clicks ourselves
function handleMousedown(e) {
lastMouseDown = new Date();
}
function handleMouseup(e) {
if (new Date() - lastMouseDown < 300) {
if (paused) e.target.play();
else e.target.pause();
}
}
function format(seconds) {
if (isNaN(seconds)) return '...';
const minutes = Math.floor(seconds / 60);
seconds = Math.floor(seconds % 60);
if (seconds < 10) seconds = '0' + seconds;
return `${minutes}:${seconds}`;
}
</script>
<h1>Caminandes: Llamigos</h1>
<p>From <a href="https://studio.blender.org/films">Blender Studio</a>. CC-BY</p>
<div>
<video
poster="https://sveltejs.github.io/assets/caminandes-llamigos.jpg"
src="https://sveltejs.github.io/assets/caminandes-llamigos.mp4"
on:mousemove={handleMove}
on:touchmove|preventDefault={handleMove}
on:mousedown={handleMousedown}
on:mouseup={handleMouseup}
bind:currentTime={time}
bind:duration
bind:paused>
<track kind="captions">
</video>
<div class="controls" style="opacity: {duration && showControls ? 1 : 0}">
<progress value="{(time / duration) || 0}"/>
<div class="info">
<span class="time">{format(time)}</span>
<span>click anywhere to {paused ? 'play' : 'pause'} / drag to seek</span>
<span class="time">{format(duration)}</span>
</div>
</div>
</div>
<style>
div {
position: relative;
}
.controls {
position: absolute;
top: 0;
width: 100%;
transition: opacity 1s;
}
.info {
display: flex;
width: 100%;
justify-content: space-between;
}
span {
padding: 0.2em 0.5em;
color: white;
text-shadow: 0 0 8px black;
font-size: 1.4em;
opacity: 0.7;
}
.time {
width: 3em;
}
.time:last-child { text-align: right }
progress {
display: block;
width: 100%;
height: 10px;
-webkit-appearance: none;
appearance: none;
}
progress::-webkit-progress-bar {
background-color: rgba(0,0,0,0.2);
}
progress::-webkit-progress-value {
background-color: rgba(255,255,255,0.6);
}
video {
width: 100%;
}
</style>
비디오를 클릭하게 되면 time, duration, paused가 업데이트 됩니다. 이처럼 사용자가 커스텀하여 컨트롤 할 수 있게 됩니다.
duration
: 비디오의 총 재생 시간(초)
buffered
: {start, end}의 객체 배열
seekable
: {start, end}의 객체 배열
played
: {start, end}의 객체 배열
seeking
: Boolean
ended
: Boolean
<video>
는 videoWidth
와 videoHeight
가 추가적으로 있습니다.
currentTime
: 비디오의 현재 지점(초)playbackRate1
: 비디오 재생 속도 // 1 = 보통 속도paused
: 이것은 자명해야 합니다.volume
: 0과 1 사이의 값muted
: boolean 값 // true : 음소거모든 block-level 요소에는 clientWidth
, clientHeight
, offsetWidth
, offsetHeight
바인딩을 가지고 있습니다.
이 바인딩들은 읽기 전용으로 w
, h
를 바꾸는 것 이외에는 어떤 효과도 없습니다.
<div bind:clientWidth={w} bind:clientHeight={h}>
<span style="font-size: {size}px">{text}</span>
</div>
div의 크기를 받아온 값으로 변경해줍니다.
💡 inline 요소와
<canvas>
에서는 사용이 불가능합니다.
<canvas
bind:this={canvas}
width={32}
height={32}
></canvas>
https://svelte.dev/tutorial/bind-thisDOM 요소의 속성(<div>
,<button>
등) 에 바인딩 할 수 있는 것처럼 컴포넌트에도 바인딩 할 수 있습니다. 예를 들어 keypad 컴포넌트의 props인 value를 form 요소를 통해 바인딩할 수 있습니다.
<Keypad bind:value={pin} on:submit={handleSubmit}/>
import Keypad from './Keypad.svelte';
let pin;
$: view = pin ? pin.replace(/\d(?!$)/g, '•') : 'enter your pin';
function handleSubmit() {
alert(`submitted ${pin}`);
}
</script>
<h1 style="color: {pin ? '#333' : '#ccc'}">{view}</h1>
<Keypad bind:value={pin} on:submit={handleSubmit}/>
import { createEventDispatcher } from 'svelte';
export let value = '';
const dispatch = createEventDispatcher();
const select = num => () => value += num;
const clear = () => value = '';
const submit = () => dispatch('submit');
</script>
<div class="keypad">
<button on:click={select(1)}>1</button>
<button on:click={select(2)}>2</button>
<button on:click={select(3)}>3</button>
<button on:click={select(4)}>4</button>
<button on:click={select(5)}>5</button>
<button on:click={select(6)}>6</button>
<button on:click={select(7)}>7</button>
<button on:click={select(8)}>8</button>
<button on:click={select(9)}>9</button>
<button disabled={!value} on:click={clear}>clear</button>
<button on:click={select(0)}>0</button>
<button disabled={!value} on:click={submit}>submit</button>
</div>
<style>
.keypad {
display: grid;
grid-template-columns: repeat(3, 5em);
grid-template-rows: repeat(4, 3em);
grid-gap: 0.5em
}
button {
margin: 0
}
</style>
import InputField from './InputField.svelte';
let field;
</script>
<InputField bind:this={field}/>
<button on:click={() => field.focus()}>
Focus field
</button>
let input;
export function focus() {
input.focus();
}
</script>
<input bind:this={input} />