addEventListener sur l'objet personnalisé

j'ai créé un objet qui a plusieurs méthodes. Certaines de ces méthodes sont asynchrones et je veux donc utiliser des événements pour être en mesure de réaliser des actions lorsque les méthodes sont faites. Pour ce faire, j'ai essayé d'ajouter le addEventListener à l'objet.

jsfiddle

var iSubmit = {
    addEventListener: document.addEventListener || document.attachEvent,
    dispatchEvent: document.dispatchEvent,
    fireEvent: document.fireEvent,   


    //the method below is added for completeness, but is not causing the problem.

    test: function(memo) {
        var name = "test";
        var event;
        if (document.createEvent) {
            event = document.createEvent("HTMLEvents");
            event.initEvent(name, true, true);
        } else {
            event = document.createEventObject();
            event.eventType = name;
        }
        event.eventName = name;
        event.memo = memo || { };

        if (document.createEvent) {
            try {
                document.dispatchEvent(event);
            } catch (ex) {
                iAlert.debug(ex, 'iPushError');
            }
        } else {
            document.fireEvent("on" + event.eventType, event);
        }
    }
}

iSubmit.addEventListener("test", function(e) { console.log(e); }, false);


//This call is added to have a complete test. The errors is already triggered with the line before this one.

iSubmit.test();

cela retournera une erreur: Failed to add eventlisterens: TypeError: 'addEventListener' called on an object that does not implement interface EventTarget."

maintenant ce code sera utilisé dans une application phonegap et quand je le fais, il fonctionne sur android / ios. Pendant les tests, cependant, il serait bien si je pouvais le faire fonctionner dans au moins un seul navigateur.

PS> je sais que je pourrait permettre de bouillonnement et d'écouter de la racine du document, mais je voudrais avoir juste un peu de la programmation orientée objet où chaque objet peut travailler sur son propre.

46
demandé sur Eleanor Zimmermann 2013-12-30 12:13:54

9 réponses

addEventListener est destiné aux éléments DOM qui implémentent certaines interfaces liées aux événements. Si vous voulez un système d'événements sur JavaScript et des objets, vous êtes à la recherche d'un système d'événements. Un exemple serait Backbone.Events dans la colonne vertébrale.js. L'idée de base est d'utiliser un objet comme hachage pour garder une trace des callbacks enregistrés.

personnellement j'utilise ceci: https://github.com/component/emitter

c'est un assez solution simple et élégante - avec des noms de méthode courts comme on() , off() et emit() . vous pouvez soit créer de nouvelles instances avec new Emitter() , soit utiliser Emitter(obj) pour mélanger des capacités d'événements dans des objets existants. Notez que cette bibliothèque est écrite pour être utilisée avec un système de modules CommonJS, mais vous pouvez l'utiliser n'importe où ailleurs en supprimant la ligne module.exports = ... .

39
répondu Evan You 2014-01-03 20:56:52

si vous voulez écouter un objet javascript, vous avez trois façons:

à Propos de sup/pub motif:

vous devez publier les événements.

à propos des implémentations natives:

  • Object get/set operators est suffisant pour écouter ajouter, supprimer, modifier, obtenez des événements. Les opérateurs ont bon de soutien . Problèmes seulement dans IE8-. Mais si vous voulez utiliser get/set dans IE8 utilisez Object.defineProperty mais sur les objets DOM ou utiliser L'objet.defineProperty l'imposture .
  • Object.prototype.watch a la bonne ES5 polyfill .
  • Proxy API besoins ES Harmonie de soutien.

objet.observer l'exemple

var o = {};
Object.observe(o, function (changes) {
  changes.forEach(function (change) {
    // change.object contains changed object version
    console.log('property:', change.name, 'type:', change.type);
  });
});
o.x = 1     // property: x type: add
o.x = 2     // property: x type: update
delete o.x  // property: x type: delete
14
répondu Pinal 2017-01-28 12:50:32

si vous n'avez pas besoin de fonctions d'événements réels(telles que bubbling, stopPropagation), alors vous pouvez mettre en œuvre vos propres événements. addEventListener est juste une API du DOM, donc vous n'en avez pas vraiment besoin pour vos propres objets en dehors du DOM. Si vous voulez créer un motif Pair autour d'un objet, voici une bonne façon de le faire qui ne nécessite pas D'API de navigateur supplémentaire et devrait être très rétro-compatible.

disons que vous avez un objet où vous voulez un tas de événements à déclencher lorsque la méthode d'expédition est appelée:

