addEventListener na obiekcie niestandardowym

Stworzyłem obiekt, który ma kilka metod. Niektóre z tych metod są asynchroniczne i dlatego chcę używać zdarzeń, aby móc wykonywać działania, gdy metody są wykonywane. Aby to zrobić, próbowałem dodać addEventListener do obiektu.

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

Zwróci błąd: Failed to add eventlisterens: TypeError: 'addEventListener' called on an object that does not implement interface EventTarget."

Teraz ten kod będzie używany w aplikacji phonegap, a kiedy to zrobię, działa na Androidzie/ios. Podczas testów byłoby jednak miło, gdybym mógł go uruchomić w przynajmniej jedną przeglądarkę.

PS > wiem, że mógłbym włączyć bubbling, a następnie odsłuchać root dokumentu, ale chciałbym mieć tylko trochę OOP, gdzie każdy obiekt może działać samodzielnie.

Author: Eleanor Zimmermann, 2013-12-30

9 answers

addEventListener jest przeznaczony dla elementów DOM, które implementują określone interfejsy związane ze zdarzeniami. Jeśli chcesz mieć system zdarzeń na czystych obiektach JavaScript, szukasz niestandardowego systemu zdarzeń. Przykładem może być Backbone.Events w Backbone.js. Podstawową ideą jest używanie obiektu jako hash do śledzenia zarejestrowanych wywołań zwrotnych.

Osobiście używam tego: https://github.com/component/emitter

Jest to dość proste i eleganckie rozwiązanie - ze słodkimi krótkimi nazwami metod, takimi jak on(), off() i emit(). można utworzyć nowe instancje za pomocą new Emitter() lub użyć Emitter(obj) do mieszania funkcji zdarzenia z istniejącymi obiektami. Uwaga Ta Biblioteka jest napisana do użytku z systemem CommonJS module, ale możesz jej używać gdziekolwiek indziej, usuwając linię module.exports = ....

 39
Author: Evan You,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-03 20:56:52

Jeśli chcesz słuchać obiektu javascript masz trzy sposoby:

O sup / pub wzór:

Musisz opublikować wydarzenia.

O implementacjach natywnych:

  • Object get/set operators Wystarczy posłuchać Dodaj, Usuń, Zmień, zdobądź wydarzenia. Operatory mają dobre wsparcie . Problemy tylko w IE8-. Ale jeśli chcesz użyć get / set w IE8 użyj Object.defineProperty ale na obiektach DOM lub użyj obiektu.defineProperty sham .
  • Object.prototype.watch ma dobry ES5 polyfill.
  • Proxy API potrzebuje wsparcia ES Harmony.

Obiekt.obserwuj przykład

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
Author: Pinal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-28 12:50:32

Jeśli nie potrzebujesz prawdziwych funkcji zdarzeń( takich jak bubbling, stopPropagation), możesz zaimplementować własne zdarzenia. addEventListener jest tylko API DOM, więc tak naprawdę nie potrzebujesz go dla własnych obiektów poza DOM. Jeśli chcesz utworzyć równomierny wzór wokół obiektu, oto dobry sposób, aby to zrobić, który nie wymaga żadnych dodatkowych interfejsów API przeglądarki i powinien być bardzo kompatybilny wstecz.

Powiedzmy, że masz obiekt, w którym chcesz wywołać kilka zdarzeń przy wywołaniu metody wysyłki:

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();
To naprawdę nie musi być bardziej skomplikowane. W ten sposób masz przyzwoitą kontrolę nad swoimi zdarzeniami i nie musisz się martwić o kompatybilność wsteczną lub zewnętrzne biblioteki, ponieważ wszystko jest szeroko obsługiwane. Technicznie można nawet obejść się bez setTimeout i obsługiwać wywołań zwrotnych bez żadnych interfejsów API. Wszystko inne jak stopropagacja() musiałoby być obsługiwane siebie.

Https://jsfiddle.net/ozsywxer/

Istnieją oczywiście polyfills dla CustomEvent, ale jeśli nie potrzebuję zaawansowanych funkcji eventowych, wolę zawinąć własny system WKKW w "klasę" i rozszerzyć za jego pomocą inne klasy/funkcje.

