# Yahoo! UI Library

### event 3.3.0

Yahoo! UI Library > event > focusblur.js (source view)
Search:
Filters Show Private Show Protected Show Deprecated

    /**
     * Adds bubbling and delegation support to DOM events focus and blur.
     *
     * @module event
     * @submodule event-focus
     */
    var Event    = Y.Event,
        YLang    = Y.Lang,
        isString = YLang.isString,
        useActivate = YLang.isFunction(
            Y.DOM.create('<p onbeforeactivate=";"/>').onbeforeactivate);

    function define(type, proxy, directEvent) {
        var nodeDataKey = '_' + type + 'Notifiers';

        Y.Event.define(type, {

            _attach: function (el, notifier, delegate) {
                if (Y.DOM.isWindow(el)) {
                    return Event._attach([type, function (e) {
                        notifier.fire(e);
                    }, el]);
                } else {
                    return Event._attach(
                        [proxy, this._proxy, el, this, notifier, delegate],
                        { capture: true });
                }
            },

            _proxy: function (e, notifier, delegate) {
                var node       = e.target,
                    notifiers  = node.getData(nodeDataKey),
                    yuid       = Y.stamp(e.currentTarget._node),
                    defer      = (useActivate || e.target !== e.currentTarget),
                    sub        = notifier.handle.sub,
                    filterArgs = [node, e].concat(sub.args || []),
                    directSub;

                notifier.currentTarget = (delegate) ? node : e.currentTarget;
                notifier.container     = (delegate) ? e.currentTarget : null;

                if (!sub.filter || sub.filter.apply(node, filterArgs)) {
                    // Maintain a list to handle subscriptions from nested
                    // containers div#a>div#b>input #a.on(focus..) #b.on(focus..),
                    // use one focus or blur subscription that fires notifiers from
                    // #b then #a to emulate bubble sequence.
                    if (!notifiers) {
                        notifiers = {};
                        node.setData(nodeDataKey, notifiers);

                        // only subscribe to the element's focus if the target is
                        // not the current target (
                        if (defer) {
                            directSub = Event._attach(
                                [directEvent, this._notify, node._node]).sub;
                            directSub.once = true;
                        }
                    }

                    if (!notifiers[yuid]) {
                        notifiers[yuid] = [];
                    }

                    notifiers[yuid].push(notifier);

                    if (!defer) {
                        this._notify(e);
                    }
                }
            },

            _notify: function (e, container) {
                var node        = e.currentTarget,
                    notifiers   = node.getData(nodeDataKey),
                                  // document.get('ownerDocument') returns null
                    doc         = node.get('ownerDocument') || node,
                    target      = node,
                    nots        = [],
                    notifier, i, len;

                if (notifiers) {
                    // Walk up the parent axis until the origin node,
                    while (target && target !== doc) {
                        nots.push.apply(nots, notifiers[Y.stamp(target)] || []);
                        target = target.get('parentNode');
                    }
                    nots.push.apply(nots, notifiers[Y.stamp(doc)] || []);

                    for (i = 0, len = nots.length; i < len; ++i) {
                        notifier = nots[i];
                        e.currentTarget = nots[i].currentTarget;

                        if (notifier.container) {
                            e.container = notifier.container;
                        } else {
                            delete e.container;
                        }

                        notifier.fire(e);
                    }

                    // clear the notifications list (mainly for delegation)
                    node.clearData(nodeDataKey);
                }
            },

            on: function (node, sub, notifier) {
                sub.onHandle = this._attach(node._node, notifier);
            },

            detach: function (node, sub) {
                sub.onHandle.detach();
            },

            delegate: function (node, sub, notifier, filter) {
                if (isString(filter)) {
                    sub.filter = Y.delegate.compileFilter(filter);
                }

                sub.delegateHandle = this._attach(node._node, notifier, true);
            },

            detachDelegate: function (node, sub) {
                sub.delegateHandle.detach();
            }
        }, true);
    }

    // For IE, we need to defer to focusin rather than focus because
    // `el.focus(); doSomething();` executes el.onbeforeactivate, el.onactivate,
    // el.onfocusin, doSomething, then el.onfocus.  All others support capture
    // phase focus, which executes before doSomething.  To guarantee consistent
    // behavior for this use case, IE's direct subscriptions are made against
    // focusin so subscribers will be notified before js following el.focus() is
    // executed.
    if (useActivate) {
        //     name     capture phase       direct subscription
        define("focus", "beforeactivate",   "focusin");
        define("blur",  "beforedeactivate", "focusout");
    } else {
        define("focus", "focus", "focus");
        define("blur",  "blur",  "blur");
    }

Copyright © 2011 Yahoo! Inc. All rights reserved.