var OurDispatcher, dispatcher;

OurDispatcher = (function() {
  function OurDispatcher() {
    this.dispatchHandlers = [];
  }

  OurDispatcher.prototype.on = function(eventName, handler) {
    switch (eventName) {
      case "dispatch":
        return this.dispatchHandlers.push(handler);
      case "somethingElse":
        return alert('write something for this event :)');
    }
  };

  OurDispatcher.prototype.dispatch = function() {
    var handler, i, len, ref;
    ref = this.dispatchHandlers;
    for (i = 0, len = ref.length; i < len; i++) {
      handler = ref[i];
      setTimeout(handler, 0);
    }
  };

  return OurDispatcher;

})();

dispatcher = new OurDispatcher();

dispatcher.on("dispatch", function() {
  return document.body.innerHTML += "DISPATCHED</br>";
});

dispatcher.on("dispatch", function() {
  return document.body.innerHTML += "DISPATCHED AGAIN</br>";
});

dispatcher.dispatch();

il n'a vraiment pas à être plus compliqué que cela, pour la plupart. De cette façon, vous avez un certain contrôle décent sur vos événements et vous n'avez pas besoin de vous soucier de rétrocompatibilité ou bibliothèques externes parce que tout ce qu'il y a est largement soutenu. Techniquement, vous pouvez même vous passer de setTimeout et gérer vos callbacks sans APIs. Toute autre chose comme stopPropagation () doit être traité vous-même.

https://jsfiddle.net/ozsywxer /

il y a, bien sûr, des polyfills pour CustomEvent, mais à moins que j'ai besoin de fonctionnalités avancées d'événement, je préfère envelopper mon propre système de concours complet dans une" classe " et d'étendre d'autres classes/fonctions avec elle.

Voici la version CoffeeScript, qui est ce que le JavaScript est dérivé de: https://jsfiddle.net/vmkkbbxq/1 /

^^ Un peu plus facile à comprendre.

12
répondu Ravenstine 2015-06-30 19:12:53

Il y a deux problèmes.

tout D'abord, la méthode iSubmit.addEventListener() est en fait une méthode sur EventTarget interface DOM:

ceux-ci sont destinés à être utilisés uniquement sur les éléments DOM. En l'ajoutant à l'objet iSubmit comme méthode, vous l'appelez sur un objet qui n'est pas un EventTarget . C'est pourquoi Chrome lance une erreur Uncaught TypeError: Illegal invocation JavaScript.

le premier problème est critique, mais si vous pouviez utiliser EventTarget#addEventListener() votre code ne fonctionnerait pas parce que l'événement est ajouté à iSubmit mais expédié à partir de document . Généralement, les méthodes du même objet doivent être utilisées lors de la fixation des écouteurs d'événements et de l'envoi d'événements (à moins que vous n'utilisiez des événements de barbotage, qui est un histoire différente - Note: le barbotage n'est pas limité aux événements JavaScript ou DOM, par exemple ).

utiliser des événements personnalisés avec vos propres objets est très normal. Comme Evan Yu a mentionné , il y a des bibliothèques pour cela. Voici un couple:

j'ai utilisé js-signals et je l'aime beaucoup. Je n'ai jamais utilisé Wolfy87/EventEmitter , mais il a un bel aspect.

votre exemple pourrait ressembler à ce qui suit si vous utilisez js-signals

jsFiddle

var iSubmit = {
    finished: new signals.Signal(),
    test: function test(memo) {
        this.finished.dispatch(memo || {});
    }
};

iSubmit.finished.add(function(data) {
    console.log('finished:', data);
});

iSubmit.test('this is the finished data');


// alternatively
iSubmit.finished.dispatch('this is dispatched directly from the signal');
10
répondu tiffon 2017-05-23 12:26:15

si vous êtes dans un noeud.l'environnement js alors vous pouvez utiliser la classe EventEmitter de Noeud :

CustomObject.js

const EventEmitter = require('events');

class CustomObject extends EventEmitter {
  constructor() {
    super();
  }

  doSomething() {
    const event = {message: 'Hello World!'};
    this.emit('myEventName', event);
  }
}

module.exports = CustomObject;

Utilisation:

const CustomObject = require('./CustomObject');

// 1. Create a new instance
const myObject = new CustomObject();

// 2. Subscribe to events with ID "myEventName"
myObject.on('myEventName', function(event) {
  console.log('Received event', event);
});

// 3. Trigger the event emitter
myObject.doSomething();

