Custom Events in JavaScript

Posted by stephen on December 29th, 2009

Usually I use jQuery for custom events, because it has a very nice model. However, recently I have been working on a JavaScript class that I want to be independant of other libraries, so I needed to create my own custom event model.

For the sake of this sample, I changed the namespace to “ns”. You can change that to whatever you’d like.

Eventify

To enable an object with events, you’ll use the ns.events.eventify(obj) method. Once that is done, your object will have “events” and “__listeners” properties, in addition to “dispatchEvent(eventType, eventData)”, “addEventListener(eventType, callback, bubbles)”, and “removeEventListener(eventType, callback||listener)” properties.

You may pass one or many objects to the eventify() method.
ns.events.eventify(obj);
or
ns.events.eventify(obj1, obj2, obj3);

You do not need to interact with the “events” and “__listeners” properties at all, unless you want to predefine a set of events for a particular object, usually for documentation purposes. Here’s an example of a custom class that uses this event model and predefines its events. Just to clarify, I am using the “id” property in this class primarily for the event bubbling example that follows. It is not needed at all for the events to work.

addEventListener() / dispatchEvent()


ns.MyClass = ({
function MyClass(id){
ns.events.eventify(MyClass.prototype);
this.id=id;
}
MyClass.prototype={
id:null,
events:{
ready:new ns.events.Event("ready"),
error:new ns.events.Event("error")
},
property:null,
method:function(){}
}
return MyClass;
})();

If you do not define the events explicitly in the events property, they will be added automatically when a listener is added to the object. For instance:

var my = new ns.MyClass("my");
my.addEventListener("ready", function(){
alert("my is ready");
});

Would define my.events.ready.

dispatchEvent() and passing data

To dispatch an event, use the dispatchEvent() method. To pass data with a dispatched event, use the eventData parameter. For example:

my.addEventListener("ready", function(evt, data){
alert(data.hello);
});
my.dispatchEvent("ready", {hello:"Hello from dispatcher."});

Event bubbling

Event bubbling is somewhat manual. Because these are custom objects with no inherent heirarchy, you must define the parent object as the event’s “bubbleTo” property. The object defined as “bubbleTo” must share the event type that is being dispatched on the current object (they do not need to be instances of the same class). Implement bubbling as follows:

var parent = new ns.MyClass("parent");
var child = new ns.MyClass("child");
child.events.ready.bubbleTo = parent;

child.addEventListener(”ready”, function(evt){
alert(”sincerely, “+evt.target.id+”.”);
});
parent.addEventListener(”ready”, function(evt){
alert(”Sincerely, “+evt.target.id+”. With regards, “+evt.currentTarget.id+”.”);
});

child.dispatchEvent(”ready”);

To prevent subsequent callbacks on an object, call evt.stopImmediatePropagation(). To stop the bubbling, call evt.stopPropagation().

You may also prevent the bubbling of an event when creating the listener by using the “bubbles” parameter of the “addEventListener()” method.

var listener = child.addEventListener("ready", function(evt){
alert("sincerely, "+evt.target.id+".");
}, false);

removeEventListener()

Finally, to remove a listener from an object, use the removeEventListener(listener||callback) method. This method will accept the listener or the callback function used. Note that in the above example I have assigned the listener to “var listener”.
child.removeEventListener("ready", listener);
You may also use the remove() method of the listener itself.
listener.remove();

Download

Right click and choose “Save Target As” to download events.js.

Javascript Date difference as milliseconds, minutes, hours, days, weeks, months, or years

Posted by stephen on November 13th, 2009

I needed to figure out the difference between two dates in Javascript, so I wrote up this function. It takes into account the days in each month as well as leap years. It will calculate years, months, weeks, days, hours, minutes, seconds, and milliseconds, or any combination of those. For example, if you want months and hours, this’ll do ya.

Let me know if anyone finds a discrepancy in the calculations.

sample.html
timeSpan.js

View source of the sample page above for usage. Samples include a date comparison and a date countown.

IE6 and form.submit() bug workaround

Posted by stephen on November 3rd, 2009

IE6………one of life’s great mysteries. I think we may need some federally enforced browser standards more than a new health care system, but that’s just me.

In IE6, programatically triggering the submit() method of a form element doesn’t *always* seem to work. There’s no logical reason - it just doesn’t. I’ve run into this problem twice now. The first time, I just ended up building a query string out of the form variables and using window.location to send them to the action page. That was with a static form. I just hit this wall again with a form I’m building dynamically.

For *whatever* reason, delaying the submit action by 100ms works like a charm. I wish I could tell you more, but as with all IE6 bug fixes, when they work, I try not to ask questions.

Here’s what I ended up doing:

I tried building the form through the browser’s native methods, using document.createElement() and element.appendChild(), which didn’t make a difference, so I reverted back to created the form with jQuery. So, here’s what I ended up doing….

/* ....build form elements using jQuery... */
$j("body").append(form);

setTimeout(function(){
	form.submit();
}, 100);

Hope this saves someone some time! If you’re dealing with a form that is not being built dynamically, I have an inkling that this may not work. Still, delaying the event another way might. Try this, and if it works for you, let me know!

<a href="javascript:;" onclick="setTimeout(function(){ form.submit() }, 100)">Submit</a>

That’s the onclick event of an anchor tag, but the same would apply for the onclick of any element.

jquery.tabPane()

Posted by stephen on February 26th, 2009

Everyone enjoys a good set of tabbed panes. I wrote jquery.tabPane to give us a simple way to do that, because this particular site is using Adobe Contribute as the CMS. Basically, you create some links to anchors, then tell this function what the containing elements for those are.

Usage

jQuery.tabPane(tabPaneSettings, callback);

Example

This isn’t so much of an example as a clarified description. If you want to see a sample, go here. It also has some HTML and CSS selectors you can use as a base, if you’d like.

Let’s say you have a list of anchor links, and several divs with content. You can point the tabPane settings at the containers of the links to those anchors (tabs), and the containers of those anchors (pane). The display/hide functionaly is handled for you, and you can style it any way you want.

Here you have it:

var paneSettings = {
	tabContainers:"div.pane-buttons li",
	contentContainers:"div.pane-content div.content-pane"
};
j.tabPane(paneSettings);

Javascript CSS Formatter

Posted by stephen on February 17th, 2009

I’ll probably post more on this later, but I just want to get it up for now.

Sometimes my CSS gets auto-formatted, which adds a whole bunch of “clutter” space that I don’t want. I find CSS easier to read when there is a visual hierarchy of the selectors and the HTML elements they represent, and if there is consistency in the order of the selector attributes.

The following CSS Formatter will place all of your selectors and their contents on a single line, and sort their attributes alphabetically.

For instance…

div#whatever, div.whatnot {
display:block;
background:transparent;
float:left;
}

becomes…

div#whatever,
div.whatnot {background:transparent; display:block; float:left;}

The formatter will IGNORE any space outside of the selector and its contents.

Try it out here:

http://blog.stephenrushing.com/wp-content/uploads/2009/02/cssFormat/cssformat.html


Copyright © 2007 Stephen Rushing. All rights reserved.