JsAction is a small event delegation library that decouples event binding from the code that can handle the event.
JavaScript HTML
Switch branches/tags
Nothing to show
Clone or download
mknichel Add documentation to the JsAction README for how to separate the even…
…t contract, late load the dispatcher, and replay any queued events. I haven't had time to document how event handlers can be late loaded (similar to how [] does it), so I'll try to add that at another time.

RELNOTES: None.

	Change on 2017/06/21 by mknichel <mknichel@google.com>

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=159789612
Latest commit 414f1c4 Jun 22, 2017
Permalink
Failed to load latest commit information.
LICENSE Initial commit. May 20, 2014
NOTICES Initial commit. May 20, 2014
README.md Add documentation to the JsAction README for how to separate the even… Jun 23, 2017
actionflow.js Use escValue instead of value. Jun 5, 2017
actionflow_test.js Fire ABANDONED when an abandoned ActionFlow completes. Jun 2, 2017
actionflow_test_dom.html Initial commit. May 20, 2014
actionlogger.js Move jsaction_logger to []/js so that it can be used by both reactive… Jun 5, 2017
cache.js The eventContract.getAction_ method has often shown up in our profile… Jun 2, 2017
customevents.js The "@suppress {checkStructDictInheritance}" has been a no op since t… Jun 2, 2017
customevents_test.js Add jsaction custom events testing library. Jun 2, 2017
customevents_test_dom.html Add jsaction custom events testing library. Jun 2, 2017
dispatcher.js Remove manual setTimeout calls, replace with calls to goog.async.run. Jun 2, 2017
dispatcher_auto.js fix names per comments in 67866688 May 27, 2017
dispatcher_example.js Initial commit. May 20, 2014
dispatcher_export.js Add documentation to the JsAction README for how to separate the even… Jun 23, 2017
dispatcher_test.js Make non-module-id namespaces an error, but add an opt-out attribute … Jun 2, 2017
dom.js Automated g4 rollback of changelist 80114121. Jun 2, 2017
dom_test.js Improve 'mouseenter' and 'mouseleave' handling when the mouse enters/… May 27, 2017
event.js Roll forward of changelist 104106136. Jun 2, 2017
event_test.js Convert string tag name in createDom to goog.dom.TagName. Jun 5, 2017
eventcontract.js Do not preventDefault on focus on textarea on Firefox when a jsaction… Jun 2, 2017
eventcontract_auto.js fix names per comments in 67866688 May 27, 2017
eventcontract_example.js fix names per comments in 67866688 May 27, 2017
eventcontract_export.js Create a precompiled contract + dispatcher JS binary for jsaction tha… Jun 11, 2017
eventcontract_test.js Set maximum move distance for fastclicks between touchstart and touch… Jun 2, 2017
eventcontract_test_dom.html Automated g4 rollback of changelist 83330980. Jun 2, 2017
generator.js Automated g4 rollback of changelist 83330980. Jun 2, 2017
generator_test.js Initial commit. May 20, 2014
generator_test_dom.html Initial commit. May 20, 2014
jsaction.js Tighten up selector in broadcastCustomEvent, which previously could p… Jun 2, 2017
jsaction_test.js Tighten up selector in broadcastCustomEvent, which previously could p… Jun 2, 2017
jsaction_test_dom.html Tighten up selector in broadcastCustomEvent, which previously could p… Jun 2, 2017
nativeevents.js Changed jsaction.testing.nativeEvents to testonly because it depends … Jun 2, 2017
nullactionflow.js Initial commit. May 20, 2014
nullactionflow_test.js Initial commit. May 20, 2014
replay.js Propagate the triggering event when replaying jsaction events. Jun 5, 2017
replay_test.js When replaying event, stores the timestamp of the original event in o… Jun 5, 2017
syntax.js Remove @enum annotation from non-enum value. Jun 5, 2017

README.md

JsAction

JsAction is a tiny event delegation library that allows decoupling the DOM nodes on which the action occurs from the JavaScript code that handles the action.

The traditional way of adding an event handler is to obtain a reference to the node and add the event handler to it. JsAction allows us to map between events and names of handlers for these events via a custom HTML attribute called jsaction.

Separately, JavaScript code registers event handlers with given names which need not be exposed globally. When an event occurs the name of the action is mapped to the corresponding handler which is executed.

Finally, JsAction uncouples event handling from actual implementations. Thus one may late load the implementations, while the app is always able to respond to user actions marked up through JsAction. This can help in greatly reducing page load time, in particular for server side rendered apps.

Building

JsAction is built using the Closure Compiler. You can obtain a recent compiler from the site.

