jQuery Custom Events: They Will Rock Your World!

Ok, maybe they won’t exactly “Rock Your World” but they might completely change the way you look at your jQuery development. At the very least, I hope this simple technique will help you build clean, reusable and extendable front-end code.

Download zipped archiveView the example

A Quick ‘n’ Dirty Overview

If you are the “just the facts” type of person, here is the 20 second overview. You can trigger custom events on any DOM object of your choosing using jQuery. Just trigger it using the following code:

$("#myelement").trigger('fuelified');

You can subscribe to that event using either bind or live functions in jQuery:

$("#myelement").bind('fuelified',function(e){ ... });
$(".cool_elements").live('fuelified',function(e){ ... });

You can even pass additional data about the event when triggering it and reference it on the listeners end:

$("#myelement").trigger('fuelified',{ custom: false });
$("#myelement").bind('fuelified',function(e,data){ if(data.custom) ... });

The element the trigger has been called on, is available to listener’s callback function as the variable this.

A Little Philosophy

I encourage you to view your front end code in parts and pieces instead of one huge piece of front-end code. If you look at each part separately, you can build better, reusable pieces of code. You won’ t have a lot of duplicated code in your web-site, and you will be able to extend what you have built later on. A rating site might have widgets that are used to take in the ratings. You may have another widget that monitors the rating widgets and tallies the scores. Making the pieces as independent as possible makes reuse and extension a breeze.

A Fun Example

For those who like tutorials or walkthroughs, this should be a fun demonstration of the power of jQuery custom events.

Preparation

To get started, download my blank website template. Then head over to Starter for jQuery and create two jQuery plugins using the following settings:

  1. Class Name: ColorBlock
    Include: Options, Getter
    (Not Comments)
  2. Class Name: ColorObserver
    Include: Options, Getter (Not Comments)
    Default Options: colorObjs|div.color_block

After you enter each item, click Create then Download. Save each file to the js/ directory of our Blank Website Template. Then add <script> tags linking both files into the index.html file. Finally, put a blank jQuery document.ready block into the head, just below the scripts. When you are finished, the scripts in <head> area should look like this:

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js" type="text/javascript" charset="utf-8"></script>
<script src="js/jquery.colorblock.js" type="text/javascript" charset="utf-8"></script>
<script src="js/jquery.colorobserver.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript" charset="utf-8">
 	$(document).ready(function(){

  	});
 </script>

HTML

Copy and paste the following HTML markup into the <body> tag of the index.html file:

<div id="color_blocks">
  	<div class="color_block"></div>
  	<div class="color_block"></div>
  	<div class="color_block"></div>
  	<div class="color_block"></div>
  	<div class="color_block"></div>
  	<div class="color_block"></div>
  	<div class="color_block"></div>
  	<div class="color_block"></div>
</div>

<div id="observer-one" class="observer"> </div>
<div id="observer-two" class="observer"> </div>

Normally I might delegate the creation of the div.color_block elements to a script as well, but that is not the point of this exercise!

CSS

Copy and paste the following CSS into the layout.css file. Basically it sets simple styling for our elements. The important part is that it sets a width and height of 50px for our .color_block elements, and floats them to the left. If you were to load index.html in a browser right now, you wouldn’t see too much.

.color_block {
	width: 50px;
	height: 50px;
	cursor: pointer;
	float: left;
	margin: 0 5px 5px 0;
}

.observer {
	clear: left;
	margin: 10px 0;
	padding: 10px;
	float: left;
	background: #eee;
	font-family: Helvetica, Arial;
}

#observer-one { border-top: solid 2px red; }
#observer-two { border-top: solid 2px blue; }

JS

The plugins you created using Starter for jQuery have added two new functions to our jQuery objects. We are going to call both of them in our <head> script area. Paste the following code between the opening and closing braces of the document.ready function:

$(".color_block").colorBlock();
$("#observer-one").colorObserver();

The first line calls our colorBlock plugin on all the blocks. The second, calls our colorObserver method on one of our observer divs. If you load the page now, you should not get any errors, but nothing should do anything yet either.

ColorBlock

We want to give our ColorBlocks an array of colors. They will all start on color 1 (index position 0) and every time you click on them, they will change to the next color. To do this, we will add code to our jquery.colorblock.js file.

Note: Most of you are probably not familiar with Starter for jQuery, or the code it puts out. I plan on writing an article about it sometime, but for this article remember these important principles. Anywhere in the plugin class itself ($.ColorBlock for example) base refers to base object. And base.el and base.$el refer to the DOM element and the jQuery wrapped DOM element the base object is connected to.

The plugin supports override-able options. We need to use this for our plugin, so find the $.ColorBlock.defaultOptions and paste this between the curly braces:

colors: ["red","blue","green","yellow"]

