tablas - Modelo de eventos personalizado sin usar eventos DOM en JavaScript
onclick html5 (6)
Soy nuevo en JavaScript y en la programación en general, y tengo algunas preguntas sobre objetos y eventos.
Digamos que tengo un objeto:
var computer = {
keyboard: {}
}
Lo que estoy buscando es una forma de registrar eventos en el objeto del teclado:
computer.keyboard.registerEvent( "keyEscape" );
Dispara el evento:
computer.keyboard.dispatchEvent( "keyEscape" );
Y crea controladores de eventos:
computer.keyboard.addEventListener( "keyEscape", function() {...} );
Sé cómo hacer esto con elementos DOM pero no con objetos. ¿Es esto algo que se puede hacer en JavaScript (quizás con la ayuda de JQuery)?
Incluso la más mínima guía sería apreciada en gran medida.
Aquí hay una extensión simple de share de share , presentada como un claro y breve ejemplo.
Todas sus funciones React se encapsulan en un React()
, se agrega una función removeEventListener()
, y todo el ejemplo se presenta como un archivo HTML (o lo ve en JSFiddle ).
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>JS Bin</title>
<!--https://jsfiddle.net/romleon/qs26o3p8/-->
</head>
<body>
<script>
function Reactor() {
function Event(name) {
this.name = name;
this.callbacks = [];
}
Event.prototype.registerCallback = function(callback) {
this.callbacks.push(callback);
};
Event.prototype.unregisterCallback = function(callback) {
var array = this.callbacks,
index = array.indexOf(callback);
if (index > -1)
array.splice(index, 1);
}
this.events = {};
this.registerEvent = function(eventName) {
var event = new Event(eventName);
this.events[eventName] = event;
};
this.dispatchEvent = function(eventName, eventArgs) {
var events = this.events
if (events[eventName]) {
events[eventName].callbacks.forEach(function(callback) {
callback(eventArgs);
});
}
else
console.error("WARNING: can''t dispatch " + ''"'' + eventName + ''"'')
};
this.addEventListener = function(eventName, callback) {
this.events[eventName].registerCallback(callback);
};
this.removeEventListener = function(eventName, callback) {
var events = this.events
if (events[eventName]) {
events[eventName].unregisterCallback(callback);
delete events[eventName];
}
else
console.error("ERROR: can''t delete " + ''"'' + eventName + ''"'')
};
}
/*
demo of creating
*/
var reactor = new Reactor();
reactor.registerEvent(''big bang'');
reactor.registerEvent(''second bang'');
/*
demo of using
*/
log("-- add 2 event''s listeners for ''big bang'' and 1 for ''second bang''")
var callback1 = function() {
log(''This is big bang listener'')
}
reactor.addEventListener(''big bang'', callback1);
reactor.addEventListener(''big bang'', function() {
log(''This is another big bang listener'')
});
reactor.addEventListener(''second bang'', function() {
log(''This is second bang!'')
});
log("-- dipatch ''big bang'' and ''second bang''")
reactor.dispatchEvent(''big bang'');
reactor.dispatchEvent(''second bang'');
log("-- remove first listener (with callback1)")
reactor.removeEventListener(''big bang'', callback1);
log("-- dipatch ''big bang'' and ''second bang'' again")
reactor.dispatchEvent(''big bang'');
reactor.dispatchEvent(''second bang'');
function log(txt) {
document.body.innerHTML += txt + ''<br/>''
console.log(txt)
}
</script>
</body>
</html>
Lo más probable es que necesite un mecanismo de evento como medio de comunicación entre varios objetos.
Aquí es cómo puedes lograr eso:
/**
* 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 SHOW IS ON YOUR CONSOLE!</h1>
Necroponiendo un poco aquí, pero acabo de escribir algo como esto anoche: muy simple y basado en el módulo de eventos Backbone.js:
EventDispatcher = {
events: {},
on: function(event, callback) {
var handlers = this.events[event] || [];
handlers.push(callback);
this.events[event] = handlers;
},
trigger: function(event, data) {
var handlers = this.events[event];
if (!handlers || handlers.length < 1)
return;
[].forEach.call(handlers, function(handler){
handler(data);
});
}
};
Este enfoque es increíblemente simple y extensible, lo que le permite construir un sistema de eventos más sofisticado además si lo necesita.
Usar EventDispatcher
es tan simple como:
function initializeListeners() {
EventDispatcher.on(''fire'', fire); // fire.bind(this) -- if necessary
}
function fire(x) {
console.log(x);
}
function thingHappened(thing) {
EventDispatcher.trigger(''fire'', thing);
}
Con algunos espacios de nombres simples, ¡podrás pasar eventos básicos entre módulos con facilidad!
Puedes hacerlo usando JQuery.
Para suscribirse a su evento personalizado:
$(computer.keyboard).on(''keyEscape'', function(e){
//Handler code
});
Para lanzar tu evento personalizado:
$(computer.keyboard).trigger(''keyEscape'', {keyCode:''Blah blah''});
Puede que no sea la mejor manera de hacerlo, pero también puede crear funciones en su método (addEventListener, dispatchEvent, ...) que envolverán la lógica de JQuery, para admitir api de aspecto nativo y JQuery.
Si desea crear un sistema de eventos completamente independiente sin depender de los eventos DOM, puede tener algo como esto usando un patrón de reactor
function Event(name){
this.name = name;
this.callbacks = [];
}
Event.prototype.registerCallback = function(callback){
this.callbacks.push(callback);
}
function Reactor(){
this.events = {};
}
Reactor.prototype.registerEvent = function(eventName){
var event = new Event(eventName);
this.events[eventName] = event;
};
Reactor.prototype.dispatchEvent = function(eventName, eventArgs){
this.events[eventName].callbacks.forEach(function(callback){
callback(eventArgs);
});
};
Reactor.prototype.addEventListener = function(eventName, callback){
this.events[eventName].registerCallback(callback);
};
Úselo como modelo de eventos DOM
var reactor = new Reactor();
reactor.registerEvent(''big bang'');
reactor.addEventListener(''big bang'', function(){
console.log(''This is big bang listener yo!'');
});
reactor.addEventListener(''big bang'', function(){
console.log(''This is another big bang listener yo!'');
});
reactor.dispatchEvent(''big bang'');
Si no desea implementar sus propios mecanismos de manejo de eventos, es posible que le guste mi enfoque. Obtendrás todas las características que conoces de los eventos DOM habituales (preventDefault () por ejemplo) y creo que es más liviano, porque usa las capacidades de manejo de eventos DOM ya implementadas del navegador.
Simplemente cree un objeto DOM EventTarget normal en el constructor de su objeto y pase todas las llamadas a la interfaz EventTarget al objeto DOM EventTarget:
var MyEventTarget = function(options) {
// Create a DOM EventTarget object
var target = document.createTextNode(null);
// Pass EventTarget interface calls to DOM EventTarget object
this.addEventListener = target.addEventListener.bind(target);
this.removeEventListener = target.removeEventListener.bind(target);
this.dispatchEvent = target.dispatchEvent.bind(target);
// Room your your constructor code
}
// Create an instance of your event target
myTarget = new MyEventTarget();
// Add an event listener to your event target
myTarget.addEventListener("myevent", function(){alert("hello")});
// Dispatch an event from your event target
var evt = new Event(''myevent'');
myTarget.dispatchEvent(evt);
También hay un fragmento de JSFiddle para probarlo con su navegador.