Recently I have been getting a real buzz out of developing with jQuery. I've been using the library since 2006, releasing sporadic bits of code. In April of this year, I released the third revision of my most complex plugin, jMaps, and updated several other plugins, which are available in my mercurial repository.
This was also the same month I discovered a new plugin which has dramatically changed how I develop applications with jQuery. The plugin in question is Dan Webb's Low Pro for jQuery, a port of the plugin of the same name for Prototype.
So what is Low Pro? It's a plugin that provides a way of making more object-oriented JavaScript through event delegation. jQuery's plugin architecture provides a really simple way of extending the core functionality, but there is no easy way of making macros of code that do several types of events on one element. Until now!
Probably the simplest way to explain how Low Pro works is to build a quick demonstration. Sometimes I find that too many tutorials out there expect prior knowledge, so with that in mind, I will try to make the tutorial as simple as possible. However, this example does use another couple of plugins that are outside the scope of this tutorial:
Both these plugins already have excellent documentation, so if you wish to extend this example, please check them out.
For this example we'll build a simple user registration form. This is something that is common enough on the web, but a nice advantage of this example is that it
can be used over and over again with little or no modification. Hopefully once you have learned this pattern, you will be able to see lots
of other applications that can be reused over and over again in the same fashion.
Our first step is to create our basic form:
Now, we create our JavaScript file called reg-form.js. In here, we will create our class that we'll attach to the form. Inside this class, we'll create some functions that trigger on specific events. Every class created with Low Pro takes the form of $.klass({...});. Inside the curly braces, we use JavaScript prototype nature to create a pseudo-class. We will call our example RegisterForm.
If you have developed in languages like PHP, Python, or Java, you've probably seen something like this before. This first function, initialize, is a reserved function in Low Pro that accepts one parameter—an options parameter (we'll discuss that later). This function is always called whenever you bind a class to an element. Because it does not require any event to trigger, it is very useful for setting up initial data or events on dynamic DOM elements.
The first thing we'll do is create our JavaScript validation rules. This tutorial does not cover the creation of these rules; however, they should be reasonably understandable. If you would like more information, please check out the excellent documentation.
Notice that I am using this.element instead of $('#lowpro-form') inside the function. Low Pro provides this handy alias so you always work with the correct object. If you are going to be writing a complex function, it's always a good idea within the function to assign it to another variable.
But this code isn't actually doing anything yet. This is because we now need to bind the class to the form. Here is where we go back to the good old jQuery we know and love. Below the class, put this in the file:
Now, open up the page in your browser and start to type into the fields. You should start to see the validation rules apply to the fields. Congratulations, you've written your first re-usable class with Low Pro!
Now that we have our validation rules, let's start to make the form a bit more sexy. First, something simple. Let's make sure that the first field (the username field) is always selected first, so the user can easily tab down the form. After the validation method inside the initalize function, add this:
Refresh the page, and your username field should now get the focus.
Now let's introduce another event into the mix. When the user submits the form, we want to let the user know that it's being sent to the server. To do this, we could notify the user by changing the text on the button from "Register" to "Submitting".
So there we have it: A reusable, fully functional Ajax registration form with validation. You can check out a demo here and also grab the source code of this example from my repository.
One final thing I should touch on in this first tutorial is the initialize function's options parameter. When attaching any Low Pro class, the initialize function accepts an object containing any options you want to pass into the function:
In our above example, we'll add a tag to display a message of the day. In the HTML, create a div with a class of motd. Next, add the following code into the initalize function:
Now, when we attach our class to the form, we use the following:
When you now refresh the form, you should see a message on the screen.
Well, that wraps it up for this tutorial. I hope I've given you a small idea of the power that Low Pro contains. In conjunction with LiveQuery, you can really create some dynamic and interesting applications.
If anyone would be interested in more tutorials like this, then please leave a comment and let me know what you think.
CSS and JavaScript are different in many ways, almost all of which are too obvious to mention. However, one difference between the two bears explanation, because it is often the cause of confusion and consternation, especially among those who are making the transition from CSS guru to jQuery novice. In fact, it was one of the first things I asked about on the jQuery mailing list back in 2006. Since then, I've seen at least one question on the subject every week, and sometimes as many as one per day—despite an FAQ page and these three plugins to help users deal with it.
So, what's this important difference?
In CSS, style rules are automatically applied to any element that matches the selectors, no matter when those elements are added to the document (DOM).
In JavaScript, event handlers that are registered for elements in the document apply only to those elements that are part of the DOM at the time the event is attached. If we add similar elements to the DOM at a later time, whether through simple DOM manipulation or ajax, CSS will give those elements the same appearance, but JavaScript will not automatically make them act the same way.
For example, let's say we have "<button class="alert">Alert!</button>" in our document, and we want to attach a click handler to it that generates an alert message. In jQuery, we might do so with the following code:
Here we are registering the click handler for the button with a class of "alert" as soon as the DOM has loaded. So, the button is there, and we have a click function bound to it. If we add a second <button class="alert"> later on, however, it will know nothing about that click handler. The click event had been dealt with before this second button existed. So, the second button will not generate an alert.
Let's test what we've just discussed. I've added a script with the above three lines of jQuery code so that the following button will produce an alert message when clicked. Try it: Alert!
Now, let's create a new button (if we don't already have a second one) using jQuery code like this:
Have you clicked the link to create the second button? Great. Now click that button. It does nothing. Just as expected.
Now let's take a look at another example. In this one, we have three list items—two plain items and one with a class of special:
Press the "I am special" button to create a new list item with a class of "special":
Notice that, like the first special li, the new one has the yellow background. The CSS has come through for us. But press the newly created "I am new" button and, just as with the second alert above, nothing happens. The jQuery code we're using to add the new item says that upon clicking a button inside a list item with a class of "special" (which itself is inside an element with id of "list1") a new list item with class="special" should be inserted after the list item in which the button was clicked:
So, how can we get the events to carry over to the new elements? Two common approaches are event delegation and "re-binding" event handlers. In this entry, we'll examine event delegation; in part 2, we'll explore ways to re-bind.
The general idea of event delegation is to bind the event handler to a containing element and then have an action take place based on which specific element within that containing element is targeted. Let's say we have another unordered list: <ul id="list2"> ... </ul>. Instead of attaching the .click() method to a button — $('#list2 li.special button').click(...) — we can attach it to the entire surrounding <ul>. Through the magic of "bubbling," any click on the button is also a click on the button's surrounding list item, the list as a whole, the containing div, and all the way up to the window object. Since the <ul> that gets clicked is the same one each time (we're only creating items within the <ul>), the same thing will happen when clicking on all of the buttons, regardless of when they were created.
When we use event delegation, we need to pass in the "event" argument. So, in our case, instead of .click(), we'll have .click(event). We don't have to name this argument event. We can call it e or evt or gummy or whatever we want. I just like to use labels that are as obvious as possible because I have a hard time keeping track of things. Here is what we have so far:
So far, the code is very similar to our first attempt, except for the selector we're starting with (#list2) and the addition of the event argument. Now we need to determine whether what is being clicked inside the <ul> is a "special" button or not. If it is, we can add a new <li class="special">. We check the clicked element by using the "target" property of the event argument:
Line 4 above puts the target element in a jQuery wrapper and stores it in the $tgt variable. Line 5 checks whether the click's target is a button. If it is, the new list item is inserted after the parent of the clicked button. Let's try it:
I put an additional two lines at the end to demonstrate that a click on one of the buttons is still considered a click on the <ul> You'll see that clicking anywhere within the <ul> toggles its background between pink and blue.
It's probably worth noting that jQuery makes working with the event argument cross-browser friendly. If you do this sort of thing with plain JavaScript and DOM nodes, you'd have to do something like this:
var list2 = document.getElementById('list2');
list2.onclick = function(e) {
var e = e || window.event;
var tgt = e.target || e.srcElement;
if (tgt.nodeName.toLowerCase() == 'button') {
// do something
}
};
As you can see, it's a bit of a hassle.
Event delegation is also a great way to avoid crippling the user's browser when you're working with a huge document. For example, if you have a table with thousands of cells, and you want something to happen when the user clicks on one, you won't want to attach a click handler to every single one of them (believe me, it can get ugly). Instead, you can attach the click handler to a single table element and use event.target to pinpoint the cell that is being clicked:
Note that I had to account for the possibility of clicking in a child/descendant of a table cell, but this seems a small inconvenience for the great performance increase that event delegation affords.
In part 2 of this tutorial, we'll look at how to get events to carry over to newly created elements through careful placement of function calls. We'll also, necessarily, examine unbinding events and using namespaced events. In the meantime, I hope you find part 1 useful. If I've made any mistakes, especially in my terminology, please don't hesitate to point them.