Now, we need to give our object the ability to change colors. Paste the following code after the base.init function definition (but before the base.init() call. That should always come last in the plugin) :

base.setColor = function(new_color_index){
	if( new_color_index != base.current_color_index ){
		base.current_color_index = new_color_index;
		base.$el.css({backgroundColor: base.options.colors[base.current_color_index]});
	}
}

base.nextColor = function(){
	var new_color_index = base.current_color_index + 1;
	if ( new_color_index > ( base.options.colors.length - 1 ) )
		new_color_index = 0;
	base.setColor( new_color_index );
}

base.getColor = function(){
	return base.options.colors[base.current_color_index];
}

This gives our object three methods. A method to set the color using an arbitrary index. A method to trigger the next color in the cycle. And finally a way to retrieve which color is currently in use. Nothing so far is rocket science.

Finally to enable our little widget, we add two lines of code to the base.init function after the line that initializes the options.

base.$el.click(function(){ base.nextColor(); });
base.setColor(0);

The first line triggers base.nextColor() every time our object is clicked. And the second line just initializes our object using the first color in the array (index position 0).

Now, if we load index.html in our browser, you will be able to cycle through the colors on each block simply by clicking on them.

In our make believe world, we can be happy that we made a working jQuery plugin. YAY! However, bad news also occurs in our world, and our client requests a way to monitor which colors are currently showing. So, along comes our ColorObserver widget.

Already we are making educational progress, because we realize the functionality to monitor all color_blocks is not the function of the ColorBlock widget (You do realize that right?).

ColorObserver

Our observer needs a method to find all ColorBlocks and find out which colors are showing. The following code should do the trick. Paste the following code into the jquery.colorobserver.js file, after the base.init definition, but before the base.init(); call.

base.discoverColors = function(){
	var colors_count = {};
	$(base.options.colorObjs).each(function(){
		var color = $(this).data('ColorBlock').getColor();

		if(!colors_count[color])
			colors_count[color] = 1;
		else
			colors_count[color] += 1;
	});

	var output = "";
	$.each(colors_count, function(key,value){
		output += key + "=" + value + "<br />";
	})
	base.$el.html(output);
}

In a nutshell, this code uses the selector from our defaultOptions or an overridden option to find which objects are ColorBlocks. It then gets the color, and either adds it to the colors_count array, or increases its count by one if it already exists. It then puts together a HTML snippet, and updates its base.$el content with our snippet. (Remember that base.$el refers to the jQuery wrapped DOM element we originally tied the widget to. In this case, #observer-one)

The Most Important Part

Now we need to tie to two objects together. You might be tempted to hardwire some $("#observer-one").getColorObserver().discoverColors() call into your setColor function over on the ColorBlocks. It would work for sure, but it makes your code more and more dependent on each other… making future changes more likely to break your code.

Instead we will use custom events to solve our problem. Put the following line inside the base.setColor function on the ColorBlock, inside the if statement as the last line:

base.$el.trigger('color_changed');

Now, any time the color changes on a single block, a ‘color_changed’ event fires. Now, put the following two lines in the base.init function of the ColorObserver class (After the options setup code):

$(base.options.colorObjs).live('color_changed', function(){ base.discoverColors(); });
base.discoverColors();

The first line binds to our custom event and tells the ColorObserver class to rediscover the visible colors. The second line is just the initial call to find out the starting state of the colors.

At this point our job is done, and our two widgets are correctly wired together. Load index.html up in your browser and see for yourself. Every time you change a block color, the observer knows about it and changes its data to match.

The best part is that you could delete the <script> tag that links the ColorObserver to our document, and remove the line that initializes it, and the ColorBlock’s would continue to work just fine!

Even More Coolness

You might not have even noticed, but even more flexibility was baked into our project. Add the following line to our <head> script block:

$("#observer-two").colorObserver({colorObjs:$(".color_block:lt(3)")});

Now our second observer only observes the first 3 color blocks. We over-rode the colorObjs default selector with our own.

Conclusion

I hope this opens new doors to you when using jQuery for development. Its a great way of compartmentalizing and easily extending your code. If you still have trouble understanding its practicality, consider an advanced jQuery rich internet application. It could trigger events for every major interactions, and you could build a GoogleAnalyticsObserver class to listen for those actions and send them on to Google to track.

Let me know if this helps you out, or how you think you could use Custom jQuery Events.

Doug Neiner is an Editor at Fuel Your Coding and an official member of the jQuery Team. He is addicted to new technology, and specifically loves spending time with WordPress, Ruby on Rails and jQuery. Learn more via twitter or his Google Profile.

 

If you liked this article, please help spread the news on the following sites:

  • Bump It
  • Blend It
  • Bookmark on Delicious
  • Stumble It
  • Float This
  • Reddit This
  • Share on FriendFeed
  • Clip to Evernote