src/cm/media/js/lib/yui/yui_3.10.3/docs/event/synths.html
author gibus
Tue, 11 Feb 2014 12:33:25 +0100
changeset 572 93383e54e042
parent 525 89ef5ed3c48b
permissions -rw-r--r--
Font size for piwik optout iframe.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Creating Synthetic Events</title>
    <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=PT+Sans:400,700,400italic,700italic">
    <link rel="stylesheet" href="../../build/cssgrids/cssgrids-min.css">
    <link rel="stylesheet" href="../assets/css/main.css">
    <link rel="stylesheet" href="../assets/vendor/prettify/prettify-min.css">
    <link rel="shortcut icon" type="image/png" href="../assets/favicon.png">
    <script src="../../build/yui/yui-min.js"></script>
    
</head>
<body>
<!--
<a href="https://github.com/yui/yui3"><img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on GitHub"></a>
-->
<div id="doc">
    <div id="hd">
        <h1><img src="http://yuilibrary.com/img/yui-logo.png"></h1>
    </div>
    
        <a href="#toc" class="jump">Jump to Table of Contents</a>
    

            <h1>Creating Synthetic Events</h1>
    <div class="yui3-g">
        <div class="yui3-u-3-4">
            <div id="main">
                <div class="content"><div class="intro">
    <p>Synthetic events are usually named abstractions that bind to existing
    DOM events to monitor user actions for specific patterns.  However, at 
    heart they are no more than a set of callbacks executed in response to
    various triggering methods in the DOM event system.</p>
    
    <p>You can do all sorts of things with synthetic events, including:</p>

    <ul>
        <li>
            redefine native DOM events that behave inconsistently across
            browsers (e.g. <a href="focus.html"><code>focus</code> and <code>blur</code></a>)
        </li>
        <li>
            provide abstract events that attach to different DOM events based on
            the environment (e.g. <a href="touch.html#move"><code>gesturemovestart</code>
            and family</a>)
        </li>
        <li>
            create events with different subscription signatures (e.g.
            <a href="hover.html"><code>hover</code></a>)
        </li>
        <li>
            create configurable events that only execute subscribers when
            criteria passed during subscription are met (e.g.
            <a href="touch.html#flick"><code>flick</code></a> or
            <a href="key.html"><code>key</code></a>)
        </li>
        <li>
            create events that encapsulate common UX patterns (e.g.
            <a href="outside.html"><code>clickoutside</code></a>)
        </li>
        <li>
            create fun little easter eggs (e.g. <a
            href="http://yuilibrary.com/gallery/show/event-konami"><code>konami</code></a>)
        </li>
        <li>and more...</li>
    </ul>
</div>

<h2 id="the-hooks">The hooks</h2>

<p>Synthetic events hook into the subscription binding and unbinding methods.  Specifically:</p>

<ol>
    <li><code>node.on(&quot;eventName&quot;, ...)</code>, <code>Y.on(&quot;eventName&quot;, ...)</code>, <a href="index.html#one-time-subscriptions">and family</a></li>
    <li><code>node.delegate(&quot;eventName&quot;, ...)</code> or <code>Y.delegate(&quot;eventName&quot;, ...)</code></li>
    <li><code>node.detach(...)</code> or <code>subscription.detach()</code></li>
</ol>

<p>With the exception of a separate <code>detachDelegate()</code> method, the names used when defining synthetic events are the same as these basic methods.</p>

<pre class="code prettyprint">Y.Event.define(&quot;tripleclick&quot;, {
    on: function (node, subscription, notifier) {
        &#x2F;&#x2F; called in response to individual subscriptions
    },

    delegate: function (node, subscription, notifier, filter) {
        &#x2F;&#x2F; called in response to delegate subscriptions
    },

    detach: function (node, subscription, notifier) {
        &#x2F;&#x2F; called when individual subscriptions are detached in any way
    },

    detachDelegate: function (node, subscription, notifier) {
        &#x2F;&#x2F; called when delegate subscriptions are detached in any way
    }
});</pre>


<h2 id="subscriptions-and-notifiers">Subscriptions and Notifiers</h2>

