jQuery将事件处理程序挂接到正在触发的对象的方法

jQuery hook up an event handler to an object's method being fired

本文关键字:对象 方法 事件处理 程序 jQuery      更新时间:2023-09-26
var obj = { method: function(){alert("hello world"); }
$(obj).on("method", function(){ alert("the method was invoked"); });
$(obj).bind("method", function(){ alert("the method was invoked"); });

我正在尝试执行上述操作,但它似乎仅适用于用户事件,例如单击、鼠标悬停等。

我来自道场背景,您可以执行以下操作:

dojo.connect(obj, "method", function(){ alert("the method was invoked"); });

更具体地说,我正在尝试将事件处理程序绑定到在自定义 jQuery 小部件上调用的方法。

你可以

使用 jQuery 的$.proxy来做到这一点:

$(obj).on("eventName", $.proxy(obj.method, obj));
// or
$(obj).on("eventName", $.proxy(obj, "method")); // Note the quotes

如果你可以依赖 ECMAScript5 的特性(要么是因为你所有的目标浏览器都有 ES5,要么是因为你已经加载了 ES5 shim/shiv),你可以使用 Function#bind(因为它是一个支持填充码/shiv 的特性):

$(obj).on("eventName", obj.method.bind(obj));

当然,在任何一种情况下,方法调用中的this将不再是您挂钩事件的元素(它将obj)。您应该接受方法中的 event 参数(例如,e ),然后使用 e.target(对于事件发起的元素)或e.delegateTarget(对于挂钩事件的元素,可能是 e.target 的祖先)。

这并不能完全回答我关于绑定到纯 JavaScript 对象的方法调用的原始问题,但我最终选择了:

_method: function(){
    this.element.trigger("customEventName");
}
//...elsewhere
$("#WidgetNode").bind("customEventName", function(){ alert("invoked!"); });

而不是我最初尝试的:

_method: function(){ 
    this.onMethodInvoked();
},
onMethodInvoked: function(){ 
    //stub for listening
}

对于那些好奇道场如何做到这一点的人...

此代码允许您使用 dojo.connect 将处理程序连接到任何 javascript 对象上任何方法的调用

// this file courtesy of the TurboAjax Group, licensed under a Dojo CLA
// low-level delegation machinery
dojo._listener = {
    // create a dispatcher function
    getDispatcher: function(){
        // following comments pulled out-of-line to prevent cloning them 
        // in the returned function.
        // - indices (i) that are really in the array of listeners (ls) will 
        //   not be in Array.prototype. This is the 'sparse array' trick
        //   that keeps us safe from libs that take liberties with built-in 
        //   objects
        // - listener is invoked with current scope (this)
        return function(){
            var ap=Array.prototype, c=arguments.callee, ls=c._listeners, t=c.target;
            // return value comes from original target function
            var r = t && t.apply(this, arguments);
            // make local copy of listener array so it is immutable during processing
            var lls;
            //>>includeStart("connectRhino", kwArgs.profileProperties.hostenvType == "rhino");
            if(!dojo.isRhino){
            //>>includeEnd("connectRhino");
                //>>includeStart("connectBrowser", kwArgs.profileProperties.hostenvType != "rhino");
                lls = [].concat(ls);
                //>>includeEnd("connectBrowser");
            //>>includeStart("connectRhino", kwArgs.profileProperties.hostenvType == "rhino");
            }else{
                // FIXME: in Rhino, using concat on a sparse Array results in a dense Array.
                // IOW, if an array A has elements [0, 2, 4], then under Rhino, "concat [].A"
                // results in [0, 1, 2, 3, 4], where element 1 and 3 have value 'undefined'
                // "A.slice(0)" has the same behavior.
                lls = [];
                for(var i in ls){
                    lls[i] = ls[i];
                }
            }
            //>>includeEnd("connectRhino");
            // invoke listeners after target function
            for(var i in lls){
                if(!(i in ap)){
                    lls[i].apply(this, arguments);
                }
            }
            // return value comes from original target function
            return r;
        }
    },
    // add a listener to an object
    add: function(/*Object*/ source, /*String*/ method, /*Function*/ listener){
        // Whenever 'method' is invoked, 'listener' will have the same scope.
        // Trying to supporting a context object for the listener led to 
        // complexity. 
        // Non trivial to provide 'once' functionality here
        // because listener could be the result of a dojo.hitch call,
        // in which case two references to the same hitch target would not
        // be equivalent. 
        source = source || dojo.global;
        // The source method is either null, a dispatcher, or some other function
        var f = source[method];
        // Ensure a dispatcher
        if(!f||!f._listeners){
            var d = dojo._listener.getDispatcher();
            // original target function is special
            d.target = f;
            // dispatcher holds a list of listeners
            d._listeners = []; 
            // redirect source to dispatcher
            f = source[method] = d;
        }
        // The contract is that a handle is returned that can 
        // identify this listener for disconnect. 
        //
        // The type of the handle is private. Here is it implemented as Integer. 
        // DOM event code has this same contract but handle is Function 
        // in non-IE browsers.
        //
        // We could have separate lists of before and after listeners.
        return f._listeners.push(listener) ; /*Handle*/
    },
    // remove a listener from an object
    remove: function(/*Object*/ source, /*String*/ method, /*Handle*/ handle){
        var f = (source||dojo.global)[method];
        // remember that handle is the index+1 (0 is not a valid handle)
        if(f && f._listeners && handle--){
            delete f._listeners[handle];
        }
    }
};
// Multiple delegation for arbitrary methods.
// This unit knows nothing about DOM, 
// but we include DOM aware 
// documentation and dontFix
// argument here to help the autodocs.
// Actual DOM aware code is in event.js.
dojo.connect = function(/*Object|null*/ obj, 
                        /*String*/ event, 
                        /*Object|null*/ context, 
                        /*String|Function*/ method,
                        /*Boolean*/ dontFix){
    // summary:
    //      Create a link that calls one function when another executes. 
    //
    // description:
    //      Connects method to event, so that after event fires, method
    //      does too. All connected functions are passed the same arguments as
    //      the event function was initially called with. You may connect as
    //      many methods to event as needed.
    //
    //      event must be a string. If obj is null, dojo.global is used.
    //
    //      null arguments may simply be omitted.
    //
    //      obj[event] can resolve to a function or undefined (null). 
    //      If obj[event] is null, it is assigned a function.
    //
    //      The return value is a handle that is needed to 
    //      remove this connection with dojo.disconnect.
    //
    // obj: 
    //      The source object for the event function. 
    //      Defaults to dojo.global if null.
    //      If obj is a DOM node, the connection is delegated 
    //      to the DOM event manager (unless dontFix is true).
    //
    // event:
    //      String name of the event function in obj. 
    //      I.e. identifies a property obj[event].
    //
    // context: 
    //      The object that method will receive as "this".
    //
    //      If context is null and method is a function, then method
    //      inherits the context of event.
    //  
    //      If method is a string then context must be the source 
    //      object object for method (context[method]). If context is null,
    //      dojo.global is used.
    //
    // method:
    //      A function reference, or name of a function in context. 
    //      The function identified by method fires after event does. 
    //      method receives the same arguments as the event.
    //      See context argument comments for information on method's scope.
    //
    // dontFix:
    //      If obj is a DOM node, set dontFix to true to prevent delegation 
    //      of this connection to the DOM event manager. 
    //
    // example:
    //      When obj.onchange(), do ui.update():
    //  |   dojo.connect(obj, "onchange", ui, "update");
    //  |   dojo.connect(obj, "onchange", ui, ui.update); // same
    //
    // example:
    //      Using return value for disconnect:
    //  |   var link = dojo.connect(obj, "onchange", ui, "update");
    //  |   ...
    //  |   dojo.disconnect(link);
    //
    // example:
    //      When onglobalevent executes, watcher.handler is invoked:
    //  |   dojo.connect(null, "onglobalevent", watcher, "handler");
    //
    // example:
    //      When ob.onCustomEvent executes, customEventHandler is invoked:
    //  |   dojo.connect(ob, "onCustomEvent", null, "customEventHandler");
    //  |   dojo.connect(ob, "onCustomEvent", "customEventHandler"); // same
    //
    // example:
    //      When ob.onCustomEvent executes, customEventHandler is invoked
    //      with the same scope (this):
    //  |   dojo.connect(ob, "onCustomEvent", null, customEventHandler);
    //  |   dojo.connect(ob, "onCustomEvent", customEventHandler); // same
    //
    // example:
    //      When globalEvent executes, globalHandler is invoked
    //      with the same scope (this):
    //  |   dojo.connect(null, "globalEvent", null, globalHandler);
    //  |   dojo.connect("globalEvent", globalHandler); // same
    // normalize arguments
    var a=arguments, args=[], i=0;
    // if a[0] is a String, obj was ommited
    args.push(dojo.isString(a[0]) ? null : a[i++], a[i++]);
    // if the arg-after-next is a String or Function, context was NOT omitted
    var a1 = a[i+1];
    args.push(dojo.isString(a1)||dojo.isFunction(a1) ? a[i++] : null, a[i++]);
    // absorb any additional arguments
    for(var l=a.length; i<l; i++){  args.push(a[i]); }
    // do the actual work
    return dojo._connect.apply(this, args); /*Handle*/
}
// used by non-browser hostenvs. always overriden by event.js
dojo._connect = function(obj, event, context, method){
    var l=dojo._listener, h=l.add(obj, event, dojo.hitch(context, method)); 
    return [obj, event, h, l]; // Handle
}
dojo.disconnect = function(/*Handle*/ handle){
    // summary:
    //      Remove a link created by dojo.connect.
    // description:
    //      Removes the connection between event and the method referenced by handle.
    // handle:
    //      the return value of the dojo.connect call that created the connection.
    if(handle && handle[0] !== undefined){
        dojo._disconnect.apply(this, handle);
        // let's not keep this reference
        delete handle[0];
    }
}
dojo._disconnect = function(obj, event, handle, listener){
    listener.remove(obj, event, handle);
}