si vous voulez utiliser EventEmitter de noeud en dehors d'un noeud.js environnement, alors vous pouvez utiliser webpack (de préférence v2.2 ou plus) pour obtenir un paquet de votre CustomClass avec un EventEmitter polyfill (construit par webpack).

Voici comment cela fonctionne (en supposant que vous avez installé webpack globalement en utilisant npm install -g webpack ):

  1. Exécuter webpack CustomObject.js bundle.js --output-library=CustomObject
  2. incluez bundle.js dans votre page HTML (elle exposera window.CustomObject )
  3. il n'y a pas de troisième étape!

de l'index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Title</title>
    <script src="bundle.js"></script>
  </head>
  <body>
    <script>
      // 1. Create a new instance
      const myObject = new window.CustomObject();

      // 2. Subscribe to events with ID "myEventName"
      myObject.on('myEventName', function(event) {
        console.log('Received event', event);
      });

      // 3. Trigger the event emitter
      myObject.doSomething();
    </script>
  </body>
</html>
4
répondu Benny Neugebauer 2017-02-05 01:54:53

juste des spéculations; Je ne l'ai pas essayé moi-même. Mais vous pouvez créer un élément factice et tirer/écouter des événements sur l'élément factice. Aussi, je préfère aller sans bibliothèques.

function myObject(){
    //create "dummy" element
    var dummy = document.createElement('dummy');
    //method for listening for events
    this.on = function(event, func){dummy.addEventListener(event, func);};
    //you need a way to fire events
    this.fireEvent = function(event, obj){
      dummy.dispatchEvent(new CustomEvent(event, {detail: obj}));
    }
}
//now you can use the methods in the object constructor
var obj = new myObject();
obj.on("custom", function(e){console.log(e.detail.result)});
obj.fireEvent("custom", {result: "hello world!!!"});
2
répondu Rey 2017-06-03 01:08:41

cet article explique la création d'événements personnalisés: http://www.sitepoint.com/javascript-custom-events /

voici un exemple:

créer l'événement

var event = new CustomEvent(
    "newMessage",
    {
        detail: {
            message: "Hello World!",
            time: new Date(),
        },
        bubbles: true,
        cancelable: true
    }
);

assignez l'événement à quelque chose -

document.getElementById("msgbox").dispatchEvent(event);

abonnez-vous à l'événement -

document.addEventListener("newMessage", newMessageHandler, false);
1
répondu Dawson Loudon 2014-01-02 21:38:25

je pense que vous pouvez utiliser L'objet $différé et des promesses. Il vous permettra de faire quelque chose comme ceci:

Stacked: liez plusieurs gestionnaires n'importe où dans l'application au même événement de promesse.

  var request = $.ajax(url);
  request.done(function () {
  console.log('Request completed');
});

// quelque part d'autre dans l'application

   request.done(function (retrievedData) {
     $('#contentPlaceholder').html(retrievedData);
   });

tâches parallèles: demander des promesses multiples pour retourner une promesse qui alerte de leur achèvement mutuel.

$.when(taskOne, taskTwo).done(function () {
  console.log('taskOne and taskTwo are finished');
});

tâches séquentielles: exécuter des tâches dans un ordre séquentiel.

 var step1, step2, url;

 url = 'http://fiddle.jshell.net';

 step1 = $.ajax(url);

 step2 = step1.then(
   function (data) {
       var def = new $.Deferred();

       setTimeout(function () {
           console.log('Request completed');
           def.resolve();
       },2000);

     return def.promise();

 },
   function (err) {
       console.log('Step1 failed: Ajax request');
   }
 );
 step2.done(function () {
     console.log('Sequence completed')
     setTimeout("console.log('end')",1000);
 });

Source ici: http://blog.mediumequalsmessage.com/promise-deferred-objects-in-javascript-pt2-practical-use

0
répondu JSG33kC0d3 2014-01-02 02:03:14

Usage: jsfiddle

C'est une approche naïve, mais pourrait fonctionner pour certaines applications:

CustomEventTarget.prototype = {

    'constructor': CustomEventTarget,

    on:   function( ev, el ) { this.eventTarget.addEventListener( ev, el ) },
    off:  function( ev, el ) { this.eventTarget.removeEventListener( ev, el ) },
    emit: function( ev ) { this.eventTarget.dispatchEvent( ev ) }

}

function CustomEventTarget() { this.eventTarget = new EventTarget }
0
répondu flcoder 2018-03-15 01:09:26