@ -1,122 +0,0 @@
|
||||
---
|
||||
title: Behaviours
|
||||
---
|
||||
|
||||
As well as scoped styles and a template, components can encapsulate *behaviours*. For that, we add a `<script>` element:
|
||||
|
||||
```html
|
||||
<!-- { title: 'Behaviours' } -->
|
||||
<script>
|
||||
// behaviours go here
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<!-- template goes here -->
|
||||
</div>
|
||||
```
|
||||
|
||||
|
||||
### Internal state
|
||||
|
||||
Often, it makes sense for a component to have internal state that isn't visible to the outside world.
|
||||
|
||||
```html
|
||||
<!-- { title: 'Internal state' } -->
|
||||
<script>
|
||||
let count = 0;
|
||||
</script>
|
||||
|
||||
<p>Count: {count}</p>
|
||||
<button on:click="{() => count += 1}">+1</button>
|
||||
```
|
||||
|
||||
|
||||
### External properties
|
||||
|
||||
On the other hand, for the component to form part of a system, it needs to expose certain values so that they can be set from outside. These are called *props*, and we use the `export` keyword to differentiate them from internal state:
|
||||
|
||||
```html
|
||||
<!-- { title: 'External properties' } -->
|
||||
<script>
|
||||
export let count = 0;
|
||||
</script>
|
||||
|
||||
<p>Count: {count}</p>
|
||||
<button on:click="{() => count += 1}">+1</button>
|
||||
```
|
||||
|
||||
> Effectively, we're exporting a *contract* with the outside world. The `export` keyword normally means something different in JavaScript, so you might be surprised to see it used like this. Just roll with it for now!
|
||||
|
||||
The `= 0` sets a default value for `count`, if none is provided.
|
||||
|
||||
```js
|
||||
const counter = new Counter({
|
||||
target: document.body,
|
||||
props: {
|
||||
count: 99
|
||||
}
|
||||
});
|
||||
|
||||
counter.count; // 99
|
||||
counter.count += 1; // 100
|
||||
```
|
||||
|
||||
Props declared with `const` or `function` are *read-only* — they cannot be set from outside. This allows you to, for example, attach custom methods to your component:
|
||||
|
||||
```js
|
||||
component.doSomethingFun();
|
||||
```
|
||||
|
||||
|
||||
### Lifecycle hooks
|
||||
|
||||
There are four 'hooks' provided by Svelte for adding control logic — `onMount`, `beforeUpdate`, `afterUpdate` and `onDestroy`. Import them directly from `svelte`:
|
||||
|
||||
```html
|
||||
<!-- { title: 'Lifecycle hooks' } -->
|
||||
<script>
|
||||
import { onMount, beforeUpdate, afterUpdate, onDestroy } from 'svelte';
|
||||
|
||||
beforeUpdate(() => {
|
||||
// this function is called immediately before
|
||||
// the component updates to reflect new data
|
||||
console.log(`beforeUpdate`);
|
||||
});
|
||||
|
||||
afterUpdate(() => {
|
||||
// this function is called immediately *after*
|
||||
// the component updates to reflect new data.
|
||||
// if you need to do anything that assumes the
|
||||
// DOM is up-to-date — such as measuring the
|
||||
// size of an element — do it here
|
||||
console.log(`afterUpdate`);
|
||||
});
|
||||
|
||||
onMount(() => {
|
||||
// this function is called once, after the
|
||||
// `afterUpdate` function (if there is one)
|
||||
// runs for the first time
|
||||
console.log(`onMount`);
|
||||
|
||||
return () => {
|
||||
// this function runs when the
|
||||
// component is destroyed
|
||||
console.log(`onMount cleanup`);
|
||||
};
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
// this function runs when the
|
||||
// component is destroyed
|
||||
console.log(`onDestroy`);
|
||||
});
|
||||
|
||||
let count = 0;
|
||||
</script>
|
||||
|
||||
<button on:click="{() => count += 1}">
|
||||
Trigger an update ({count})
|
||||
</button>
|
||||
```
|
||||
|
||||
> Lifecycle hooks do *not* run in server-side rendering (SSR) mode, with the exception of `onDestroy`. More on SSR later.
|
@ -1,147 +0,0 @@
|
||||
---
|
||||
title: Nested components
|
||||
---
|
||||
|
||||
As well as containing elements (and `if` blocks and `each` blocks), Svelte components can contain *other* Svelte components.
|
||||
|
||||
```html
|
||||
<!-- { title: 'Nested components' } -->
|
||||
<script>
|
||||
import Widget from './Widget.html';
|
||||
</script>
|
||||
|
||||
<div class='widget-container'>
|
||||
<Widget answer={42}/>
|
||||
</div>
|
||||
```
|
||||
|
||||
```html
|
||||
<!--{ filename: 'Widget.html' }-->
|
||||
<p>I am a nested component. The answer is {answer}</p>
|
||||
```
|
||||
|
||||
That's similar to doing this...
|
||||
|
||||
```js
|
||||
import Widget from './Widget.html';
|
||||
|
||||
const widget = new Widget({
|
||||
target: document.querySelector('.widget-container'),
|
||||
props: {
|
||||
answer: 42
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
...except that Svelte takes care of destroying the child component when the parent is destroyed, and keeps props in sync if they change.
|
||||
|
||||
> Component names must be capitalised, following the widely-used JavaScript convention of capitalising constructor names. It's also an easy way to distinguish components from elements in your template.
|
||||
|
||||
|
||||
### Props
|
||||
|
||||
Props, short for 'properties', are the means by which you pass data down from a parent to a child component — in other words, they're just like attributes on an element. As with element attributes, prop values can contain any valid JavaScript expression.
|
||||
|
||||
Often, the name of the property will be the same as the value, in which case we can use a shorthand:
|
||||
|
||||
```html
|
||||
<!-- { repl: false } -->
|
||||
<!-- these are equivalent -->
|
||||
<Widget foo={foo}/>
|
||||
<Widget {foo}/>
|
||||
```
|
||||
|
||||
> Note that props are *one-way* — to get data from a child component into a parent component, use [bindings](docs#bindings).
|
||||
|
||||
|
||||
### Composing with `<slot>`
|
||||
|
||||
A component can contain a `<slot></slot>` element, which allows the parent component to inject content:
|
||||
|
||||
```html
|
||||
<!-- { title: 'Using <slot>' } -->
|
||||
<script>
|
||||
import Box from './Box.html';
|
||||
</script>
|
||||
|
||||
<Box>
|
||||
<h2>Hello!</h2>
|
||||
<p>This is a box. It can contain anything.</p>
|
||||
</Box>
|
||||
```
|
||||
|
||||
```html
|
||||
<!--{ filename: 'Box.html' }-->
|
||||
<style>
|
||||
.box {
|
||||
border: 2px solid black;
|
||||
padding: 0.5em;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="box">
|
||||
<slot><!-- content is injected here --></slot>
|
||||
</div>
|
||||
```
|
||||
|
||||
The `<slot>` element can contain 'fallback content', which will be used if no children are provided for the component:
|
||||
|
||||
```html
|
||||
<!-- { title: 'Default slot content' } -->
|
||||
<script>
|
||||
import Box from './Box.html';
|
||||
</script>
|
||||
|
||||
<Box></Box>
|
||||
```
|
||||
|
||||
```html
|
||||
<!--{ filename: 'Box.html' }-->
|
||||
<style>
|
||||
.box {
|
||||
border: 2px solid black;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
.fallback {
|
||||
color: #999;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="box">
|
||||
<slot>
|
||||
<p class="fallback">the box is empty!</p>
|
||||
</slot>
|
||||
</div>
|
||||
```
|
||||
|
||||
You can also have *named* slots. Any elements with a corresponding `slot` attribute will fill these slots:
|
||||
|
||||
```html
|
||||
<!-- { title: 'Named slots' } -->
|
||||
<script>
|
||||
import ContactCard from './ContactCard.html';
|
||||
</script>
|
||||
|
||||
<ContactCard>
|
||||
<span slot="name">P. Sherman</span>
|
||||
<span slot="address">42 Wallaby Way, Sydney</span>
|
||||
</ContactCard>
|
||||
```
|
||||
|
||||
```html
|
||||
<!--{ filename: 'ContactCard.html' }-->
|
||||
<style>
|
||||
.contact-card {
|
||||
border: 2px solid black;
|
||||
padding: 0.5em;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="contact-card">
|
||||
<h2><slot name="name"></slot></h2>
|
||||
<slot name="address">Unknown address</slot>
|
||||
<br>
|
||||
<slot name="email">Unknown email</slot>
|
||||
</div>
|
||||
```
|
@ -1,180 +0,0 @@
|
||||
---
|
||||
title: Events
|
||||
---
|
||||
|
||||
In most applications, you'll need to respond to the user's actions. In Svelte, this is done with the `on:[event]` directive.
|
||||
|
||||
### Element events
|
||||
|
||||
When used on an element, `on:click={handler}` is equivalent to calling `element.addEventListener('click', handler)`. When the element is removed, Svelte calls `removeEventListener` automatically.
|
||||
|
||||
```html
|
||||
<!-- { title: 'Inline event handlers' } -->
|
||||
<p>Count: {count}</p>
|
||||
<button on:click="{() => count += 1}">+1</button>
|
||||
```
|
||||
|
||||
```json
|
||||
/* { hidden: true } */
|
||||
{
|
||||
count: 0
|
||||
}
|
||||
```
|
||||
|
||||
For more complicated behaviours, you'll probably want to declare an event handler in your `<script>` block:
|
||||
|
||||
```html
|
||||
<!-- { title: 'Event handlers' } -->
|
||||
<script>
|
||||
let count = 0;
|
||||
|
||||
function incrementOrDecrement(event) {
|
||||
const d = event.shiftKey
|
||||
? -1
|
||||
: +1;
|
||||
|
||||
count += d;
|
||||
}
|
||||
</script>
|
||||
|
||||
<p>Count: {count}</p>
|
||||
<button on:click={incrementOrDecrement}>update</button>
|
||||
```
|
||||
|
||||
```json
|
||||
/* { hidden: true } */
|
||||
{
|
||||
count: 0
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### Event handler modifiers
|
||||
|
||||
While you can invoke methods like `event.stopPropagation` directly...
|
||||
|
||||
```html
|
||||
<!-- { repl: false } -->
|
||||
<div on:click="{e => e.stopPropagation()}">...</div>
|
||||
```
|
||||
|
||||
...it gets annoying if you want to combine that with some other behaviour:
|
||||
|
||||
```html
|
||||
<!-- { repl: false } -->
|
||||
<script>
|
||||
let foo = false;
|
||||
|
||||
function toggleFoo(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
foo = !foo;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div on:click={toggleFoo}>...</div>
|
||||
```
|
||||
|
||||
For that reason, Svelte lets you use *event modifiers*:
|
||||
|
||||
- [`preventDefault`](https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
|
||||
- [`stopPropagation`](https://developer.mozilla.org/en-US/docs/Web/API/Event/stopPropagation)
|
||||
- [`passive`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Parameters) — improves scrolling performance on touch/wheel events (Svelte will add it automatically where it's safe to do so)
|
||||
- [`once`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Parameters) — removes the listener after the first invocation
|
||||
- [`capture`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Parameter)
|
||||
|
||||
> `passive` and `once` are not implemented in `legacy` mode
|
||||
|
||||
The example above can be achieved with modifiers — no need for a separate event handler:
|
||||
|
||||
```html
|
||||
<!-- { repl: false } -->
|
||||
<div on:click|stopPropagation|preventDefault="{() => foo = !foo}">...</div>
|
||||
```
|
||||
|
||||
|
||||
### Component events
|
||||
|
||||
Events are an excellent way for [nested components](docs#nested-components) to communicate with their parents. Let's revisit our earlier example, but turn it into a `<CategoryChooser>` component:
|
||||
|
||||
```html
|
||||
<!-- { filename: 'CategoryChooser.html', repl: false } -->
|
||||
<p>Select a category:</p>
|
||||
|
||||
{#each categories as category}
|
||||
<button on:click="fire('select', { category })">select {category}</button>
|
||||
{/each}
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
categories: [
|
||||
'animal',
|
||||
'vegetable',
|
||||
'mineral'
|
||||
]
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
When the user clicks a button, the component will fire a `select` event, where the `event` object has a `category` property. Any component that nests `<CategoryChooser>` can listen for events like so:
|
||||
|
||||
```html
|
||||
<!--{ title: 'Component events' }-->
|
||||
<CategoryChooser on:select="playTwentyQuestions(event.category)"/>
|
||||
|
||||
<script>
|
||||
import CategoryChooser from './CategoryChooser.html';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
CategoryChooser
|
||||
},
|
||||
|
||||
methods: {
|
||||
playTwentyQuestions(category) {
|
||||
alert(`ok! you chose ${category}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
```html
|
||||
<!--{ filename: 'CategoryChooser.html', hidden: true }-->
|
||||
<p>Select a category:</p>
|
||||
|
||||
{#each categories as category}
|
||||
<button on:click="fire('select', { category })">select {category}</button>
|
||||
{/each}
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
categories: [
|
||||
'animal',
|
||||
'vegetable',
|
||||
'mineral'
|
||||
]
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
Just as `this` in an element's event handler refers to the element itself, in a component event handler `this` refers to the component firing the event.
|
||||
|
||||
There is also a shorthand for listening for and re-firing an event unchanged.
|
||||
|
||||
```html
|
||||
<!-- { repl: false } -->
|
||||
<!-- these are equivalent -->
|
||||
<Widget on:foo="fire('foo', event)"/>
|
||||
<Widget on:foo/>
|
||||
```
|
||||
|
||||
Since component events do not propagate as DOM events do, this can be used to pass events through intermediate components. This shorthand technique also applies to element events (`on:click` is equivalent to `on:click="fire('click', event)"`).
|
@ -1,161 +0,0 @@
|
||||
---
|
||||
title: Bindings
|
||||
---
|
||||
|
||||
|
||||
### Bindings
|
||||
|
||||
As we've seen, data can be passed down to elements and components with attributes and [props](docs#props). Occasionally, you need to get data back *up*; for that we use bindings.
|
||||
|
||||
|
||||
#### Component bindings
|
||||
|
||||
Component bindings keep values in sync between a parent and a child:
|
||||
|
||||
```html
|
||||
<!-- { repl: false } -->
|
||||
<Widget bind:childValue=parentValue/>
|
||||
```
|
||||
|
||||
Whenever `childValue` changes in the child component, `parentValue` will be updated in the parent component and vice versa.
|
||||
|
||||
If the names are the same, you can shorten the declaration:
|
||||
|
||||
```html
|
||||
<!-- { repl: false } -->
|
||||
<Widget bind:value/>
|
||||
```
|
||||
|
||||
> Use component bindings judiciously. They can save you a lot of boilerplate, but will make it harder to reason about data flow within your application if you overuse them.
|
||||
|
||||
|
||||
#### Element bindings
|
||||
|
||||
Element bindings make it easy to respond to user interactions:
|
||||
|
||||
```html
|
||||
<!-- { title: 'Element bindings' } -->
|
||||
<h1>Hello {name}!</h1>
|
||||
<input bind:value={name}>
|
||||
```
|
||||
|
||||
```json
|
||||
/* { hidden: true } */
|
||||
{
|
||||
name: 'world'
|
||||
}
|
||||
```
|
||||
|
||||
Some bindings are *one-way*, meaning that the values are read-only. Most are *two-way* — changing the data programmatically will update the DOM. The following bindings are available:
|
||||
|
||||
| Name | Applies to | Kind |
|
||||
|-----------------------------------------------------------------|----------------------------------------------|----------------------|
|
||||
| `value` | `<input>` `<textarea>` `<select>` | <span>Two-way</span> |
|
||||
| `checked` `indeterminate` | `<input type=checkbox>` | <span>Two-way</span> |
|
||||
| `group` (see note) | `<input type=checkbox>` `<input type=radio>` | <span>Two-way</span> |
|
||||
| `currentTime` `paused` `played` `volume` | `<audio>` `<video>` | <span>Two-way</span> |
|
||||
| `buffered` `duration` `seekable` | `<audio>` `<video>` | <span>One-way</span> |
|
||||
| `offsetWidth` `offsetHeight` `clientWidth` `clientHeight` | All block-level elements | <span>One-way</span> |
|
||||
| `scrollX` `scrollY` | `<svelte:window>` | <span>Two-way</span> |
|
||||
| `online` `innerWidth` `innerHeight` `outerWidth` `outerHeight` | `<svelte:window>` | <span>One-way</span> |
|
||||
|
||||
> 'group' bindings allow you to capture the current value of a [set of radio inputs](repl?demo=binding-input-radio), or all the selected values of a [set of checkbox inputs](repl?demo=binding-input-checkbox-group).
|
||||
|
||||
Here is a complete example of using two way bindings with a form:
|
||||
|
||||
```html
|
||||
<!-- { title: 'Form bindings' } -->
|
||||
<form on:submit="handleSubmit(event)">
|
||||
<input bind:value=name type=text>
|
||||
<button type=submit>Say hello</button>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
methods: {
|
||||
handleSubmit(event) {
|
||||
// prevent the page from reloading
|
||||
event.preventDefault();
|
||||
|
||||
const { name } = this.get();
|
||||
alert(`Hello ${name}!`);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
```json
|
||||
/* { hidden: true } */
|
||||
{
|
||||
name: "world"
|
||||
}
|
||||
```
|
||||
|
||||
> 'two way' bindings allow you to update a value in a nested property as seen in [this checkbox input example](repl?demo=binding-input-checkbox).
|
||||
|
||||
|
||||
### bind:this
|
||||
|
||||
There's a special binding that exists on all elements and components — `this`. It allows you to store a reference to a DOM node or component instance so that you can interact with it programmatically:
|
||||
|
||||
```html
|
||||
<!-- { title: 'Refs' } -->
|
||||
<canvas bind:this={canvas} width={200} height={200}></canvas>
|
||||
|
||||
<script>
|
||||
import { onMount } from 'svelte';
|
||||
import createRenderer from './createRenderer.js';
|
||||
|
||||
let canvas;
|
||||
|
||||
onMount(() => {
|
||||
const ctx = canvas.getContext('2d');
|
||||
const renderer = createRenderer(canvas, ctx);
|
||||
|
||||
// stop updating the canvas when
|
||||
// the component is destroyed
|
||||
return renderer.stop;
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
```js
|
||||
/* { filename: 'createRenderer.js', hidden: true } */
|
||||
export default function createRenderer(canvas, ctx) {
|
||||
let running = true;
|
||||
loop();
|
||||
|
||||
return {
|
||||
stop: () => {
|
||||
running = false;
|
||||
}
|
||||
};
|
||||
|
||||
function loop() {
|
||||
if (!running) return;
|
||||
requestAnimationFrame(loop);
|
||||
|
||||
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
||||
|
||||
for (let p = 0; p < imageData.data.length; p += 4) {
|
||||
const i = p / 4;
|
||||
const x = i % canvas.width;
|
||||
const y = i / canvas.height >>> 0;
|
||||
|
||||
const t = window.performance.now();
|
||||
|
||||
const r = 64 + (128 * x / canvas.width) + (64 * Math.sin(t / 1000));
|
||||
const g = 64 + (128 * y / canvas.height) + (64 * Math.cos(t / 1000));
|
||||
const b = 128;
|
||||
|
||||
imageData.data[p + 0] = r;
|
||||
imageData.data[p + 1] = g;
|
||||
imageData.data[p + 2] = b;
|
||||
imageData.data[p + 3] = 255;
|
||||
}
|
||||
|
||||
ctx.putImageData(imageData, 0, 0);
|
||||
}
|
||||
}
|
||||
```
|
@ -1,110 +0,0 @@
|
||||
---
|
||||
title: Transitions
|
||||
---
|
||||
|
||||
|
||||
Transitions allow elements to enter and leave the DOM gracefully, rather than suddenly appearing and disappearing.
|
||||
|
||||
```html
|
||||
<!-- { title: 'Transitions' } -->
|
||||
<script>
|
||||
import { fade } from 'svelte/transition';
|
||||
let visible = false;
|
||||
</script>
|
||||
|
||||
<input type=checkbox bind:checked={visible}> visible
|
||||
|
||||
{#if visible}
|
||||
<p transition:fade>fades in and out</p>
|
||||
{/if}
|
||||
```
|
||||
|
||||
Transitions can have parameters — typically `delay` and `duration`, but often others, depending on the transition in question. For example, here's the `fly` transition:
|
||||
|
||||
```html
|
||||
<!-- { title: 'Transition with parameters' } -->
|
||||
<script>
|
||||
import { fly } from 'svelte/transition';
|
||||
let visible = false;
|
||||
</script>
|
||||
|
||||
<input type=checkbox bind:checked={visible}> visible
|
||||
|
||||
{#if visible}
|
||||
<p transition:fly="{{y: 200, duration: 1000}}">flies 200 pixels up, slowly</p>
|
||||
{/if}
|
||||
```
|
||||
|
||||
|
||||
### In and out
|
||||
|
||||
An element can have separate `in` and `out` transitions:
|
||||
|
||||
```html
|
||||
<!-- { title: 'Transition in/out' } -->
|
||||
<script>
|
||||
import { fade, fly } from 'svelte-transitions';
|
||||
let visible = false;
|
||||
</script>
|
||||
|
||||
<input type=checkbox bind:checked={visible}> visible
|
||||
|
||||
{#if visible}
|
||||
<p in:fly="{y: 50}" out:fade>flies up, fades out</p>
|
||||
{/if}
|
||||
```
|
||||
|
||||
|
||||
### Built-in transitions
|
||||
|
||||
Svelte comes with a handful of ready-to-use transitions:
|
||||
|
||||
```html
|
||||
<!-- { repl: false } -->
|
||||
<script>
|
||||
import {
|
||||
fade,
|
||||
fly,
|
||||
slide,
|
||||
draw
|
||||
} from 'svelte/transition';
|
||||
</script>
|
||||
```
|
||||
|
||||
|
||||
### Custom transitions
|
||||
|
||||
You can also make your own. Transitions are simple functions that take a `node` and any provided `parameters` and return an object with the following properties:
|
||||
|
||||
* `duration` — how long the transition takes in milliseconds
|
||||
* `delay` — milliseconds before the transition starts
|
||||
* `easing` — an [easing function](https://github.com/rollup/eases-jsnext)
|
||||
* `css` — a function that accepts an argument `t` between 0 and 1 and returns the styles that should be applied at that moment
|
||||
* `tick` — a function that will be called on every frame, with the same `t` argument, while the transition is in progress
|
||||
|
||||
Of these, `duration` is required, as is *either* `css` or `tick`. The rest are optional. Here's how the `fade` transition is implemented, for example:
|
||||
|
||||
```html
|
||||
<!-- { title: 'Fade transition' } -->
|
||||
<script>
|
||||
function fade(node, { delay = 0, duration = 400 }) {
|
||||
const o = +getComputedStyle(node).opacity;
|
||||
|
||||
return {
|
||||
delay,
|
||||
duration,
|
||||
css: t => `opacity: ${t * o}`
|
||||
};
|
||||
}
|
||||
|
||||
let visible = false;
|
||||
</script>
|
||||
|
||||
<input type=checkbox bind:checked={visible}> visible
|
||||
|
||||
{#if visible}
|
||||
<p transition:fade>fades in and out</p>
|
||||
{/if}
|
||||
```
|
||||
|
||||
> If the `css` option is used, Svelte will create a CSS animation that runs efficiently off the main thread. Therefore if you can achieve an effect using `css` rather than `tick`, you should.
|
@ -1,103 +0,0 @@
|
||||
---
|
||||
title: Actions
|
||||
---
|
||||
|
||||
|
||||
### Actions
|
||||
|
||||
Actions let you decorate elements with additional functionality. Actions are functions which may return an object with lifecycle methods, `update` and `destroy`. The action will be called when its element is added to the DOM.
|
||||
|
||||
Use actions for things like:
|
||||
|
||||
* tooltips
|
||||
* lazy loading images as the page is scrolled, e.g. `<img use:lazyload data-src='giant-photo.jpg'/>`
|
||||
* capturing link clicks for your client router
|
||||
* adding drag and drop
|
||||
|
||||
```html
|
||||
<!-- { title: 'Actions' } -->
|
||||
<button on:click={toggleLanguage} use:tooltip={translations[language].tooltip}>
|
||||
{language}
|
||||
</button>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
actions: {
|
||||
tooltip(node, text) {
|
||||
const tooltip = document.createElement('div');
|
||||
tooltip.textContent = text;
|
||||
|
||||
Object.assign(tooltip.style, {
|
||||
position: 'absolute',
|
||||
background: 'black',
|
||||
color: 'white',
|
||||
padding: '0.5em 1em',
|
||||
fontSize: '12px',
|
||||
pointerEvents: 'none',
|
||||
transform: 'translate(5px, -50%)',
|
||||
borderRadius: '2px',
|
||||
transition: 'opacity 0.4s'
|
||||
});
|
||||
|
||||
function position() {
|
||||
const { top, right, bottom } = node.getBoundingClientRect();
|
||||
tooltip.style.top = `${(top + bottom) / 2}px`;
|
||||
tooltip.style.left = `${right}px`;
|
||||
}
|
||||
|
||||
function append() {
|
||||
document.body.appendChild(tooltip);
|
||||
tooltip.style.opacity = 0;
|
||||
setTimeout(() => tooltip.style.opacity = 1);
|
||||
position();
|
||||
}
|
||||
|
||||
function remove() {
|
||||
tooltip.remove();
|
||||
}
|
||||
|
||||
node.addEventListener('mouseenter', append);
|
||||
node.addEventListener('mouseleave', remove);
|
||||
|
||||
return {
|
||||
update(text) {
|
||||
tooltip.textContent = text;
|
||||
position();
|
||||
},
|
||||
|
||||
destroy() {
|
||||
tooltip.remove();
|
||||
node.removeEventListener('mouseenter', append);
|
||||
node.removeEventListener('mouseleave', remove);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
toggleLanguage() {
|
||||
const { language } = this.get();
|
||||
|
||||
this.set({
|
||||
language: language === 'english' ? 'latin' : 'english'
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
```json
|
||||
/* { hidden: true } */
|
||||
{
|
||||
language: "english",
|
||||
translations: {
|
||||
english: {
|
||||
tooltip: "Switch Languages",
|
||||
},
|
||||
latin: {
|
||||
tooltip: "Itchsway Anguageslay",
|
||||
},
|
||||
}
|
||||
}
|
||||
```
|
@ -1,70 +0,0 @@
|
||||
---
|
||||
title: Classes
|
||||
---
|
||||
|
||||
Like any attribute, the `class` attribute can be set using regular JavaScript. Suppose we had an `active` class that we wanted to apply to an element when `isActive` is true — we could do it like this:
|
||||
|
||||
```html
|
||||
<!-- { title: 'Dynamic classes using ternaries' } -->
|
||||
<script>
|
||||
let isActive = false;
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.active {
|
||||
color: red;
|
||||
}
|
||||
</style>
|
||||
|
||||
<h1 class="{isActive ? 'active' : ''}">red if active</h1>
|
||||
|
||||
<label>
|
||||
<input type=checkbox bind:checked={isActive}> isActive
|
||||
</label>
|
||||
```
|
||||
|
||||
That's a little verbose though, so the `class:` directive gives you a simpler way to achieve the same thing:
|
||||
|
||||
```html
|
||||
<!-- { title: 'Dynamic classes using directives' } -->
|
||||
<script>
|
||||
let isActive = false;
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.active {
|
||||
color: red;
|
||||
}
|
||||
</style>
|
||||
|
||||
-<h1 class="{isActive ? 'active' : ''}">red if active</h1>
|
||||
+<h1 class:active={isActive}>red if active</h1>
|
||||
|
||||
<label>
|
||||
<input type=checkbox bind:checked={isActive}> isActive
|
||||
</label>
|
||||
```
|
||||
|
||||
As with any directive, you can use any JavaScript expression. If it's a variable name that matches the class name, you can use a shorthand:
|
||||
|
||||
```html
|
||||
<!-- { title: 'Dynamic classes using directives' } -->
|
||||
<script>
|
||||
- let isActive = false;
|
||||
+ let active = false;
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.active {
|
||||
color: red;
|
||||
}
|
||||
</style>
|
||||
|
||||
-<h1 class:active={isActive}>red if active</h1>
|
||||
+<h1 class:active>red if active</h1>
|
||||
|
||||
<label>
|
||||
- <input type=checkbox bind:checked={isActive}> isActive
|
||||
+ <input type=checkbox bind:checked={active}> active
|
||||
</label>
|
||||
```
|
@ -1,67 +0,0 @@
|
||||
---
|
||||
title: Module context
|
||||
---
|
||||
|
||||
So far, our `<script>` tags have been running in the context of a component *instance*. In other words, if you have two components like this...
|
||||
|
||||
```html
|
||||
<!-- { title: 'Counter' } -->
|
||||
<script>
|
||||
import Counter from './Counter.html';
|
||||
</script>
|
||||
|
||||
<Counter/>
|
||||
<Counter/>
|
||||
```
|
||||
|
||||
```html
|
||||
<!--{ filename: 'Counter.html' }-->
|
||||
<script>
|
||||
let count = 0;
|
||||
</script>
|
||||
|
||||
<button on:click="{() => count += 1}">+1</button>
|
||||
```
|
||||
|
||||
...each counter has its own `count` variable. The code runs once per instance.
|
||||
|
||||
Occasionally, you want code to run once *per module* instead. For that, we use `context="module"`:
|
||||
|
||||
```html
|
||||
<!-- { title: 'Module context' } -->
|
||||
<script context="module">
|
||||
console.log(`this will run once`);
|
||||
const answer = 42;
|
||||
</script>
|
||||
|
||||
<script>
|
||||
console.log(`this will run once per instance`);
|
||||
console.log(`we can 'see' module-level variables like ${answer}`);
|
||||
</script>
|
||||
```
|
||||
|
||||
> Don't worry about manually hoisting functions from instance context to module context to avoid creating multiple copies of them — Svelte will do that for you
|
||||
|
||||
|
||||
### Module exports
|
||||
|
||||
Any named exports from a `context="module"` script become part of the module's static exports. For example, to define a `preload` function for use with [Sapper](https://sapper.svelte.technology):
|
||||
|
||||
```html
|
||||
<!-- { title: 'Module exports', repl: false } -->
|
||||
<script context="module">
|
||||
export async function preload({ params }) {
|
||||
const res = await this.fetch(`/blog/${params.slug}.json`);
|
||||
|
||||
return {
|
||||
post: await res.json()
|
||||
};
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
```js
|
||||
import BlogPost, { preload } from './BlogPost.html';
|
||||
```
|
||||
|
||||
You can only have named exports — no `export default` — because the component *is* the default export.
|
@ -1,304 +0,0 @@
|
||||
---
|
||||
title: State management
|
||||
---
|
||||
|
||||
Svelte components have built-in state management via the `get` and `set` methods. But as your application grows beyond a certain size, you may find that passing data between components becomes laborious.
|
||||
|
||||
For example, you might have an `<Options>` component inside a `<Sidebar>` component that allows the user to control the behaviour of a `<MainView>` component. You could use bindings or events to 'send' information up from `<Options>` through `<Sidebar>` to a common ancestor — say `<App>` — which would then have the responsibility of sending it back down to `<MainView>`. But that's cumbersome, especially if you decide you want to break `<MainView>` up into a set of smaller components.
|
||||
|
||||
Instead, a popular solution to this problem is to use a *global store* of data that cuts across your component hierarchy. React has [Redux](https://redux.js.org/) and [MobX](https://mobx.js.org/index.html) (though these libraries can be used anywhere, including with Svelte), and Vue has [Vuex](https://vuex.vuejs.org/en/).
|
||||
|
||||
Svelte has `Store`. `Store` can be used in any JavaScript app, but it's particularly well-suited to Svelte apps.
|
||||
|
||||
|
||||
### The basics
|
||||
|
||||
Import `Store` from `svelte/store.js` (remember to include the curly braces, as it's a *named import*), then create a new store with some (optional) data:
|
||||
|
||||
```js
|
||||
import { Store } from 'svelte/store.js';
|
||||
|
||||
const store = new Store({
|
||||
name: 'world'
|
||||
});
|
||||
```
|
||||
|
||||
Each instance of `Store` has `get`, `set`, `on` and `fire` methods that behave identically to their counterparts on a Svelte component:
|
||||
|
||||
```js
|
||||
const { name } = store.get(); // 'world'
|
||||
|
||||
store.on('state', ({ current, changed, previous }) => {
|
||||
console.log(`hello ${current.name}`);
|
||||
});
|
||||
|
||||
store.set({ name: 'everybody' }); // 'hello everybody'
|
||||
```
|
||||
|
||||
|
||||
|
||||
### Creating components with stores
|
||||
|
||||
Let's adapt our [very first example](docs#understanding-svelte-components):
|
||||
|
||||
```html
|
||||
<!-- { repl: false } -->
|
||||
<h1>Hello {$name}!</h1>
|
||||
<Greeting/>
|
||||
|
||||
<script>
|
||||
import Greeting from './Greeting.html';
|
||||
|
||||
export default {
|
||||
components: { Greeting }
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
```html
|
||||
<!--{ filename: 'Greeting.html' }-->
|
||||
<p>It's nice to see you, {$name}</p>
|
||||
```
|
||||
|
||||
```js
|
||||
/* { filename: 'main.js' } */
|
||||
import App from './App.html';
|
||||
import { Store } from 'svelte/store.js';
|
||||
|
||||
const store = new Store({
|
||||
name: 'world'
|
||||
});
|
||||
|
||||
const app = new App({
|
||||
target: document.querySelector('main'),
|
||||
store
|
||||
});
|
||||
|
||||
window.store = store; // useful for debugging!
|
||||
```
|
||||
|
||||
There are three important things to notice:
|
||||
|
||||
* We're passing `store` to `new App(...)` instead of `data`
|
||||
* The template refers to `$name` instead of `name`. The `$` prefix tells Svelte that `name` is a *store property*
|
||||
* Because `<Greeting>` is a child of `<App>`, it also has access to the store. Without it, `<App>` would have to pass the `name` property down as a component property (`<Greeting name={name}/>`)
|
||||
|
||||
Components that depend on store properties will re-render whenever they change.
|
||||
|
||||
|
||||
### Declarative stores
|
||||
|
||||
As an alternative to adding the `store` option when instantiating, the component itself can declare a dependency on a store:
|
||||
|
||||
```html
|
||||
<!-- { title: 'Declarative stores' } -->
|
||||
<h1>Hello {$name}!</h1>
|
||||
<Greeting/>
|
||||
|
||||
<script>
|
||||
import Greeting from './Greeting.html';
|
||||
import store from './store.js';
|
||||
|
||||
export default {
|
||||
store: () => store,
|
||||
components: { Greeting }
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
```html
|
||||
<!--{ filename: 'Greeting.html' }-->
|
||||
<p>It's nice to see you, {$name}</p>
|
||||
```
|
||||
|
||||
```js
|
||||
/* { filename: 'store.js' } */
|
||||
import { Store } from 'svelte/store.js';
|
||||
export default new Store({ name: 'world' });
|
||||
```
|
||||
|
||||
Note that the `store` option is a function that *returns* a store, rather than the store itself — this provides greater flexibility.
|
||||
|
||||
|
||||
### Computed store properties
|
||||
|
||||
Just like components, stores can have computed properties:
|
||||
|
||||
```js
|
||||
store = new Store({
|
||||
width: 10,
|
||||
height: 10,
|
||||
depth: 10,
|
||||
density: 3
|
||||
});
|
||||
|
||||
store.compute(
|
||||
'volume',
|
||||
['width', 'height', 'depth'],
|
||||
(width, height, depth) => width * height * depth
|
||||
);
|
||||
|
||||
store.get().volume; // 1000
|
||||
|
||||
store.set({ width: 20 });
|
||||
store.get().volume; // 2000
|
||||
|
||||
store.compute(
|
||||
'mass',
|
||||
['volume', 'density'],
|
||||
(volume, density) => volume * density
|
||||
);
|
||||
|
||||
store.get().mass; // 6000
|
||||
```
|
||||
|
||||
The first argument is the name of the computed property. The second is an array of *dependencies* — these can be data properties or other computed properties. The third argument is a function that recomputes the value whenever the dependencies change.
|
||||
|
||||
A component that was connected to this store could reference `{$volume}` and `{$mass}`, just like any other store property.
|
||||
|
||||
|
||||
### Accessing the store inside components
|
||||
|
||||
Each component gets a reference to `this.store`. This allows you to attach behaviours in `oncreate`...
|
||||
|
||||
```html
|
||||
<!-- { repl: false } -->
|
||||
<script>
|
||||
export default {
|
||||
oncreate() {
|
||||
const listener = this.store.on('state', ({ current }) => {
|
||||
// ...
|
||||
});
|
||||
|
||||
// listeners are not automatically removed — cancel
|
||||
// them to prevent memory leaks
|
||||
this.on('destroy', listener.cancel);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
...or call store methods in your event handlers, using the same `$` prefix as data properties:
|
||||
|
||||
```html
|
||||
<!-- { repl: false } -->
|
||||
<button on:click="$set({ muted: true })">
|
||||
Mute audio
|
||||
</button>
|
||||
```
|
||||
|
||||
|
||||
### Custom store methods
|
||||
|
||||
`Store` doesn't have a concept of *actions* or *commits*, like Redux and Vuex. Instead, state is always updated with `store.set(...)`.
|
||||
|
||||
You can implement custom logic by subclassing `Store`:
|
||||
|
||||
```js
|
||||
class TodoStore extends Store {
|
||||
addTodo(description) {
|
||||
const todo = {
|
||||
id: generateUniqueId(),
|
||||
done: false,
|
||||
description
|
||||
};
|
||||
|
||||
const todos = this.get().todos.concat(todo);
|
||||
this.set({ todos });
|
||||
}
|
||||
|
||||
toggleTodo(id) {
|
||||
const todos = this.get().todos.map(todo => {
|
||||
if (todo.id === id) {
|
||||
return {
|
||||
id,
|
||||
done: !todo.done,
|
||||
description: todo.description
|
||||
};
|
||||
}
|
||||
|
||||
return todo;
|
||||
});
|
||||
|
||||
this.set({ todos });
|
||||
}
|
||||
}
|
||||
|
||||
const store = new TodoStore({
|
||||
todos: []
|
||||
});
|
||||
|
||||
store.addTodo('Finish writing this documentation');
|
||||
```
|
||||
|
||||
Methods can update the store asynchronously:
|
||||
|
||||
```js
|
||||
class NasdaqTracker extends Store {
|
||||
async fetchStockPrices(ticker) {
|
||||
const token = this.token = {};
|
||||
const prices = await fetch(`/api/prices/${ticker}`).then(r => r.json());
|
||||
if (token !== this.token) return; // invalidated by subsequent request
|
||||
|
||||
this.set({ prices });
|
||||
}
|
||||
}
|
||||
|
||||
const store = new NasdaqTracker();
|
||||
store.fetchStockPrices('AMZN');
|
||||
```
|
||||
|
||||
You can call these methods in your components, just like the built-in methods:
|
||||
|
||||
|
||||
```html
|
||||
<!-- { repl: false } -->
|
||||
<input
|
||||
placeholder="Enter a stock ticker"
|
||||
on:change="$fetchStockPrices(this.value)"
|
||||
>
|
||||
```
|
||||
|
||||
### Store bindings
|
||||
|
||||
You can bind to store values just like you bind to component values — just add the `$` prefix:
|
||||
|
||||
```html
|
||||
<!-- { repl: false } -->
|
||||
<!-- global audio volume control -->
|
||||
<input bind:value=$volume type=range min=0 max=1 step=0.01>
|
||||
```
|
||||
|
||||
|
||||
### Using store properties in computed properties
|
||||
|
||||
Just as in templates, you can access store properties in component computed properties by prefixing them with `$`:
|
||||
|
||||
```html
|
||||
<!-- { repl: false } -->
|
||||
{#if isVisible}
|
||||
<div class="todo {todo.done ? 'done': ''}">
|
||||
{todo.description}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<script>
|
||||
export default {
|
||||
computed: {
|
||||
// `todo` is a component property, `$filter` is
|
||||
// a store property
|
||||
isVisible: ({ todo, $filter }) => {
|
||||
if ($filter === 'all') return true;
|
||||
if ($filter === 'done') return todo.done;
|
||||
if ($filter === 'pending') return !todo.done;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
|
||||
### Built-in optimisations
|
||||
|
||||
The Svelte compiler knows which store properties your components are interested in (because of the `$` prefix), and writes code that only listens for changes to those properties. Because of that, you needn't worry about having many properties on your store, even ones that update frequently — components that don't use them will be unaffected.
|
||||
|
@ -1,61 +0,0 @@
|
||||
---
|
||||
title: Preprocessing
|
||||
---
|
||||
|
||||
Some developers like to use non-standard languages such as [Pug](https://pugjs.org/api/getting-started.html), [Sass](http://sass-lang.com/) or [CoffeeScript](http://coffeescript.org/).
|
||||
|
||||
It's possible to use these languages, or anything else that can be converted to HTML, CSS and JavaScript, using *preprocessors*.
|
||||
|
||||
|
||||
### svelte.preprocess
|
||||
|
||||
Svelte exports a `preprocess` function that takes some input source code and returns a Promise for a standard Svelte component, ready to be used with `svelte.compile`:
|
||||
|
||||
```js
|
||||
const svelte = require('svelte');
|
||||
|
||||
const input = fs.readFileSync('App.html', 'utf-8');
|
||||
|
||||
svelte.preprocess(input, {
|
||||
filename: 'App.html', // this is passed to each preprocessor
|
||||
|
||||
markup: ({ content, filename }) => {
|
||||
return {
|
||||
code: '<!-- some HTML -->',
|
||||
map: {...}
|
||||
};
|
||||
},
|
||||
|
||||
style: ({ content, attributes, filename }) => {
|
||||
return {
|
||||
code: '/* some CSS */',
|
||||
map: {...}
|
||||
};
|
||||
},
|
||||
|
||||
script: ({ content, attributes, filename }) => {
|
||||
return {
|
||||
code: '// some JavaScript',
|
||||
map: {...}
|
||||
};
|
||||
}
|
||||
}).then(preprocessed => {
|
||||
fs.writeFileSync('preprocessed/App.html', preprocessed.toString());
|
||||
|
||||
const { js } = svelte.compile(preprocessed);
|
||||
fs.writeFileSync('compiled/App.js', js.code);
|
||||
});
|
||||
```
|
||||
|
||||
The `markup` preprocessor, if specified, runs first. The `content` property represents the entire input string.
|
||||
|
||||
The `style` and `script` preprocessors receive the contents of the `<style>` and `<script>` elements respectively, along with any `attributes` on those elements (e.g. `<style lang='scss'>`).
|
||||
|
||||
All three preprocessors are optional. Each should return a `{ code, map }` object or a Promise that resolves to a `{ code, map }` object, where `code` is the resulting string and `map` is a sourcemap representing the transformation.
|
||||
|
||||
> The returned `map` objects are not currently used by Svelte, but will be in future versions
|
||||
|
||||
|
||||
### Using build tools
|
||||
|
||||
Many build tool plugins, such as [rollup-plugin-svelte](https://github.com/rollup/rollup-plugin-svelte) and [svelte-loader](https://github.com/sveltejs/svelte-loader), allow you to specify `preprocess` options, in which case the build tool will do the grunt work.
|
@ -1,109 +0,0 @@
|
||||
---
|
||||
title: Custom elements
|
||||
---
|
||||
|
||||
[Custom elements](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Custom_Elements) are an emerging web standard for creating DOM elements that encapsulate styles and behaviours, much like Svelte components. They are part of the [web components](https://developer.mozilla.org/en-US/docs/Web/Web_Components) family of specifications.
|
||||
|
||||
> Most browsers need [polyfills](https://www.webcomponents.org/polyfills) for custom elements. See [caniuse](https://caniuse.com/#feat=custom-elementsv1) for more details
|
||||
|
||||
Svelte components can be used as custom elements by doing the following:
|
||||
|
||||
1. Declaring a `tag` name. The value must contain a hyphen (`hello-world` in the example below)
|
||||
2. Specifying `customElement: true` in the compiler configuration
|
||||
|
||||
```html
|
||||
<!-- { filename: 'HelloWorld.html', repl: false } -->
|
||||
<h1>Hello {name}!</h1>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
tag: 'hello-world'
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
Importing this file will now register a globally-available `<hello-world>` custom element that accepts a `name` property:
|
||||
|
||||
```js
|
||||
import './HelloWorld.html';
|
||||
document.body.innerHTML = `<hello-world name="world"/>`;
|
||||
|
||||
const el = document.querySelector('hello-world');
|
||||
el.name = 'everybody';
|
||||
```
|
||||
|
||||
See [svelte-custom-elements.surge.sh](http://svelte-custom-elements.surge.sh/) ([source here](https://github.com/sveltejs/template-custom-element)) for a larger example.
|
||||
|
||||
The compiled custom elements are still full-fledged Svelte components and can be used as such:
|
||||
|
||||
```js
|
||||
el.get().name === el.name; // true
|
||||
el.set({ name: 'folks' }); // equivalent to el.name = 'folks'
|
||||
```
|
||||
|
||||
One crucial difference is that styles are *fully encapsulated* — whereas Svelte will prevent component styles from leaking *out*, custom elements use [shadow DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Shadow_DOM) which also prevents styles from leaking *in*.
|
||||
|
||||
### Using `<slot>`
|
||||
|
||||
Custom elements can use [slots](docs#composing-with-slot) to place child elements, just like regular Svelte components.
|
||||
|
||||
### Firing events
|
||||
|
||||
You can dispatch events inside custom elements to pass data out:
|
||||
|
||||
```js
|
||||
// inside a component method
|
||||
const event = new CustomEvent('message', {
|
||||
detail: 'Hello parent!',
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
composed: true // makes the event jump shadow DOM boundary
|
||||
});
|
||||
|
||||
this.dispatchEvent(event);
|
||||
```
|
||||
|
||||
Other parts of the application can listen for these events with `addEventListener`:
|
||||
|
||||
```js
|
||||
const el = document.querySelector('hello-world');
|
||||
el.addEventListener('message', event => {
|
||||
alert(event.detail);
|
||||
});
|
||||
```
|
||||
|
||||
> Note the `composed: true` attribute of the custom event. It enables the custom DOM event to cross the shadow DOM boundary and enter into main DOM tree.
|
||||
|
||||
### Observing properties
|
||||
|
||||
Svelte will determine, from the template and `computed` values, which properties the custom element has — for example, `name` in our `<hello-world>` example. You can specify this list of properties manually, for example to restrict which properties are 'visible' to the rest of your app:
|
||||
|
||||
```js
|
||||
export default {
|
||||
tag: 'my-thing',
|
||||
props: ['foo', 'bar']
|
||||
};
|
||||
```
|
||||
|
||||
### Compiler options
|
||||
|
||||
Earlier, we saw the use of `customElement: true` to instruct the Svelte compiler to generate a custom element using the `tag` and (optional) `props` declared inside the component file.
|
||||
|
||||
Alternatively, you can pass `tag` and `props` direct to the compiler:
|
||||
|
||||
```js
|
||||
const { js } = svelte.compile(source, {
|
||||
customElement: {
|
||||
tag: 'overridden-tag-name',
|
||||
props: ['yar', 'boo']
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
These options will override the component's own settings, if any.
|
||||
|
||||
### Transpiling
|
||||
|
||||
* Custom elements use ES2015 classes (`MyThing extends HTMLElement`). Make sure you don't transpile the custom element code to ES5, and use a ES2015-aware minifier such as [uglify-es](https://www.npmjs.com/package/uglify-es).
|
||||
|
||||
* If you do need ES5 support, make sure to use `Reflect.construct` aware transpiler plugin such as [babel-plugin-transform-builtin-classes](https://github.com/WebReflection/babel-plugin-transform-builtin-classes) and a polyfill like [custom-elements-es5-adapterjs](https://github.com/webcomponents/webcomponentsjs#custom-elements-es5-adapterjs).
|
@ -1,8 +0,0 @@
|
||||
---
|
||||
title: Miscellaneous
|
||||
---
|
||||
|
||||
|
||||
### `<noscript>`
|
||||
|
||||
If you use `<noscript>` tags in a component, Svelte will only render them in SSR mode. The DOM compiler will strip them out, since you can't create the component without JavaScript, and `<noscript>` has no effect if JavaScript is available.
|
@ -1,5 +0,0 @@
|
||||
---
|
||||
title: TODO...
|
||||
---
|
||||
|
||||
This documentation is still a work-in-progress, like Svelte itself. If there are particular things that are missing or could be improved, then [please raise an issue on GitHub](https://github.com/sveltejs/svelte.technology)!
|
Before Width: | Height: | Size: 842 B After Width: | Height: | Size: 542 B |
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 872 B |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 4.6 KiB |
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 9.0 KiB After Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 883 B After Width: | Height: | Size: 329 B |
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 967 B |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 7.4 KiB |
After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 9.9 KiB |
Before Width: | Height: | Size: 9.7 KiB After Width: | Height: | Size: 3.0 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 5.5 KiB |
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 9.4 KiB |
Before Width: | Height: | Size: 8.1 KiB After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 51 KiB |
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 1021 B |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 7.4 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 9.0 KiB After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 59 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 4.7 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 7.1 KiB |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 738 B |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 1020 B |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 9.5 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 8.1 KiB After Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 738 B |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 9.7 KiB |
Before Width: | Height: | Size: 9.0 KiB After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 3.9 KiB |
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 50 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 8.2 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 6.3 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 6.5 KiB After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 8.3 KiB After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 3.8 KiB |
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 3.8 KiB |
Before Width: | Height: | Size: 6.9 KiB After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 11 KiB |