Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
Home
Discussion GroupsGeneralPHPASPPerlColdFusionFlashHTML, CSS, ScriptsBrowsers

Webmaster Forum / HTML, CSS, Scripts / JavaScript / December 2007



Tip: Looking for answers? Try searching our database.

Adding custom events to widget

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
Anthony Levensalor - 29 Dec 2007 08:47 GMT
Hey guys!

I'm writing a widget wherein I want the pages using it to be able to
subscribe to events on it. I did my own research, not asking anyone to
write it for me, just want to make sure I'm on the right track here.

Basically, I'm creating a private collection of listeners, then
providing a mechanism for adding functions to that event.

A dispatch function loops through the listeners, finds the right one,
and calls the observer.

// Constructor
function mySampleWidget () {
 // Collection of listeners (private)
 var listeners = [];

 // Function for adding listeners
 this.addEventListener =
  function (evName, observer, stopHere) {
   listeners.push(
    {
      "event":evName,
      "observer":observer,
      "stop":stopHere} );
   };
           
 // the dispatch function.
 this.dispatch =
  function (calEvent) {
   for (var i = 0; i < listeners.length; ++i) {
    var listener = listeners[i];
    if (listener.event == "on" + calEvent) {
     listener.observer();
     if (listener.stop) {
    break;               
     }
    }
   }
 }         

mySampleWidget.prototype.show =
  function() {
    this.dispatch("beforeshow");
    // code to display widget
    this.dispatch("aftershow");
  }

I'm a little worried about the dispatch method, right now it has
pseudo-protected access, which means someone could call it from outside
on a whim. I would like to make it private, but I was unable to wrap my
head all the way around Crockford's article on public/private/protected
pseudo access sufficiently.

The code works in my tests so far (unless I pass in some nulls, it
breaks real easy because I'm not type checking and making sure
everything is right before I assign.

The usage would be:

var w = new myWidget();
w.addEventListener(
  "onbeforeshow",
  function() {alert("Before Show");},
  false};
w.show();

At this point, the application does display the proper message/call the
proper function, and does so for several different listeners, unless and
until one has a stop property that evaluates to true, and it will not
process listeners after that.

A few questions:
1. Is this a good path to be on? Is there something in there I'm doing
that's ingorant of the possible repercussions beyond the type and value
checking I've not yet implemented?

2. Can you provide any insight into how I can effectively hide the
dispatch method while still giving the other prototyped functions access
to him?

3. What other issues/caveats have you run into with this kind of thing
that I should be thinking about?

4. OT for this post, but IE7 seems not to like the
"application/javascript" type attribute. Just more of the same?

Thanks a million guys, you are appreciated.

~A!

Signature

Anthony Levensalor
anthony@mypetprogrammer.com

Only two things are infinite, the universe and human stupidity,
and I'm not sure about the former. - Albert Einstein

David Mark - 29 Dec 2007 19:50 GMT
On Dec 29, 3:47 am, Anthony Levensalor <anth...@mypetprogrammer.com>
wrote:
> Hey guys!
>
> I'm writing a widget wherein I want the pages using it to be able to
> subscribe to events on it. I did my own research, not asking anyone to

I take it that YUI was part of that research.

> write it for me, just want to make sure I'm on the right track here.

It depends on your intended destination.

> Basically, I'm creating a private collection of listeners, then
> providing a mechanism for adding functions to that event.
[quoted text clipped - 4 lines]
> // Constructor
> function mySampleWidget () {

Constructor names should be capitalized.

>   // Collection of listeners (private)
>   var listeners = [];
[quoted text clipped - 8 lines]
>        "stop":stopHere} );
>     };

This is a bad name for this as it has nothing to do with adding event
listeners.  Something like "registerCallback" would make more sense.
I know YUI calls these "custom events", and bundles this functionality
with their event module, but this seems like an odd choice to me.

