TwinSpark

TwinSpark was heavily influenced by Intercooler and the main reasons for existing are following:

See at Github.

Tests

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


Guide

Fragment updating

The core feature of TwinSpark is it's ability to update fragments of the page using AJAX requests to server without writing JavaScript.

To simplify inner logic you have one limitation - you can only return single element from the server. If you return more, they will be ignored. How to deal with that and why it is so - read on.

Demo

Update 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:

Demo

Hover me!

Doing stuff before/after request

If you need to do some custom things around your request, you can use ts-req-before and ts-req-after. Those use same syntax as ts-action, so it's pretty flexible. One additional thing is that if last (or the only) command of ts-req-before returns falsy value (false, or an empty string, or a zero) - this stops that request.

Demo

This will work with delay
This is prevented

Collecting data

Sometimes collecting the data you need to send in templates is really annoying: you'll have to teach lower-level components/partials/etc some knowledge about higher levels. That is something TwinSpark can prevent: when you send a request, it iterates through element parents and searches for ts-data attributes, parses their content as query string or JSON and merges it into a single object. Note how values with same keys are aggregated into an array (like normal query string or form data). You can override that by specifying key with null or empty string (see demo source).

Demo

So. Much. Data.

Targeting other elements

Often you don't want to replace clicked element, but some other part of the page, like if you hit "Refresh" and need to update big part of the page. This can be done with ts-target attribute, which takes a CSS selector.

Demo

I'm waiting... Do it!

Targeting parents

Interestingly, this seems like the most common pattern - when a button needs to update an element around itself. Add modifier parent before selector and element.closest(selector) method will be used to find a parent. This will help a lot to minimize amount of ids.

There are a few other selector modifiers - please read sources of the function _findSingleTarget until we get proper documentation. They are here because regular CSS selectors start at the top of the document, but in case of TwinSpark you're starting from a concrete element. DOM has various APIs to do that, which TwinSpark maps to modifiers: inherit, parent, child, sibling.

Demo

Wanna read text behind me? Do it!

Changing URLs

URLs are a fundament of the Web. Changing URLs in line with activity makes your app reloadable, browseable with backward/forward button and overall a good citizen of the Web.

NOTE: twinspark is loaded with limit of 3 items in history storage (which uses IndexedDB), so if you click all of those links and then go back you'll see than it'll only restore HTML for the first 3 clicks

Demo

Let's change history!

Let's change history 2!

Let's change history 3!

Let's change history 4!

Indicating activity

It's really irritating when you click a link and nothing happens for some time. Luckily TwinSpark makes it really easy: it adds class ts-active to an element, enhanced with ts-req.

Demo

Just click me

Actions

Sometimes you don't need to go to server to do something. Closing popup or hiding an element can be done without network round trip.

Default actions (those are defined in TwinSpark without you doing anything):

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 (no Safari here)

Demo of multiple actions

This is going to be removed

And this only after transition

Triggering on changed 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).

Handling clicks outside of an element

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.

Demo

Advanced

Dealing with multiple elements

TwinSpark usually deals with single element being target (ts-target) and single element being replacement (ts-req-selector) - this is much more controlled behavior than multiple elements. But 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

Batching requests

This is a thing which is not immediately obvious, but is one of the reasons why TwinSpark appeared. It is an important optimization. Our use case for it was following: page renders for an anonymous user (for efficient caching on CDN) and then status of wishlisted products is checked. It's a pattern we use often and we really wanted it to be a first-class feature.

Demo

Span 1
Span 2
Span 3

Joining requests

Demo

Update me!

And me!

And me!

Also waiting here.

Doing something when node is removed

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.

Check actions pipelining

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

Demo

Returning script elements in responses

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

Demo

Update me with a script


Use cases

Writing 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 200 ms.

Demo

Progress bar

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
Brand

Selected filters: