ドキュメント

Tutorials:AJAX and Events

From jQuery JavaScript Library

Jump to: navigation, search

Discusses binding event handlers to DOM elements at the appropriate times.

Original: http://www.packtpub.com/jQuery/book/mid/100407j4kh3d
Author: Karl Swedberg and Jonathan Chaffer

Similar Tutorials: AjaxEventsManipulation

The following tutorial is an excerpt from the book, Learning jQuery: Better Interaction Design and Web Development with Simple JavaScript Techniques.

Handling the Handlers

Suppose we wanted to highlight all the <h3> elements on the page when they are clicked. By now the code to perform such a task is almost second-nature:


$(document).ready(function() {
 $('h3').click(function() {
   $(this).toggleClass('highlighted');
 });
});

All is well, in that clicking on the letters on the left side of the page highlights them. But the dictionary terms are also <h3> elements, and they do not get the highlight. Why? The dictionary terms are not yet part of the DOM when the page is loaded, so the event handlers are never bound. This is an example of a general issue with event handlers and AJAX calls: loaded elements must have all of their event handlers bound at the appropriate time.

A first pass at solving this problem is to factor the binding out into a function, and call that function both at the time the document is ready and after the AJAX call:


$(document).ready(function() {
 var bindBehaviors = function() {
   $('h3').click(function() {
     $(this).toggleClass('highlighted');
   });
 }

 bindBehaviors();

 $('#letter-a .button').click(function() {
   $('#dictionary').hide().load('a.html', function() {
     bindBehaviors();
     $(this).fadeIn();
   });
 });
});

Now we can put all our event handlers in the bindBehaviors() function, and call that whenever the DOM changes. Clicking on a dictionary term now highlights it, as we intended. Unfortunately, we've also managed to cause very strange behavior when the letters are clicked. At first they highlight correctly, but after the button is clicked (loading the dictionary entries), they no longer highlight on subsequent clicks.

Closer inspection reveals that, after the AJAX call, the highlighting breaks because the click handler is fired twice. A doubled .toggleClass() is the same as none at all, so the click seems not to work. A tricky behavior to debug, to be sure. The culprit here is bindBehaviors(), which binds the click event to all <h3> elements each time. After a button click, there are actually two event handlers for clicks on an <h3>, which happen to do the exact same thing.

Scoping an Event Binding Function

A nice way around this double-firing is to pass some context into bindBehaviors() each time we call it. The $() function can take a second argument, a DOM node to which the search is restricted. By using this feature in bindBehaviors(), we can avoid multiple event bindings:


$(document).ready(function() {
 var bindBehaviors = function(scope) {
   $('h3', scope).click(function() {
     $(this).toggleClass('highlighted');
   });
 }

 bindBehaviors(this);

 $('#letter-a .button').click(function() {
   $('#dictionary').hide().load('a.html', function() {
     bindBehaviors(this);
     $(this).fadeIn();
   });
 });
});

The first time bindBehaviors() is called, the scope is document, so all <h3> elements in the document are matched and have the click event bound. After an AJAX load, though, the scope is instead the <div id="dictionary"> element, so the letters are not matched and are left alone.

Using Event Bubbling

Adding scope to a behavior-binding function is often a very elegant solution to the problem of binding event handlers after an AJAX load. We can often avoid the issue entirely, however, by exploiting event bubbling. We can bind the handler not to the elements that are loaded, but to a common ancestor element:


$(document).ready(function() {
 $('body').click(function(event) {
   if ($(event.target).is('h3')) {
     $(event.target).toggleClass('highlighted');
   }
 });
});

Here we bind the click event handler to the <body> element. Because this is not in the portion of the document that is changed when the AJAX call is made, the อับดุลย์ ปาตะห์สะมะแว has to be re-bound. However, the event context is now wrong, so we compensate for this by checking what the event's target attribute is. If the target is of the right type, we perform our normal action; otherwise, we do nothing.

FYI: This technique is sometimes referred to as "Event Delegation". A live demo of this in action can be found here (yes, it uses Yahoo! UI, but it's still a good example).