>   // the dispatch function.
>   this.dispatch =
>    function (calEvent) {
>     for (var i = 0; i < listeners.length; ++i) {
>      var listener = listeners[i];
>      if (listener.event == "on" + calEvent) {

Why store the "on?"

>       listener.observer();

You should have a context parameter to the registration function that
allows you to set the "this" identifier for the callback.  Then you
could do something like this:

listener.observer.call(listener.context || this);

YUI calls this the "scope" of the event, which makes no sense at all.

>       if (listener.stop) {
>         break;                          
>       }

Why not use the return value of the observer (callback) function?

>      }
>     }
[quoted text clipped - 9 lines]
> I'm a little worried about the dispatch method, right now it has
> pseudo-protected access, which means someone could call it from outside

It doesn't seem protected at all to me.  The only thing hidden is the
listeners array.

> on a whim. I would like to make it private, but I was unable to wrap my
> head all the way around Crockford's article on public/private/protected
> pseudo access sufficiently.

In this example, you would have to move the creation of the show
method inside the constructor.

> The code works in my tests so far (unless I pass in some nulls, it
> breaks real easy because I'm not type checking and making sure
> everything is right before I assign.

There is no point in doing that anyway.  Just stipulate the required
types in the documentation.

> The usage would be:
>
[quoted text clipped - 12 lines]
> A few questions:
> 1. Is this a good path to be on? Is there something in there I'm doing

Personally, I think this sort of syntactic sugar is overkill for all
but the most complex applications.  If a page is so complex that you
need to assign multiple teams of developers to it, then perhaps
something like this would make sense.  Otherwise, why would the
widgets involved need more than one callback per "event."

> that's ingorant of the possible repercussions beyond the type and value
> checking I've not yet implemented?

The only repercussions of that would be if a developer fails to read
the documentation or mistakenly passes invalid parameters, they will
get an error.  That is how it should be.

> 2. Can you provide any insight into how I can effectively hide the
> dispatch method while still giving the other prototyped functions access
> to him?

You can't.

> 3. What other issues/caveats have you run into with this kind of thing
> that I should be thinking about?

Do you plan to create one base widget that all other widgets inherit
from?  Will all widgets really need this sort of event system?

> 4. OT for this post, but IE7 seems not to like the
> "application/javascript" type attribute. Just more of the same?

Use text/javascript or nothing.
Anthony Levensalor - 30 Dec 2007 06:46 GMT
David Mark said:
[snip]
>> I'm writing a widget wherein I want the pages using it to be able to
>> subscribe to events on it. I did my own research, not asking anyone to
>
> I take it that YUI was part of that research.
No, should it have been? I was looking for articles and examples on
events as they relate to classes, but I largely eschewed the big
libraries. Subscribe was a term I picked up from one of the articles I
read, though. I've worked with YUI a little, so I understand why you'd
say that.

>> write it for me, just want to make sure I'm on the right track here.
>
> It depends on your intended destination.
Just for the science of it, really. I just like to tinker sometimes.

[snip]
> This is a bad name for this as it has nothing to do with adding event
> listeners.  Something like "registerCallback" would make more sense.
> I know YUI calls these "custom events", and bundles this functionality
> with their event module, but this seems like an odd choice to me.

Ok, so I must have read something on this from someone who uses it
heavily, then. Your suggestion makes a lot more sense, thanks!

[snip]
> Why store the "on?"

I debated that for a bit, decided to put it in because the events all
have that, and I am after all trying to pass these off as events, right?
:) Seemed like the way to go, but in retrospect it would be silly to do
that for real, considering that one day more events may come down the
pike with the standard naming and I would be stomping all over them, or
they me, or any other unintended and unexpected result. Thanks again.

>>       listener.observer();
>
[quoted text clipped - 3 lines]
>
> listener.observer.call(listener.context || this);

Where's my net? Whooosh, right over the head. I'll look up context
variavles.

> YUI calls this the "scope" of the event, which makes no sense at all.

What the hell for?  I at least understand they are not real events, but
just callbacks in a flimsy costume, and I wouldn't be fooling anyone but me.

>>       if (listener.stop) {
>>         break;                          
>>       }
>
> Why not use the return value of the observer (callback) function?

Most likely because I did not think of it. Thanks again.

[snip]
>> I'm a little worried about the dispatch method, right now it has
>> pseudo-protected access, which means someone could call it from outside
>
> It doesn't seem protected at all to me.  The only thing hidden is the
> listeners array.

Yeah, that sentence didn't make much sense.

>>I would like to make it private, but I was unable to wrap my
>> head all the way around Crockford's article on public/private/protected
>> pseudo access sufficiently.
>
> In this example, you would have to move the creation of the show
> method inside the constructor.

And I didn't want a behemoth constructor, but that does get my head the
rest of the way around it. Danke.

>> The code works in my tests so far (unless I pass in some nulls, it
>> breaks real easy because I'm not type checking and making sure
>> everything is right before I assign.
>
> There is no point in doing that anyway.  Just stipulate the required
> types in the documentation.

I've never been good at that. I like to try to fail gracefully if I can,
but Javascript errors are never the end of the world, so I imagine
you're right. Maybe I'll just stay on the light side of it, do some
small basic checks.

[snip]
>> A few questions:
>> 1. Is this a good path to be on? Is there something in there I'm doing
[quoted text clipped - 4 lines]
> something like this would make sense.  Otherwise, why would the
> widgets involved need more than one callback per "event."

Absolutely true, I've never needed more than one handler for any real
life event, never mind a pseudo-bs event from a widget. I'm  messing
with the idea, seeing what the language will and will not do when I try,
ya know?

I am fascinated by the idea itself, and so am exploring a bit, but I'm
finding it to be, as you said, largely useless and more than a little
nonsensical. Fun as the dickens, though. :)

>> 2. Can you provide any insight into how I can effectively hide the
>> dispatch method while still giving the other prototyped functions access
>> to him?
>
> You can't.
Darn. That's the answer I came up with too. Oh well, no big deal since
it won't ever see the outside of the sandbox, more likely than not.

>> 3. What other issues/caveats have you run into with this kind of thing
>> that I should be thinking about?
>
> Do you plan to create one base widget that all other widgets inherit
> from?  Will all widgets really need this sort of event system?

Perhaps, but I sincerely doubt it. When I'm feeling playful with a code
idea, I just write it out, code it, and then worry about whether I need
it for anything. Course, this is not on the clock time. :)

