Event listeners/broadcasting solution

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Pavils Jurjans

    Event listeners/broadcasting solution

    Hello,

    I wanted to propose a small class that would help to overcome the
    feature that's missing in MSIE. I'd like to get some feedback from
    people and, perhaps, improvements in code/other ideas:

    /*************** *************** *************** *************** *************** *************** *
    DCListener class
    Description: provides basic event listener functionality
    Interface :
    contructor DCListener(obj, evt)
    public void dispatch(evt)
    public int addSubscriber(f nSubscriber)
    public bool removeSubscribe r(id)
    public void unregister()
    public static int id
    public static {DCListener} index
    *************** *************** *************** *************** *************** *************** */

    function DCListener(obj, evt) {
    var id = DCListener.id++ ;
    this.id = id;
    this.obj = obj;
    this.evt = evt;
    this.subscriber s = {};
    this.subId = 0;
    this.save = obj.evt;

    var listenerId = this.id;
    obj[evt] = function() {
    DCListener.inde x[listenerId].dispatch();
    }

    DCListener.inde x[id] = this;
    }
    DCListener.prot otype.dispatch = function(evt) {
    var subscribers = DCListener.inde x[this.id].subscribers;
    for (var sub in subscribers) subscribers[sub](evt);
    }
    DCListener.prot otype.addSubscr iber = function(fnSubs criber) {
    var id = this.subId++;
    this.subscriber s[id] = fnSubscriber;
    return id;
    }
    DCListener.prot otype.removeSub scriber = function(id) {
    if (id in this.subscriber s) {
    delete this.subscriber s[id];
    return true;
    } else {
    return false;
    }
    }
    DCListener.prot otype.unregiste r = function() {
    delete DCListener.inde x[this.id];
    this.obj[this.evt] = this.save;
    delete this.id;
    delete this.obj;
    delete this.evt;
    delete this.subscriber s;
    delete this.subId;
    delete this.save;
    }
    DCListener.id = 0;
    DCListener.inde x = {};

    ********** end of code **********

    Usage:

    var lstDocumenOnCli ck = new DCListener(docu ment, "onclick");
    var idA = lstDocumenOnCli ck.addSubscribe r(function() {
    alert("[A] received document.onclic k");
    });
    var idB = lstDocumenOnCli ck.addSubscribe r(function() {
    alert("[B] received document.onclic k");
    });
    // Later, to remove the first subscriber:
    lstDocumenOnCli ck.removeSubscr iber(idA);

    *************** *************** **********

    This is just starting draft, and I know it needs some things to be
    done about:
    1) Passing the event object to every subscriber
    2) Perhaps syncing the method naming with Mozilla event
    listener/subscriber model.
    3) Testing for memory leaks

    Please, I am open to ideas, and I hope this thingy might be useful for
    JS community.

    Regards,

    Pavils
  • Yann-Erwan Perio

    #2
    Re: Event listeners/broadcasting solution

    Pavils Jurjans wrote:

    Hello,
    [color=blue]
    > I wanted to propose a small class that would help to overcome the
    > feature that's missing in MSIE.[/color]

    Ah but it's not missing in IE, IE simply doesn't follow the W3C Events
    model but its own model (which is inferior to the W3C's one IMHO) - the
    methods you want to look at are attachEvent and detachEvent.
    [color=blue]
    > this.save = obj.evt;[/color]

    You probably meant
    this.save=obj[evt];

    I note that you don't trigger the existing listener if there's one, this
    might be a problem if your module is to interact with scripts setting
    handlers the 'old' way.
    [color=blue]
    > DCListener.inde x[listenerId].dispatch();[/color]

    You need to pass the evt object here, as you've noted in your "todos".
    [color=blue]
    > for (var sub in subscribers) subscribers[sub](evt);[/color]

    Dispatching the handlers like this isn't enough, you should use "call"
    to set the correct "this" value in the listener:

    for (var sub in subscribers)
    subscribers[sub].call(this.obj, evt);

    This way, the "this" keyword will refer to the target element, like in a
    element.onevent or element.addEven tListener(...) (but not with
    attachEvent, which unfortunately doesn't make the assignment).
    [color=blue]
    > Please, I am open to ideas, and I hope this thingy might be useful for
    > JS community.[/color]

    I'm sure it will, the code's promising and your conception is strong
    (I'd make the "dispatch", static members and DCListener private, though,
    not public - a minor detail).

    You might also want to look at a similar module written by Lasse
    Reichstein Nielsen, less object-oriented than yours, but tackling all
    relevant issues:

    <URL:http://www.infimum.dk/privat/eventListener.j s>


    Regards,
    Yep.

    Comment

    • Pavils Jurjans

      #3
      Re: Event listeners/broadcasting solution

      Thanks, Yann-Erwan for your suggestions, you really have an eagle's
      eye

      So, here comes an update code:
      *************** *************** *************** **************
      /*************** *************** *************** *************** *************** *************** *
      DCListener class
      Description: provides basic event listener functionality
      Interface :
      private int id;
      private object obj;
      private string evtType;
      private {Function} subscribers;
      private int subId;
      private int originalHandler ;
      contructor DCListener(obj, evt)
      public void dispatch(evt)
      public int addSubscriber(f nSubscriber)
      public bool removeSubscribe r(id)
      public void unregister()
      private static int id
      private static {DCListener} index
      *************** *************** *************** *************** *************** *************** */

      function DCListener(obj, evtType) {
      var id = DCListener.id++ ;
      DCListener.inde x[id] = this;
      this.id = id;
      this.obj = obj;
      this.evtType = evtType;
      this.subscriber s = {};
      this.subId = 1;
      this.originalHa ndler = obj[evtType] ?
      this.addSubscri ber(obj[evtType]): 0;
      obj[evtType] = function(evt) {
      evt = evt||window.eve nt;
      DCListener.inde x[id].dispatch(evt);
      }
      }
      DCListener.prot otype.dispatch = function(evt) {
      var subscribers = DCListener.inde x[this.id].subscribers;
      for (var subId in subscribers) subscribers[subId].call(this.obj,
      evt);
      }
      DCListener.prot otype.addSubscr iber = function(fnSubs criber) {
      var subId = this.subId++;
      this.subscriber s[subId] = fnSubscriber;
      return subId;
      }
      DCListener.prot otype.removeSub scriber = function(subId) {
      if (subId in this.subscriber s) {
      delete this.subscriber s[subId];
      return true;
      } else {
      return false;
      }
      }
      DCListener.prot otype.unregiste r = function() {
      delete DCListener.inde x[this.id];
      if (this.originalH andler) this.obj[this.evtType] =
      this.subscriber s[this.originalHa ndler];
      delete this.id;
      delete this.obj;
      delete this.evtType;
      delete this.subscriber s;
      delete this.subId;
      delete this.save;
      }
      DCListener.id = 0;
      DCListener.inde x = {};

      *************** *************** *************** **************

      Some notes on your comments:
      [color=blue]
      > Ah but it's not missing in IE, IE simply doesn't follow the W3C Events
      > model but its own model (which is inferior to the W3C's one IMHO) - the
      > methods you want to look at are attachEvent and detachEvent.[/color]

      Yes, I really missed that. But, this approach is quite poor, if
      comparing with the one offered by W3C. It's a sad fact, that they
      still prefer to stay away from standards.
      [color=blue]
      > I note that you don't trigger the existing listener if there's one, this
      > might be a problem if your module is to interact with scripts setting
      > handlers the 'old' way.[/color]

      It's fixed now. The original handler becomes the first subscriber.
      [color=blue]
      > You need to pass the evt object here, as you've noted in your "todos".[/color]

      Implemented.
      [color=blue]
      > Dispatching the handlers like this isn't enough, you should use "call"
      > to set the correct "this" value in the listener:
      >
      > for (var sub in subscribers)
      > subscribers[sub].call(this.obj, evt);
      >
      > This way, the "this" keyword will refer to the target element, like in a
      > element.onevent or element.addEven tListener(...) (but not with
      > attachEvent, which unfortunately doesn't make the assignment).[/color]

      That's right, I foregone the scope issue. It's fixed now.
      [color=blue]
      > I'm sure it will, the code's promising and your conception is strong
      > (I'd make the "dispatch", static members and DCListener private, though,
      > not public - a minor detail).[/color]

      Of course, all the talk about private/public is pretty irrelevant,
      since there are no private methods in JS. Well, they can be mastered
      via closures tho. But, I prefer to use this notation in the interface
      description, so that developer would honor it.

      I think you mistyped, saying you'd like to see "DCListener " private,
      as it's the class name ;) Regarding the dispatch method, I think it's
      cool to let it be public, because it allows to 1)provoke the event
      triggering from code; and 2) Create your own custom objects that can
      issue events and can have subscribers attached:

      var lstDocumentOnPr ess = new DCListener(docu ment, "onkeydown" );

      var subA = lstDocumentOnPr ess.addSubscrib er(function(evt ) {
      var charCode = event.keyCode ? event.keyCode : e.which;
      document.f.trac eA.value = "A has received the event: " + charCode;
      });
      var subB = lstDocumentOnPr ess.addSubscrib er(function(evt ) {
      var charCode = event.keyCode ? event.keyCode : e.which;
      document.f.trac eB.value = "B has received the event: " + charCode;
      if (charCode == 32) lstCustomObject Evt.dispatch();
      });

      var myCustomObject = {};
      var lstCustomObject Evt = new DCListener(myCu stomObject, "evt");
      lstCustomObject Evt.addSubscrib er(function() {
      alert("Hey, I've got custom event!");
      });

      <body>
      <form name="f">
      <input type="text" name="traceA" size="50"/><br/>
      <input type="text" name="traceB" size="50"/>
      </form>
      </body>
      [color=blue]
      > You might also want to look at a similar module written by Lasse
      > Reichstein Nielsen, less object-oriented than yours, but tackling all
      > relevant issues:
      > <URL:http://www.infimum.dk/privat/eventListener.j s>[/color]

      I will surely have some study of his work. It's quite hard to read it,
      as it has no much comments :),

      Rgds,

      Pavils

      Comment

      Working...