JsAction depends on the Closure Library. You can obtain a copy of the library from the GitHub repository.

The compiler is able to handle dependency ordering automatically with the --only_closure_dependencies flag. It needs to be provided with the sources and any entry points.

See the files dispatch_auto.js, eventcontract_auto.js, and eventcontract_example.js for typical entry points.

Here is a typical command line for building JsAction's dispatch_auto.js:

find path/to/closure-library path/to/jsaction -name "*.js" |
    xargs java -jar compiler.jar  \
    --output_wrapper="(function(){%output%})();" \
    --only_closure_dependencies \
    --closure_entry_point=jsaction.dispatcherAuto

Using drop-in scripts

If you would like to test out JsAction, you can link precompiled scripts into your page.

<script src="https://www.gstatic.com/jsaction/contract.js"></script>

...

<script src="https://www.gstatic.com/jsaction/dispatcher.js" async></script>

Usage

You can play around with JsAction already set up with the following directions at https://jsfiddle.net/q2eacgs7/.

In the DOM

Actions are indicated with the jsaction attribute. They are separated by ;, where each one takes the form:

[eventType:]<namespace>.<actionName>

If an eventType is not specified, JsAction will assume click.

<div id="container">
  <div id="foo"
       jsaction="leftNav.clickAction;dblclick:leftNav.doubleClickAction">
    some content here
  </div>
</div>

In JavaScript

Set up

const eventContract = new jsaction.EventContract();

// Events will be handled for all elements under this container.
eventContract.addContainer(document.getElementById('container'));

// Register the event types we care about.
eventContract.addEvent('click');
eventContract.addEvent('dblclick');

const dispatcher = new jsaction.Dispatcher();
eventContract.dispatchTo(dispatcher.dispatch.bind(dispatcher));

Register individual handlers

/**
 * Do stuff when actions happen.
 * @param {!jsaction.ActionFlow} flow Contains the data related to the action
 *     and more. See actionflow.js.
 */
const doStuff = function(flow) {
  // do stuff
  alert('doStuff called!');
};

dispatcher.registerHandlers(
    'leftNav',                       // the namespace
    null,                            // handler object
    {                                // action map
      'clickAction' : doStuff,
      'doubleClickAction' : doStuff
    });

Late loading the JsAction dispatcher and event handlers

JsAction splits the event contract and dispatcher into two separably loadable binaries. This allows applications to load the small event contract early on the page to capture events, and load the dispatcher and event handlers at a later time. Since captured events are queued until the dispatcher loads, this pattern can ensure that user events are not lost even if they happen before the primary event handlers load.

Visit http://jsfiddle.net/880m0tpd/4/ to try out a working example.

Load the contract early in the page

Just like in the regular example, in this example the event contract is loaded very early on the page, ideally in the head of the page.

<script id="contract" src="https://www.gstatic.com/jsaction/contract.js"></script>
<script>
  const eventContract = new jsaction.EventContract();

  // Events will be handled for all elements on the page.
  eventContract.addContainer(window.document.documentElement);

  // Register the event types handled by JsAction.
  eventContract.addEvent('click');
</script>

<button jsaction="button.handleEvent">
  click here to capture events
</button>

The event contract is configured to capture events for the entire page. Since the dispatcher and event handlers are not loaded yet, the event contract will just queue the events if the user tries to interact with the page. These events can then be replayed after the dispatcher and event handlers are loaded, which will be shown in this example next. This will ensure that no user interaction is lost, even if it happens before the code is loaded.

Loading the dispatcher and replaying events

At any point later in the page, the dispatcher and event handlers can be loaded and any queued events can be replayed.

After the dispatcher and event handler code loads, you will configure the dispatcher just like in the regular example:

// This is the actual event handler code.
function handleEvent() {
  alert('event handled!');
}

// Initialize the dispatcher, register the handlers, and then replay the queued events.
const dispatcher = new jsaction.Dispatcher();
eventContract.dispatchTo(dispatcher.dispatch.bind(dispatcher));
dispatcher.registerHandlers(
    'button',
    null,
    { 'handleEvent': handleEvent });

There is some new code to replay the queued events:

// This code replays the queued events. Applications can define custom replay
// strategies.
function replayEvents(events, jsActionDispatcher) {
  while (events.length) {
    jsActionDispatcher.dispatch(events.shift());
  }
}

// This will automatically trigger the event replayer to run if there are
// queued events.
dispatcher.setEventReplayer(replayEvents);

Now any events that happen during page load before the JS has loaded will be replayed when the primary JS does load, ensuring that user interactions are not lost.