>> 4. OT for this post, but IE7 seems not to like the
>> "application/javascript" type attribute. Just more of the same?
>
> Use text/javascript or nothing.
That I do. It was another of those little experiments, except I wes
confusing myself with some of the new 1.8 (incomplete) docs I found, and
just started messing around. Notcied FireFox executed the code for me
and IE didn't, then went back to standard.

Thanks, David! Have a happy New Year.

Signature

Anthony Levensalor
anthony@mypetprogrammer.com

Only two things are infinite, the universe and human stupidity,
and I'm not sure about the former. - Albert Einstein

slebetman - 30 Dec 2007 10:39 GMT
On Dec 30, 2:46 pm, Anthony Levensalor <anth...@mypetprogrammer.com>
wrote:
> David Mark said:
> >> 4. OT for this post, but IE7 seems not to like the
[quoted text clipped - 6 lines]
> just started messing around. Notcied FireFox executed the code for me
> and IE didn't, then went back to standard.

It's better to use nothing. That's because W3C defines it as "text/
javascript" so IE is behaving correctly according to standard. But
unhelpfully IETF decided to define the MIME type of javascripts as
"application/javascript". So, technically, web servers should serve
pages as content-type "application/javascript" but in HTML it should
be declared as "text/javascript" (which is actually a non-existent
MIME type according to IETF). That is why it's better to just say
<script></script>.
dhtmlkitchen@gmail.com - 30 Dec 2007 19:29 GMT
> On Dec 29, 3:47 am, Anthony Levensalor <anth...@mypetprogrammer.com>
> wrote:
[quoted text clipped - 3 lines]
> > I'm writing a widget wherein I want the pages using it to be able to
> > subscribe to events on it. I did my own research, not asking anyone to

<snip>

> >   // the dispatch function.
> >   this.dispatch =
[quoted text clipped - 24 lines]
> >     }
> >   }

Yes, you could have something like:
if(callback.call(context, e) == false) {
 break; // cancel subsequent callbacks.
}

But this is jumping ahead to the event argument/parameter.

In a pub/sub registry, it's usually handy to push a payload in an
event object. This is called from the object that fires the event.
It's like that object says: "The 'choose' event occured, and here's
what happened: newValue = xxx, oldValue = yyy." You could build this
object like:

/** Fires a "choose" event if a blah was chosen.
MySampleWidget.clickHandler = function( ev ) {
 var target = getTarget( ev );
 ...
 if(condition) { // fire custom event.
   var prevValue, newValue;
   ...

   var ce = {
     domEvent : ev,
     type : "choose",
     prevValue : prevValue,
     newValue : newValue,
     currentTarget: this // This will help in problems where you've
used a | context | arg/property.
   }
   this.choose(ce);
 }
};

Now this can seem a bit messy, so you might want to consider building
you're event through a constructor. This is more a matter of style,
but it can make the code that you're reading more succinct.

var ce = new ChooseEvent(ev, "choose", prevValue, newValue, this);
- or -
this.choose ( new ChooseEvent(ev, "choose", prevValue, newValue,
this) );

> > mySampleWidget.prototype.show =
> >    function() {
[quoted text clipped - 22 lines]
> There is no point in doing that anyway.  Just stipulate the required
> types in the documentation.

I think error conditions are important. I noticed something about DOM
events. Try this:

function err() { throw Error('bad'); }
function ok() { document.title = 'happy'; }
document.body.addEventListener('click', err, false);
document.body.addEventListener('click', ok, false);

You can naturally expect that you'll get the callbacks firing.

Other registries will fail in the fireCallbacks function, and
everything will fail. I remember a specific case on a Friday night. I
was scheduled for a massage, but got called back in because the entire
page broke. We had something like:

YAHOO.util.Event.addListener("hubSelect", "change", hubSelected);

function hubSelected() {
document.getElementById( 'newItem' ).style.display = "none";

well this callback failed. My boss had specified that the button must
be hidden and my coworker tried to omit this using conditional logic
in the JSP, and so it did not exist. This was a last minute thing my
boss asked me to do. I do not like last minute checkins on untested
code, and told him so. He told me to do it anyway.

The next callback to hubSelect was the important one. Since the first
one failed, the second one did not fire, and the entire page was
broken from that point one.

I went back in, figured out the condition in about 10 minutes and the
use-case was tried and functioned properly. It was an easy fix for
that one.

Missing my massage was annoying. I broke my appointment with the woman
who was waiting for me. It pissed me off.

So I thought that it would make sense to wrap the callbacks in a try
catch.

for(var i = 0; i < callbacks.length; i++) {
 try {
   if( callbacks[i].call( context, ev ) == false ) break; // cancel
subsequent calls.
 }
 catch (ex) {
   setTimeout("throw ex", 1);
 }
}

I was concerned about performance, but I tried it out with mousemove
and it did not seem to be a problem. I did not find try/catch to be a
huge performance hit and the safety it got me seemed to make it a good
choice.

I've written this out here:
http://dhtmlkitchen.com/scripts/draglib/DragModule/src/dragUtils.js

I called it EventPublisher.

It's different in one other aspect: The  | return false | happens at
the end of the callStack. I left room for beforeEach and afterEach,
which is equivalent to your  | stopHere |  argument. I did not find a
need for that, and so did not want to create tests for something I
don't need.

> > At this point, the application does display the proper message/call the
> > proper function, and does so for several different listeners, unless and
[quoted text clipped - 3 lines]
> > A few questions:
> > 1. Is this a good path to be on?

How do you justify the stopHere functionality?

Are there other pieces of code that could use event registry
functionality? Would it make sense to move the event pub/sub code into
one place?

> Is there something in there I'm doing
> > that's ingorant of the possible repercussions beyond the type and value
> > checking I've not yet implemented?

0) consider a custom event.
1) the error handling - the callstack should not be breakable by
simply throwing a function in that throws an error. (try/catch
approach)
2) consider the | stopHere | boolean. It would also be possible to
have a function there, if you need it, passing the event param back to
that function.

> The only repercussions of that would be if a developer fails to read
> the documentation or mistakenly passes invalid parameters, they will
[quoted text clipped - 5 lines]
>
> You can't.

You can wrap dispatch in a function.

EventPublisher.fire = function(publisher) {
    // This closure sucks. We should have partial/bind in ES.
    // If we did, this could more reasonably be a prototype method.

    // return function w/identifier doesn't work in Safari 2.
    return fireEvent;
    function fireEvent(e) {

I left me "this closure sucks" comment in.

I can see why the callStack should be private. If you reassign the
callstack, widget.callStack = false, then there will be errors. That
is bad, but can be avoided.

> > 3. What other issues/caveats have you run into with this kind of thing
> > that I should be thinking about?
>
> Do you plan to create one base widget that all other widgets inherit
> from?  Will all widgets really need this sort of event system?

I would consider using either a registry or using an AOP crosscut to
"borrow" the methods that exist in a generic EventPublisher. I would
prefer either of these (registry or AOP crosscut) over having it in
the superclass. Putting it in the superclass would seem to violate
some OOD concepts like SRP and LSP.

Garrett
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2009 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.