<p>In addition to the subscribing Node, each method receives a
<em>subscription</em> and a <em>notifier</em>.  Use the <em>subscription</em>
to store event handles or other data that may be needed by another method.  Use
<em><code>notifier.fire(e)</code></em> to dispatch the event to the callbacks that were
bound to it.</p>

<pre class="code prettyprint">Y.Event.define(&quot;tripleclick&quot;, {
    on: function (node, subscription, notifier) {
        var count = 0;

        subscription._handle = node.on(&quot;click&quot;, function (e) {
            if (++count === 3) {
                &#x2F;&#x2F; Call notifier.fire(e) to execute subscribers.
                &#x2F;&#x2F; Pass the triggering event facade to fire()
                notifier.fire(e);
            } else {
                ...
            }
        });
    },

    detach: function (node, subscription, notifier) {
        subscription._handle.detach();
    },

    delegate: function (node, subscription, notifier, filter) { ... },
    detachDelegate: function (node, subscription, notifier) { ... }
});</pre>


<p>Subscribers to the synthetic event should receive a <code>DOMEventFacade</code>.  The
easiest way to provide one is to pass the triggering DOM event's facade to
<code>notifier.fire(e)</code>.  The facade's <code>e.type</code> will be updated to the name of the
synth.  You will also have the opportunity to add extra data to the event
before dispatching to the subscription callbacks.</p>

<pre class="code prettyprint">Y.Event.define(&#x27;multiclick&#x27;, {
    on: function (node, sub, notifier) {
        var count = 0,
            timer;

        sub._handle = node.on(&#x27;click&#x27;, function (e) {
            count++;

            if (timer) {
                timer.cancel();
            }

            timer = Y.later(200, null, function () {
                e.clicks = count;
                count = 0;
                
                &#x2F;&#x2F; subscribers will get e with e.type == &#x27;multiclick&#x27;
                &#x2F;&#x2F; and extra property e.clicks
                notifier.fire(e);
            });
        });
    },
    ...
});</pre>


<h2 id="delegation-support">Delegation support</h2>

<p>The <code>delegate</code> function implementation takes an extra argument, the <code>filter</code> that was passed <code>node.delegate(type, callback, <em>HERE</em>)</code>.  It's your responsibility to make sense of this filter for your event.</p>

<p>Typically, it is just passed along to a <code>node.delegate(...)</code> call against another event, deferring the filtration to the core <code>delegate()</code> method.</p>

<pre class="code prettyprint">Y.Event.define(&quot;tripleclick&quot;, {
    on: function (node, subscription, notifier) { ...  },
    detach: function (node, subscription, notifier) { ...  },

    delegate: function (node, subscription, notifier, filter) {
        var activeNode = null,
            count = 0,
            timer;

        subscription._handle = node.delegate(&quot;click&quot;, function (e) {
            if (timer) {
                timer.cancel();
            }

            if (this !== activeNode) {
                activeNode = this;
                count = 0;
            }

            if (++count === 3) {
                &#x2F;&#x2F; Call notifier.fire(e) just as with &#x60;on&#x60;
                notifier.fire(e);
            } else {
                timer = Y.later(300, null, function () {
                    count = 0;
                });
            }
        }, filter); &#x2F;&#x2F; filter is passed on to the underlying &#x60;delegate()&#x60; call
    },

    detachDelegate: function (node, subscription, notifier) {
        subscription._handle.detach();
    }
});</pre>


<h2 id="extra-arguments">Extra Arguments</h2>

<p>Supply a <code>processArgs</code> method in the event definition to support a custom
subscription signature.  The method receives two arguments:</p>

<ol>
    <li>an array of the subscription arguments for analysis</li>
    <li>
        a boolean <code>true</code> if the subscription is being made through
        <code>delegate(...)</code>
    </li>
</ol>

<p>If this method is supplied, it</p>
<ul>
    <li>
        <strong>MUST</strong> remove the extra arguments from the arg array
        that is passed in, and
    </li>
    <li>
        <strong>SHOULD</strong> return the extra data relevant to the
        subscription.
    </li>
</ul>


<p>The same <code>processArgs</code> method is used by both <code>on</code> and <code>delegate</code>, but there
are various signatures to account for.  The easiest way to accept extra
arguments is to require them from index 3 in the argument list.  It's also best
to limit the number of extra arguments to one and require an object literal to
allow for future changes.</p>

