web-dev-qa-db-fra.com

Modèle d'événements personnalisé sans utiliser les événements DOM en JavaScript

Je suis nouveau dans JavaScript et la programmation en général, et j'ai des questions sur les objets et les événements.

Disons que j'ai un objet:

var computer = {
    keyboard: {}
}

Ce que je recherche, c'est un moyen d'enregistrer des événements sur l'objet clavier:

computer.keyboard.registerEvent( "keyEscape" );

Déclenchez l'événement:

computer.keyboard.dispatchEvent( "keyEscape" );

Et créez des gestionnaires d'événements:

computer.keyboard.addEventListener( "keyEscape", function() {...} );

Je sais comment faire cela avec des éléments DOM mais pas avec des objets. Est-ce quelque chose qui peut être fait en JavaScript (peut-être avec l'aide de JQuery)?

Même le moindre conseil serait grandement apprécié.

45
NTDave

Si vous voulez créer un système d'événements complètement autonome sans compter sur les événements DOM, vous pouvez avoir quelque chose comme ça en utilisant le modèle de réacteur

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);
};

Utilisez-le comme modèle d'événements 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');

en direct sur JSBin

52
Mohsen

Si vous ne voulez pas implémenter vos propres mécanismes de gestion d'événements, vous aimerez peut-être mon approche. Vous obtiendrez toutes les fonctionnalités que vous connaissez des événements DOM habituels (preventDefault () par exemple) et je pense qu'il est plus léger, car il utilise les capacités de gestion des événements DOM déjà implémentées du navigateur.

Créez simplement un objet DOM EventTarget normal dans le constructeur de votre objet et passez tous les appels d'interface EventTarget à l'objet 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);

Il y a aussi un extrait JSFiddle pour le tester avec votre navigateur.

38
Torben

Necroposting un peu ici, mais je viens d'écrire quelque chose comme ça hier soir - super simple, et basé sur le module d'événements 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);
        });
    }
};

Cette approche est incroyablement simple et extensible, vous permettant de créer un système d'événements plus sophistiqué si vous en avez besoin.

L'utilisation de EventDispatcher est aussi simple que:

function initializeListeners() {
    EventDispatcher.on('fire', fire); // fire.bind(this) -- if necessary
}

function fire(x) {
    console.log(x);
}

function thingHappened(thing) {
    EventDispatcher.trigger('fire', thing);
}

Avec un simple espace de noms, vous pourrez facilement passer des événements de base entre les modules!

12
Jordan Foreman

Vous pouvez le faire en utilisant JQuery.

Pour vous abonner à votre événement personnalisé:

$(computer.keyboard).on('keyEscape', function(e){
    //Handler code
});

Pour lancer votre événement personnalisé:

$(computer.keyboard).trigger('keyEscape', {keyCode:'Blah blah'});

Ce n'est peut-être pas la meilleure façon de le faire, mais vous pouvez également créer des fonctions dans votre méthode (addEventListener, dispatchEvent, ...) qui encapsuleront la logique JQuery, pour prendre en charge à la fois une API native et JQuery.

4
Philipp Munin

Très probablement, vous avez besoin d'un mécanisme d'événement comme moyen de communication entre plusieurs objets.

Voici comment vous pouvez y parvenir:

/**
 * 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>
0
Igwe Kalu