This are all examples collected in a single page, they are also present on respective directive pages. This page is used mostly for CI.
Tests
This document has tests embedded in examples, to run them, press:
Examples
Fragment Updating #
Explanation: a
issues a request to a server on click, and then
is replaced with a new link from a response.
Demo
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.
Demo
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:
Demo
Before/After Request #
This example demostrates how you can interfere with request using
ts-req-before
.
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.
Demo
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:
- a text input
- a hidden input
- additional data in
ts-data
attribute - two submit buttons
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.
Demo
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.
Demo
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)
).
Demo
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.
Demo
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 event.target
:
Demo
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.
Demo
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
Demo
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
.
Demo
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]
.
Demo
Update me!
And me!
Also waiting here.
Redirect from server #
Redirect to target url, pass to response headers ts-location
parameter.
Demo
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.
Demo
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
- Inputs have an
id
attribute - so that morphing algorithm can find them reliably. - POST body contains
<input type="submit">
's value - this way backend distinguish submission and validation. keyup
updates form on every character input and it feels natural - morph algorithm skips currently focused element so that state is intact.
Demo
Solve this problem with odd numbers
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.
Demo
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.
Demo
<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.
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"
.
Demo
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.
Demo
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.
Demo
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.
Demo
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.
Demo
Request JSON API #
Simple POST
request with JSON body.