Oto Wersja CoffeeScript, z której pochodzi JavaScript: https://jsfiddle.net/vmkkbbxq/1/

^^ trochę łatwiej zrozumieć.

 12
Author: Ravenstine,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-30 19:12:53

Są dwa problemy.

Po pierwsze, metoda iSubmit.addEventListener() jest w rzeczywistości metodą na interfejsie EventTarget DOM:

Są przeznaczone tylko do użycia na elementach DOM. Dodając ją do obiektu iSubmit jako metodę, wywołujesz ją na obiekcie, który nie jest EventTarget. Dlatego Chrome rzuca Uncaught TypeError: Illegal invocation błąd JavaScript.

Pierwszy problem jest krytyczny, ale jeśli możesz użyć EventTarget#addEventListener() twój kod nie zadziała, ponieważ zdarzenie jest dodawane do iSubmit, ale wysyłane z document. Ogólnie rzecz biorąc, metody tego samego obiektu muszą być używane podczas dołączania detektorów zdarzeń i wysyłania zdarzeń (chyba że używasz zdarzeń bubbling, co jest inną historią - Uwaga: bubbling nie jest ograniczony do zdarzeń związanych z JavaScript lub DOM, na przykład).

Używanie niestandardowych zdarzeń z własnymi obiektami jest bardzo normalne. Jako Evan Yu wspomniane , istnieją biblioteki do tego. Oto kilka:

Użyłem js-signals i bardzo mi się podoba. Nigdy nie używałem Wolfy87/EventEmitter, ale ma ładny wygląd.

Twój przykład może wyglądać mniej więcej tak, jeśli użyłeś 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
Author: tiffon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:26:15

Jeśli jesteś w węźle.środowisko js wtedy możesz użyć klasy EventEmitter węzła :

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;

Użycie:

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

Jeśli chcesz użyć Eventemittera węzła poza węzłem.środowisko js, następnie możesz użyć webpack (najlepiej v2. 2 lub później), aby uzyskać pakiet CustomClass razem z EventEmitter polyfill (zbudowanym przez webpack).

Oto Jak to działa (zakładając, że zainstalowałeś webpack globalnie za pomocą npm install -g webpack):

  1. Run webpack CustomObject.js bundle.js --output-library=CustomObject
  2. Dołącz bundle.js do swojej strony HTML (spowoduje to wystawienie window.CustomObject)
  3. Nie ma kroku trzeciego!

Indeks.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
Author: Benny Neugebauer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-05 01:54:53

Tylko spekulacje; sam nie próbowałem. Ale możesz utworzyć element atrapy i odpalić / odsłuchać zdarzenia na elemencie atrapy. Poza tym wolę chodzić bez bibliotek.

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
Author: Rey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-03 01:08:41

Ten artykuł wyjaśnia tworzenie niestandardowych zdarzeń: http://www.sitepoint.com/javascript-custom-events/

Oto przykład:

Utwórz Zdarzenie-

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

Przypisanie zdarzenia do czegoś -

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

Subskrybuj wydarzenie -

document.addEventListener("newMessage", newMessageHandler, false);
 1
Author: Dawson Loudon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-02 21:38:25

Myślę, że możesz użyć obiektu $ Deferred i promises. To pozwoli Ci zrobić coś takiego:

Stacked: wiąże wiele programów obsługi w dowolnym miejscu aplikacji z tym samym zdarzeniem promise.

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

// gdzie indziej w aplikacji

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

Zadania równoległe: poproś wiele obietnic, aby zwróciły obietnicę, która ostrzega o ich wzajemnym wypełnieniu.

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

Zadania sekwencyjne: wykonywanie zadań w kolejności sekwencyjnej.

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

Źródło proszę.: http://blog.mediumequalsmessage.com/promise-deferred-objects-in-javascript-pt2-practical-use

 0
Author: JSG33kC0d3,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-02 02:03:14

Użycie: jsfiddle

Jest to naiwne podejście, ale może działać dla niektórych aplikacji:

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
Author: flcoder,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-15 01:09:26