Skip to content
forked from google/jsaction

JsAction is a small event delegation library that decouples event binding from the code that can handle the event.

License

Notifications You must be signed in to change notification settings

gmesch/jsaction

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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 https://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.

Common events to use with JsAction

This is a list of common events to listen to when configuring JsAction, although it's not comprehensive.

contract.addEvent('click');
contract.addEvent('dblclick');
contract.addEvent('focus');
contract.addEvent('blur');
contract.addEvent('keydown');
contract.addEvent('keyup');
contract.addEvent('keypress');
contract.addEvent('load');
contract.addEvent('mouseover');
contract.addEvent('mousein');
contract.addEvent('mouseout');
contract.addEvent('mouseleave');
contract.addEvent('submit');
contract.addEvent('touchstart');
contract.addEvent('touchend');
contract.addEvent('touchmove');

About

JsAction is a small event delegation library that decouples event binding from the code that can handle the event.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • JavaScript 98.2%
  • HTML 1.8%