<pre class="code prettyprint">&#x2F;&#x2F; for an event that takes one extra param
processArgs: function (args, isDelegate) {
    var extra = args[3];
    
    &#x2F;&#x2F; remove the extra arguments from the array
    args.splice(3,1);

    return extra;
}

&#x2F;&#x2F; for an event that takes three extra args
processArgs: function (args, isDelegate) {
    return args.splice(3,3);
}</pre>


<p>Requiring extra params start at index 3 of the <code>args</code> array results in the
following subscription signatures:</p>
<pre class="code prettyprint">var extraConfig = { ... };

&#x2F;&#x2F; Third argument for node.on() and node.delegate
node.on(&#x27;extraArgEvent&#x27;, callback, extraConfig, thisOverride, arg...);
node.delegate(&#x27;extraArgEvent&#x27;, callback, extraConfig, filter, thisOverride, arg...);

&#x2F;&#x2F; Fourth argument for Y.on() and Y.delegate
Y.on(&#x27;extraArgEvent&#x27;, callback, targetSelector, extraConfig, thisOverride, arg...);
Y.delegate(&#x27;extraArgEvent&#x27;, callback, parentSelector, extraConfig, filter, thisOverride, arg...);</pre>


<p>For some custom signatures, the placement of the extra argument for
implementers using <code>Y.on()</code> or <code>Y.delegate()</code> may look awkward.  Sometimes you
can support extras at other indexes if you can reliably tell that the argument
is not part of
<a href="index.html#binding-this-and-additional-callback-arguments">the extended
signature for <code>on(...)</code> or <code>delegate(...)</code></a>. See the <a
href="http://yuilibrary.com/yui/docs/api/files/event_js_hover.js.html">source for the "hover"
event</a> for an example of supporting multiple signatures.</p>

<p>The return value of <code>processArgs</code> is assigned to <code>subscription._extras</code> for the <code>on</code> and <code>delegate</code> definition methods.</p>

<pre class="code prettyprint">Y.Event.define(&#x27;multiclick&#x27;, {
    processArgs: function (args, isDelegate) {
        &#x2F;&#x2F; The args list will look like this coming in:
        &#x2F;&#x2F; [ type, callback, node, (extras...), [filter,] thisObj, arg0...argN ]
        return args.splice(3,1)[1] || {};
    },

    &#x2F;&#x2F; Custom subscription signatures don&#x27;t change the params of on&#x2F;delegate
    on: function (node, sub, notifier) {
        var clicks = 0,
            &#x2F;&#x2F; data returned from processArgs is available at sub._extras
            min = sub._extras.minClicks || 3,
            max = sub._extras.maxClicks || 10,
            timer;

        sub._handle = node.on(&#x27;click&#x27;, function (e) {
            if (timer) {
                timer.cancel();
            }

            if (++clicks === max) {
                e.clicks = clicks;
                notifier.fire(e);
            } else {
                timer = Y.later(200, null, function () {
                    if (clicks &gt; min) {
                        e.clicks = count;
                        notifier.fire(e);
                    }
                    count = 0;
                });
            }
        });
    },
    ...
});</pre>


<p>Usage of this synthetic event then expects a third argument as a
configuration object with <code>minClicks</code> and <code>maxClicks</code> properties.</p>

<pre class="code prettyprint">node.on(&#x27;multiclick&#x27;, obj.method, {
    minClicks: 5,
    maxClicks: 8
}, obj);

&#x2F;&#x2F; extra args are supplied before the delegate filter
container.delegate(&#x27;multiclick&#x27;, doSomething, {
    minClicks: 3,
    maxClicks: 55
}, &#x27;.clickable&#x27;);</pre>


<h2 id="a-tip-to-make-your-synth-definition-smaller">A Tip to Make Your Synth Definition Smaller</h2>

<p>If the only difference between your <code>on</code> and <code>delegate</code> definitions is which method is used to bind to the supporting events, then you can propably get away with defining <code>delegate</code> and aliasing it to <code>on</code> (and so with <code>detach</code> and <code>detachDelegate</code>).  See the
<a href="http://yuilibrary.com/yui/docs/api/files/event_js_hover.js.html">source for the "hover"
event</a> for an example of this approach.</p>
</div>
            </div>
        </div>

        <div class="yui3-u-1-4">
            <div class="sidebar">
                
                    <div id="toc" class="sidebox">
                        <div class="hd">
                            <h2 class="no-toc">Table of Contents</h2>
                        </div>

                        <div class="bd">
                            <ul class="toc">
<li>
<a href="#the-hooks">The hooks</a>
</li>
<li>
<a href="#subscriptions-and-notifiers">Subscriptions and Notifiers</a>
</li>
<li>
<a href="#delegation-support">Delegation support</a>
</li>
<li>
<a href="#extra-arguments">Extra Arguments</a>
</li>
<li>
<a href="#a-tip-to-make-your-synth-definition-smaller">A Tip to Make Your Synth Definition Smaller</a>
</li>
</ul>
                        </div>
                    </div>
                

                
                    <div class="sidebox">
                        <div class="hd">
                            <h2 class="no-toc">Examples</h2>
                        </div>

                        <div class="bd">
                            <ul class="examples">
                                
                                    
                                        <li data-description="Use the Event Utility to attach simple DOM event handlers.">
                                            <a href="basic-example.html">Simple DOM Events</a>
                                        </li>
                                    
                                
                                    
                                        <li data-description="Using the synthetic event API to create a DOM event that fires in response to arrow keys being pressed.">
                                            <a href="synth-example.html">Creating an Arrow Event for DOM Subscription</a>
                                        </li>
                                    
                                
                                    
                                        <li data-description="Supporting cross-device swipe gestures, using the event-move gesture events">
                                            <a href="swipe-example.html">Supporting A Swipe Left Gesture</a>
                                        </li>
                                    
                                
                                    
                                
                                    
                                
                                    
                                
                                    
                                
                                    
                                
                            </ul>
                        </div>
                    </div>
                

                
                    <div class="sidebox">
                        <div class="hd">
                            <h2 class="no-toc">Examples That Use This Component</h2>
                        </div>

                        <div class="bd">
                            <ul class="examples">
                                
                                    
                                
                                    
                                
                                    
                                
                                    
                                        <li data-description="Creating an accessible menu button using the Focus Manager Node Plugin, Event&#x27;s delegation support and mouseenter event, along with the Overlay widget and Node&#x27;s support for the WAI-ARIA Roles and States.">
                                            <a href="../node-focusmanager/node-focusmanager-button.html">Accessible Menu Button</a>
                                        </li>
                                    
                                
                                    
                                        <li data-description="Shows how to extend the base widget class, to create your own Widgets.">
                                            <a href="../widget/widget-extend.html">Extending the Base Widget Class</a>
                                        </li>
                                    
                                
                                    
                                        <li data-description="Example Photo Browser application.">
                                            <a href="../dd/photo-browser.html">Photo Browser</a>
                                        </li>
                                    
                                
                                    
                                        <li data-description="Portal style example using Drag &amp; Drop Event Bubbling and Animation.">
                                            <a href="../dd/portal-drag.html">Portal Style Example</a>
                                        </li>
                                    
                                
                                    
                                        <li data-description="Use IO to request data over HTTP.">
                                            <a href="../io/get.html">HTTP GET to request data</a>
                                        </li>
                                    
                                
                            </ul>
                        </div>
                    </div>
                
            </div>
        </div>
    </div>
</div>

<script src="../assets/vendor/prettify/prettify-min.js"></script>
<script>prettyPrint();</script>

<script>
YUI.Env.Tests = {
    examples: [],
    project: '../assets',
    assets: '../assets/event',
    name: 'event',
    title: 'Creating Synthetic Events',
    newWindow: '',
    auto:  false 
};
YUI.Env.Tests.examples.push('basic-example');
YUI.Env.Tests.examples.push('synth-example');
YUI.Env.Tests.examples.push('swipe-example');
YUI.Env.Tests.examples.push('node-focusmanager-button');
YUI.Env.Tests.examples.push('widget-extend');
YUI.Env.Tests.examples.push('photo-browser');
YUI.Env.Tests.examples.push('portal-drag');
YUI.Env.Tests.examples.push('get');

</script>
<script src="../assets/yui/test-runner.js"></script>



</body>
</html>