如何在 Javascript 中向非 dom 元素添加事件监听器?
2011-06-03
2901
在 Java、C#、Actionscript 等中,事件适用于类,而在 Javascript 中,它似乎仅限于 dom。我在这里读到了一个使用 jQuery 实现的示例 http://www.west-wind.com/weblog/posts/2010/May/27/NonDom-Element-Event-Binding-with-jQuery
但如果我不需要 jQuery,并且我想了解该机制,你会怎么做?
2个回答
最简单的机制是这样的:
function PubSub() {
this.subs = {};
this.subscribe = function(channel, sub) {
this.subs[channel] = this.subs[channel] || []; //create array for channel
this.subs[channel].push(sub);
};
this.publish = function(channel) {
var args = [].slice.call(arguments, 1); //pop off channel argument
this.subs[channel].forEach(function(sub) {
sub.apply(void 0, args); //call each method listening on the channel
});
};
}
演示在这里: http://jsfiddle.net/3PNtR/
-- 编辑(约 5 年后)--
同样简单的机制,更新的语法
class PubSub {
constructor () {
this.subs = {}
}
subscribe (channel, sub) {
this.subs[channel] = this.subs[channel] || []
this.subs[channel].push(sub)
}
publish (channel, ...args) {
;(this.subs[channel] || []).forEach(sub => sub(...args))
}
}
dtudury
2013-09-20
至少有两种方法可以实现这一点:
-- 如果您是 JQuery 用户(目前我不是),您可以使用 JQuery 包装您的简单 JavaScript 对象,然后像 DOM 元素一样,它可以监听事件。此方法已经有 答案 这里 。我不确定这种方法在底层是如何工作的,我打算稍后调查,您可以自己调查。
-- 使用 VanillaJS,您可以通过创建一个事件丰富的“类”来实现相同的效果,所有愿意交互的对象都将从该类中派生/创建。然后该类的所有实例将能够注册、发出和广播事件。借用 DOM API 和 AngularJS 的语义,我编写了一个示例来演示如何实现这一点以及如何使用它。它在这里:
/**
* EventfulObject constructor/base.
* @type EventfulObject_L7.EventfulObjectConstructor|Function
*/
var EventfulObject = function() {
/**
* Map from event name to a list of subscribers.
* @type Object
*/
var event = {};
/**
* List of all instances of the EventfulObject type.
* @type Array
*/
var instances = [];
/**
* @returns {EventfulObject_L1.EventfulObjectConstructor} An `EventfulObject`.
*/
var EventfulObjectConstructor = function() {
instances.push(this);
};
EventfulObjectConstructor.prototype = {
/**
* Broadcasts an event of the given name.
* All instances that wish to receive a broadcast must implement the `receiveBroadcast` method, the event that is being broadcast will be passed to the implementation.
* @param {String} name Event name.
* @returns {undefined}
*/
broadcast: function(name) {
instances.forEach(function(instance) {
(instance.hasOwnProperty("receiveBroadcast") && typeof instance["receiveBroadcast"] === "function") &&
instance["receiveBroadcast"](name);
});
},
/**
* Emits an event of the given name only to instances that are subscribed to it.
* @param {String} name Event name.
* @returns {undefined}
*/
emit: function(name) {
event.hasOwnProperty(name) && event[name].forEach(function(subscription) {
subscription.process.call(subscription.context);
});
},
/**
* Registers the given action as a listener to the named event.
* This method will first create an event identified by the given name if one does not exist already.
* @param {String} name Event name.
* @param {Function} action Listener.
* @returns {Function} A deregistration function for this listener.
*/
on: function(name, action) {
event.hasOwnProperty(name) || (event[name] = []);
event[name].push({
context: this,
process: action
});
var subscriptionIndex = event[name].length - 1;
return function() {
event[name].splice(subscriptionIndex, 1);
};
}
};
return EventfulObjectConstructor;
}();
var Model = function(id) {
EventfulObject.call(this);
this.id = id;
this.receiveBroadcast = function(name) {
console.log("I smell another " + name + "; and I'm model " + this.id);
};
};
Model.prototype = Object.create(EventfulObject.prototype);
Model.prototype.constructor = Model;
// ---------- TEST AND USAGE (hopefully it's clear enough...)
// ---------- note: I'm not testing event deregistration.
var ob1 = new EventfulObject();
ob1.on("crap", function() {
console.log("Speaking about craps on a broadcast? - Count me out!");
});
var model1 = new Model(1);
var model2 = new Model(2);
model2.on("bust", function() {
console.log("I'm model2 and I'm busting!");
});
var ob2 = new EventfulObject();
ob2.on("bust", function() {
console.log("I'm ob2 - busted!!!");
});
ob2.receiveBroadcast = function() {
console.log("If it zips, I'll catch it. - That's me ob2.");
};
console.log("start:BROADCAST\n---------------");
model1.broadcast("crap");
console.log("end :BROADCAST\n---------------\n-\n-\n");
console.log("start:EMIT\n---------------");
ob1.emit("bust");
console.log("end:EMIT\n---------------");
<h1>THE CODE IS IN the JavaScript pane!</h1>
<h3>AND... THE SHOW IS ON YOUR CONSOLE!</h3>
您可以将其用作更强大的解决方案的基础。
Igwe Kalu
2014-10-08