TwinSpark includes a very simple syntax (inspired by shell) for calling small JavaScript functions: some-command arg1 arg2, other-command arg3 arg4. So some-command takes two arguments, and then passes result to other-command, forming a pipeline.

Pipeline is asynchronous and waits for promises to resolve. If a command returns false (not a falsy value, but exactly false), pipeline stops. You can write multiple pipelines in a single ts-action attribute, joining them by ;. Those pipelines are independent, but executed in order.

Actions specified in ts-action are triggered by event specified in ts-trigger, and respect ts-target if it’s specified.

If you need to pass argument with spaces, you can enclose it in quotes or double quotes, like target "parent p". Escaping works, i.e. log '\' is a quote'.

Built-in commands (arguments in brackets are optional)
Command Description
stop Calls .stopPropagation() on triggering event.
prevent Calls .preventDefault() on triggering event.
delay N Delays pipeline execution by N ms, or seconds with syntax Ns.
target SEL Selects another element, identified by SEL. Supports ts-trigger modifiers.
remove [SEL] Removes target element. With optional argument removes element identified by SEL.
class+ CLS Adds class CLS to a target element.
class CLS Alias for class+.
class- CLS Removes class CLS from a target element.
class^ CLS Toggles class CLS on a target element.
classtoggle CLS Alias for class^.
text [VALUE] Returns .innerText. If VALUE or input is passed also sets it.
html [VALUE] Returns .innerHTML. If VALUE or input is passed also sets it.
attr name [VALUE] Returns VALUE of attribute name. If VALUE is passed also sets it.
log [...] Logs all passed argument and pipeline input if passed.
not cmd [...] Inverts result of calling cmd ....
wait EVENTNAME Waits until event EVENTNAME happens on a target element, only once. Suitable to use in cases like class+ fade, wait transitionend, remove.
on EVENTNAME Adds an event listener on EVENTNAME. Warning: needs to be executed to add a listener, so something like ts-trigger="load" is necessary.
req [METHOD] URL Execute a request, just like ts-req. If there is any input, adds it to request data as input=INPUT.

Registering new commands

New commands can be registered with twinspark.func({"command-name": () => {}}) API. Command is a simple JavaScript function and is passed all arguments from a pipeline, plus options object as a last argument (usually named just o). Options object contains the following:

There is a helper twinspark.arity to simplify registering commands with optional arguments. It dispatches on arguments.length and uses func.length to determine which one to call. For example, remove is implemented that way:

remove: arity(
  function(o) { o.el.remove(); },
  function(sel, o) { findTarget(o.el, sel).remove(); }


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.


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.