» tagged pages
» logout

sorted by: recent | see : popular
Content Tagged with events + DOM

Document Object Model Events

nt occurs when a control loses the in

W3C: Del.icio.us W3C Tags

Using Low Pro for jQuery

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.

What is Low Pro?

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:

  • Live Query - Low Pro uses this plugin to make binding to dynamic elements in the DOM easier.
  • jQuery Validation - We'll use this plugin to show how we can bind other plugin functions to events.

Both these plugins already have excellent documentation, so if you wish to extend this example, please check them out.

Example 1: Creating a re-usable registration form

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:

HTML:
  1. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
  2.   "http://www.w3.org/TR/html4/strict.dtd">
  3. <html>
  4.   <head>
  5.     <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
  6.     <title>Lowpro Example</title>
  7.     <script type="text/javascript" src="js/jquery.js" /></script>
  8.     <script type="text/javascript" src="js/jquery.livequery.js" /></script>
  9.     <script type="text/javascript" src="js/lowpro.jquery.js" /></script>
  10.     <script type="text/javascript" src="js/jquery.validate.js" /></script>
  11.     <script type="text/javascript" src="js/reg-form.js" /></script>
  12.   </head>
  13.   <body>
  14.     <div id="form-container">
  15.       <form method="post" action="." id="lowpro-form">
  16.         <fieldset>
  17.           <legend>User Details</legend>
  18.           <ul>
  19.             <li>
  20.               <label for="username">Username</label>
  21.               <input type="text" id="username" name="username" />
  22.             </li>
  23.             <li>
  24.               <label for="email">Email</label>
  25.               <input type="text" id="email" name="email" />
  26.             </li>
  27.             <li>
  28.               <label for="password1">Password</label>
  29.               <input type="password" id="password1" name="password1" />
  30.             </li>
  31.             <li>
  32.               <label for="username">Password (Again)</label>
  33.               <input type="password" id="password2" name="password2" />
  34.             </li>
  35.           </ul>
  36.           <input type="submit" class="register" value="Register" />
  37.         </fieldset>
  38.       </form>
  39.     </div>
  40.   </body>
  41. </html>

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.

JavaScript:
  1. RegisterForm = $.klass({
  2.   initialize : function(options) {
  3.  
  4.   }
  5. });

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.

Validation

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.

JavaScript:
  1. RegisterForm = $.klass({
  2.   initialize : function(options) {
  3.     this.element.validate({
  4.       rules: {
  5.         username: {
  6.           required: true,
  7.           minlength: 4
  8.         },
  9.         email : {
  10.           required: true,
  11.           email: true
  12.         },
  13.         password2 : {
  14.           equalTo: "#password1"
  15.         }
  16.       },
  17.       messages : {
  18.         username : {
  19.           required: 'Your username is required',
  20.           minlength: 'The minimum number of characters is 4'
  21.         },
  22.         email : {
  23.           required: 'You must enter your email',
  24.           email: 'You must enter a valid email'
  25.         },
  26.         password2 : {
  27.           equalTo: 'Your passwords must match'
  28.         }
  29.       }
  30.     });
  31.   }
  32. });

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:

JavaScript:
  1. $(document).ready(function(){
  2.   $('#lowpro-form').attach(RegisterForm);
  3. });


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!

Enhancements

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:

JavaScript:
  1. $('#username').focus();

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".

JavaScript:
  1. RegisterForm = $.klass({
  2.   initialize : function(options) {
  3.     ...
  4.   },
  5.   onsubmit : function() {
  6.     $form = this.element;
  7.     $('.register').val('Submitting');
  8.         /* If your using Ajax it goes in here */
  9.         return false;
  10.   }
  11. });

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:

JavaScript:
  1. $('#myform').attach(MyFunction, {foo:'moo', bar:2}

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:

JavaScript:
  1. if (options.motd) {
  2.   $('.motd').text(options.motd);
  3. }

Now, when we attach our class to the form, we use the following:

JavaScript:
  1. $(document).ready(function(){
  2.   $('#lowpro-form').attach(RegisterForm, {motd: 'jQuery Rocks!'});
  3. });

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.

Tane Piper is a full time web developer, geek and Humanist from Edinburgh, Scotland.(55.957898, -3.159792)
He has released or is working on several open source projects including jMaps and Mercurial manager.
He is also available for hire as a freelance developer, working with PHP, Python and JavaScript.

jQuery: Learning jQuery

Working with Events, part 1

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.

How CSS and JavaScript Are Different

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:

JavaScript:
  1. $(document).ready(function() {
  2.   $('button.alert').click(function() {
  3.     alert('this is an alert message');
  4.   });
  5. });

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!

Events Don't Work with Added Elements

Now, let's create a new button (if we don't already have a second one) using jQuery code like this:

JavaScript:
  1. $('#create-button').click(function() {
  2.   if ( $('button.alert').length <2) {
  3.     $('<button class="alert">Not another alert</button>').insertAfter(this);
  4.   }
  5.   return false;
  6. });

create the button

Have you clicked the link to create the second button? Great. Now click that button. It does nothing. Just as expected.

CSS Continues to "Work" with Newly Created Elements

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:

HTML:
  1. <ul id="list1" class="eventlist">
  2.   <li>plain</li>
  3.   <li class="special">special <button>I am special</button></li>
  4.   <li>plain</li>
  5. </ul>

Press the "I am special" button to create a new list item with a class of "special":

  • plain
  • special I am special
  • plain

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:

JavaScript:
  1. $(document).ready(function() {
  2.   $('#list1 li.special button').click(function() {
  3.     var $newLi = $('<li class="special">special and new <button>I am new</button></li>');
  4.     $(this).parent().after($newLi);
  5.   });
  6. });

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.

Event Delegation: Getting Events to Embrace New Elements

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:

JavaScript:
  1. $(document).ready(function() {
  2.   $('#list2').click(function(event) {
  3.     var $newLi = $('<li class="special">special and new <button>I am new</button></li>');
  4.   });
  5. });

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:

JavaScript:
  1. $(document).ready(function() { 
  2.   $('#list2').click(function(event) {
  3.     var $newLi = $('<li class="special">special and new <button>I am new</button></li>');
  4.     var $tgt = $(event.target);
  5.     if ($tgt.is('button')) {
  6.       $tgt.parent().after($newLi);
  7.     }
  8.  
  9.     // next 2 lines show that you've clicked on the ul
  10.     var bgc = $(this).css('backgroundColor');
  11.     $(this).css({backgroundColor: bgc == '#ffcccc' || bgc == 'rgb(255, 204, 204)' ? '#ccccff' : '#ffcccc'});
  12.   });
  13. });

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:

  • plain
  • special I am special
  • plain

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.

Another Huge Benefit of Event Delegation

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:

JavaScript:
  1. $(document).ready(function() {
  2.   $('table').click(function(event) {
  3.     var $thisCell, $tgt = $(event.target);
  4.     if ($tgt.is('td')) {
  5.       $thisCell = $tgt;
  6.     } else if ($tgt.parents('td').length) {
  7.       $thisCell = $tgt.parents('td:first');
  8.     }
  9.     // now do something with $thisCell
  10.   });
  11. });

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.

Coming Up Next

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.

jQuery: Learning jQuery

Firefox 3 - Plays Well With Others

A good list of what Firefox 3 is implementing from WhatWG Web Applications and IE DOM Extensions

Firefox: del.icio.us/tag/firefox

Progress events 1.0

This document describes event types that can be used for monitoring the progress of an operation. It is primarily intended for data transfer operations such as XMLHTTPRequest [XHR], but should be usable in other relevant contexts.

W3C: Del.icio.us W3C Tags