This are all examples collected in a single page, they are also present on respective directive pages. This page is used mostly for CI.


This document has tests embedded in examples, to run them, press:


Fragment Updating #

Explanation: on click link issues a request to a server, and then is replaced with a new link from a response.


Update me

Use only part of the response #

Server returns more HTML than we need - maybe a full template is rendered, maybe something else is going on - and we need to use just a part of it.


Click me

Triggering Requests #

Usually requests are triggered on natural interrupts: submit on forms and clicks elsewhere, but sometimes you want more, like triggering on being seen or hovered:


Hover me!

Before/After Request #

This example demostrates how you can interfere with request using ts-req-before.


This will work with delay
This is prevented

Collecting Data #

Here parent span declares ts-data with three keys (a, b, c) and then child a adds another entry with key b, drops existing c value and adds another (so it overrides c value). And then sends a POST request to a backend. Click link to see what data was sent.


So. Much. Data.

Form With Data #

Form can be a bit tricky to collect data correctly, particularly weird thing is when you have a few submit buttons. The following form has:

When you click one of the buttons, you can observe that only that button was included in the data &emdash; which is what you need to decide which action to take.


Click a button to replace me with form data.

Targeting Other Elements #

Default behavior is to replace element, which issues the request (in this case an a). ts-target sets another element as a target for the request.


I'm waiting... Do it!

Targeting Parents #

This is very common pattern - when a button needs to update an element around itself. Modifier parent will search upwards for a suitable element (using element.closest(sel)).


Wanna read text behind me? Do it!

Relative Targeting (advanced)#

Most twinspark commands and extensions operate directly on the current target element. However, some of them might require a pair of elements (e.g. a command that copies data from one input to another). This means supplying a TwinSpark selector as an argument. To point directly to the current target element, use the target keyword.


Click here or here

to copy the value here:

Click one of these buttons to rename it with the value above: or

Another useful target is the element that created the event. The simplest way to access it is to create a twinspark extension that sets target to


None of these buttons have ts-action attribute, but when you click, the event is bubbled to the container that executes commands:
One Two Three

Changing URL #

Clicking on each of those links in turn will cause URL to change and add a new entry in browser's history.


Indicator #

It's really irritating when you click a link and nothing happens for some time. Luckily TwinSpark makes it really easy: it adds attribute aria-busy="true" to an origin element.


Just click me

Actions #

Those are simple examples of using actions, see their source for details.

Demo of remove

Hey! I'm here!

Demo of delay

Remove with timeout

Demo of wait (waiting for an event to happen)

Remove after transition

Demo of animate

Demo of multiple actions

This is going to be removed

And this only after transition

Sortable #

An advanced example showing how to use actions (in particular on and req) in conjunction with html5sortable.

It adds a handler to sortupdate event, which is raised by html5sortable, and then triggers a POST request to a backend to store a new order.

Demo of on and req

πŸ™‚Smile πŸ˜‡Halo 😁Beam 🀣ROFL

Here you'll see sorted names.

Visibility #

Doing something when element is almost visible makes it possible to implement lazy loading and various analytical events


You'll probably see this text after around 5 seconds or so. Click "Reset" to see loader again.

This sentence will log some message when it becomes invisible (moves out of browser viewport, and, actually, on load as well).

Click Outside #

Popups, modals, menus and some other elements can make use of click happened outside. It could be done with markup and underlying element, but why bother if you have straightforward trigger.

This trigger is ideally used with modifier once, since you're probably going to remove that modal you calling it on - using once will clean up your listeners so you won't get memory leaks.


Multiple Children #

If you look how endless scrolling is implemented in HTML, it's usually a long list of elements inside some other element - so you have to deal with several elements being appended to a parent. For this and similar use cases there is a modifier children in ts-req-selector.


Element 1 Element 2

Element 1 Element 2

Multiple Fragments #

First element is updated through regular flow with ts-target. Second element uses ts-swap-push attribute. Third element uses ts-swap-push header. The latter has a form [replace strategy]: [selector-in-document] <= [selector-in-response].


Update me!

And me!

Also waiting here.

Batching (advanced)#

Here all spans trigger request on visible, so click "Reset" to see more requests. See sources and debug panel to see how requests are combined in a single one.


Span 1
Span 2
Span 3

Dynamic Form Validation (advanced)#

Form validation is a common task, and TwinSpark allows to consolidate validation logic on the server. Surprisingly, it could be difficult, but ts-swap="morph" strategy allows us to just return whole new form with errors and not mess up with focus.

Important bits


Solve this problem with odd numbers

= 14

On Removal (advanced)#

It is useful to do something when node is removed (especially if that's some child or even non-related node triggering that removal). It's possible, but not recommended to use often since performance characteristics of the code are not well understood.


When this paragraph is removed by clicking button, it will resurrect itself.

Pipelining Actions (advanced)#

Actions pipe their return values into next action as o.input, check the source of the next example to see how it works.


<script> in response (advanced)#

Setting innerHTML to a value which contains script element does not execute JavaScript inside that element. TwinSpark handles that for you, check it out.


Update me with a script

Autocomplete #

Autocomplete is interesting because it executes many things at once. Just look at the source, the interesting part is trigger modifiers - it does something only if user typed something (rather than just navigated field with cursor keys) and then stopped for 100 ms.

When autocomplete triggers a time-consuming operation (e.g. full-text search), the implementation above triggers numerous requests if the user types slow enough. If requests finish at different durations, an older request can override the latest. To avoid this, we need to abort the XHR using ts-req-strategy="last".


Progress Bar #

Smooth progress bar with updates from a server

If you look at source, there is a button targeting parent div. When you click it, server responds with a new markup, which will make a request to a server again in 500 ms thanks to ts-trigger="load delay 500".

Interesting part of that response is div id="pb1", which is actually progress bar. Each new response from the server uses the same id, so settling kicks in, and makes .bar-item transition work.


Start progress

Filters #

Filtering on ecommerce sites is a complex task. On one side you want it to be crawlable by Google, on the other if a user selected two filters one by one you'd like to see products, filtered by both. NaΓ―ve implementation will filter only one of them if a pause between clicks was short enough. It seems like the best way is form, full of links (so that Google/no-js envs can still use it), which toggle checkboxes when JS is enabled and auto-submit form.

Adidas Nike Puma

Selected filters:

Animated Pagination #

Morphing gives you ability to add animations and transitions during HTML replacement. This example uses zero lines of custom JS, just some styles to add transitions.


Morphing Video #

This is a hard problem to solve with other algorithms (notably, morphdom gets it wrong). HTML structure is changed a lot, and element with an id is deep (enough) inside that structure, but Youtube video still plays after change.