synth-example.mustache revision 8c17117005b705713c4b9310868677a71b236783
0N/A<style scoped>
0N/A #homebase {
0N/A margin-left: 100px;
0N/A position: relative;
407N/A height: 125px;
0N/A width: 200px;
0N/A }
0N/A .robot {
0N/A height: 100px;
0N/A width: 74px;
0N/A background: url({{componentAssets}}/red_robot.png) no-repeat top left;
0N/A position: absolute;
0N/A top: 0;
0N/A left: 0;
0N/A }
0N/A #B {
0N/A background-image: url({{componentAssets}}/blue_robot.png);
0N/A left: 125px;
0N/A }
0N/A #demo input {
0N/A margin-left: 4em;
0N/A }
0N/A #demo label {
0N/A font-size: 87%;
0N/A color: #555;
392N/A }
392N/A</style>
392N/A
392N/A<div class="intro">
392N/A <p>
392N/A This example will illustrate how to use the synthetic event creation
392N/A API. We'll create an `arrow` event that fires in response
392N/A to the user pressing the arrow keys (up, down, left, right) and adds a
457N/A `direction` property to the generated event.
392N/A </p>
392N/A
392N/A <p>Subscribing to this new event will look like this:</p>
392N/A ```
392N/A node.on("arrow", onArrowHandler);
392N/A ```
392N/A
392N/A <p>
392N/A Support will also be added for delegation, allowing a single subscriber
392N/A from a node higher up the DOM tree, to listen for the new event
392N/A emanating from its descendant elements.
392N/A </p>
392N/A
392N/A ```
392N/A containerNode.delegate("arrow", onArrowHandler, ".robot");
392N/A ```
392N/A</div>
392N/A
392N/A<div class="example yui3-skin-sam">
392N/A {{>synth-example-source}}
392N/A</div>
392N/A
99N/A<h2>`on`, `fire`, and `detach`</h2>
392N/A
392N/A<p>
392N/A The three interesting moments in the lifecycle of a DOM event subscription
392N/A are
392N/A</p>
30N/A
0N/A<ol>
0N/A <li>The event is subscribed to</li>
0N/A <li>The event is fired</li>
0N/A <li>The event is unsubscribed from</li>
0N/A</ol>
0N/A
0N/A<p>
0N/A Create a new synthetic DOM event with `Y.Event.define( <em>name</em>,
527N/A <em>config</em> )`. Define the implementation logic for the
456N/A `on` and `detach` moments in the configuration.
527N/A Typically the condition triggering the event firing is set up in the
527N/A `on` phase.
527N/A</p>
527N/A
202N/A```
202N/AY.Event.define("arrow", {
0N/A on: function (node, sub, notifier) {
457N/A // what happens when a subscription is made
457N/A
457N/A // if (condition) {
0N/A notifier.fire(); // subscribers executed
0N/A // }
0N/A },
456N/A
0N/A detach: function (node, sub, notifier) {
508N/A // what happens when a subscription is removed
889N/A }
0N/A});
0N/A```
0N/A
527N/A<p>
527N/A In the case of arrow handling, the trigger is simply a key event with a
889N/A `keyCode` between 37 and 40. There are a few browser quirks with arrow
889N/A handling that warrant listening to `keydown` for some browsers and
889N/A `keypress` for others, so we'll take care of that transparently for `arrow`
889N/A subscribers.
889N/A</p>
889N/A
889N/A```
889N/AY.Event.define("arrow", {
889N/A on: function (node, sub, notifier) {
0N/A var directions = {
889N/A 37: 'left',
889N/A 38: 'up',
527N/A 39: 'right',
99N/A 40: 'down'
99N/A };
99N/A
0N/A // Webkit and IE repeat keydown when you hold down arrow keys.
0N/A // Opera links keypress to page scroll; others keydown.
0N/A // Firefox prevents page scroll via preventDefault() on either
457N/A // keydown or keypress.
0N/A // Bummer to sniff, but can't test the repeating behavior, and a
0N/A // feature test for the scrolling would more than double the code size.
527N/A var eventName = (Y.UA.webkit || Y.UA.ie) ? 'keydown' : 'keypress';
457N/A
0N/A // To make detaching the associated DOM event easy, store the detach
0N/A // handle from the DOM subscription on the synthethic subscription
527N/A // object.
0N/A sub._detacher = node.on(eventName, function (e) {
0N/A // Only notify subscribers if one of the arrow keys was pressed
0N/A if (directions[e.keyCode]) {
527N/A // Add the extra property
0N/A e.direction = directions[e.keyCode];
527N/A
527N/A // Firing the notifier event executes the arrow subscribers
0N/A // Pass along the key event, which will be renamed "arrow"
527N/A notifier.fire(e);
0N/A }
527N/A });
0N/A },
527N/A
0N/A detach: function (node, sub, notifier) {
0N/A // Detach the key event subscription using the stored detach handle
0N/A sub._detacher.detach();
0N/A }
527N/A} );
527N/A```
0N/A
0N/A<h2>Add Delegation Support</h2>
527N/A<p>
527N/A Since the `arrow` event is simply a filtered `keydown` or `keypress` event,
0N/A no special handling needs to be done for delegate subscriptions. We will
0N/A extract the key event handler and use it for both `on("arrow", ...)` and
527N/A `delegate("arrow", ...)` subscriptions.
527N/A</p>
0N/A
0N/A```
527N/AY.Event.define("arrow", {
527N/A // Webkit and IE repeat keydown when you hold down arrow keys.
0N/A // Opera links keypress to page scroll; others keydown.
0N/A // Firefox prevents page scroll via preventDefault() on either
527N/A // keydown or keypress.
0N/A _event: (Y.UA.webkit || Y.UA.ie) ? 'keydown' : 'keypress',
527N/A
527N/A _keys: {
0N/A '37': 'left',
527N/A '38': 'up',
0N/A '39': 'right',
0N/A '40': 'down'
527N/A },
527N/A
527N/A _keyHandler: function (e, notifier) {
0N/A if (this._keys[e.keyCode]) {
0N/A e.direction = this._keys[e.keyCode];
0N/A notifier.fire(e);
527N/A }
392N/A },
392N/A
527N/A on: function (node, sub, notifier) {
0N/A // Use the extended subscription signature to set the 'this' object
0N/A // in the callback and pass the notifier as a second parameter to
527N/A // _keyHandler
527N/A sub._detacher = node.on(this._event, this._keyHandler,
0N/A this, notifier);
527N/A },
0N/A
0N/A detach: function (node, sub, notifier) {
0N/A sub._detacher.detach();
527N/A },
527N/A
0N/A // Note the delegate handler receives a fourth parameter, the filter
0N/A // passed (e.g.) container.delegate('click', callback, '.HERE');
0N/A // The filter could be either a string or a function.
0N/A delegate: function (node, sub, notifier, filter) {
527N/A sub._delegateDetacher = node.delegate(this._event, this._keyHandler,
0N/A filter, this, notifier);
0N/A },
0N/A
0N/A // Delegate uses a separate detach function to facilitate undoing more
0N/A // complex wiring created in the delegate logic above. Not needed here.
0N/A detachDelegate: function (node, sub, notifier) {
527N/A sub._delegateDetacher.detach();
436N/A }
436N/A});
436N/A```
436N/A
436N/A<h2>Use it</h2>
436N/A<p>
0N/A Subscribe to the new event or detach the event as you would any other DOM
0N/A event.
436N/A</p>
436N/A
436N/A```
436N/Afunction move(e) {
0N/A // to prevent page scrolling
0N/A e.preventDefault();
527N/A
527N/A // See full code listing to show the data set up
0N/A var xy = this.getData();
0N/A
0N/A switch (e.direction) {
527N/A case 'up': xy.y -= 10; break;
392N/A case 'down': xy.y += 10; break;
392N/A case 'left': xy.x -= 10; break;
0N/A case 'right': xy.x += 10; break;
0N/A }
0N/A
527N/A this.transition({
527N/A top : (xy.y + 'px'),
0N/A left: (xy.x + 'px'),
527N/A duration: .2
0N/A });
527N/A}
527N/A
0N/A// Subscribe using node.on("arrow", ...);
0N/AY.one("#A").on("arrow", move),
0N/AY.one("#B").on("arrow", move)
527N/A
392N/A// OR using container.delegate("arrow", ...);
392N/Asubs = Y.one('#demo').delegate('arrow', move, '.robot');
0N/A```
0N/A
0N/A<h2>Bonus Step: to the Gallery!</h2>
0N/A<p>
527N/A Synthetic events are perfect candidates for Gallery modules. There are a
527N/A number already hosted there, and there are plenty of UI interaction
0N/A patterns that would benefit from being encapsulated in synthetic
527N/A events.
527N/A</p>
0N/A
0N/A<p>
527N/A The `arrow` event in this example is also
0N/A <a href="http://yuilibrary.com/gallery/show/event-arrow">in the gallery</a>,
527N/A but with additional functionality. Check out
0N/A <a href="https://github.com/lsmith/yui3-gallery/blob/master/build/gallery-event-arrow/gallery-event-arrow-debug.js">its source</a>
527N/A to see what you can do with synthetic events.
0N/A</p>
392N/A
0N/A<h2>Full Code Listing</h2>
0N/A```
0N/A{{>synth-example-source}}
527N/A```
527N/A
527N/A