Ask Ben: Using jQuery's triggerHandler() To Skip Default Behaviors

Posted March 12, 2009 at 9:45 AM

Tags: Javascript / DHTML, Ask Ben

Ben, I have a somewhat simple jquery q for you. I have x number of divs... inside each div i have a checkbox next to each checkbox i have a span tag with some text. i want to make it where if they click on the text OR the checkbox, it will highlight that div (addClass('red')). i was looking at using the trigger and when you click on the text portion it will trigger the checkbox click() event,but when i click on the text, the last div and checkbox is checked. If i check on the checkboxes, it works fine. I did have a label tag in there at first but thought it was causing problems. Hope that makes sense. the divs with the checkboxes are generated dynamically based on an ajax call.

The way I read this question, there are three real components:

  1. The form elements are being built programmatically (not in original HTML).
  2. When a checkbox is checked or unchecked, we want to highlight or unhighlight the parent container respectively.
  3. When the span next to the checkbox is clicked, we want this to act like a checkbox click.

As I was going about answering this, I actually ran into some interesting behavior. I think your instincts were correct - when you click on the text, it should trigger the click event on the sibling checkbox. But, when it comes to checkboxes, it seems the order of triggered events is not in our favor. Let's look at the code, and then I will go into more detail:

 Launch code in new window » Download code as text file »

  • <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  • <html>
  • <head>
  • <title>jQuery triggerHandler() Demo</title>
  •  
  • <style type="text/css">
  •  
  • p.field {
  • border: 1px solid #666666 ;
  • padding: 10px 10px 10px 10px ;
  • }
  •  
  • p.selected-field {
  • background-color: #F0F0F0 ;
  • }
  •  
  • </style>
  •  
  • <script type="text/javascript" src="jquery-1.3.2.js"></script>
  • <script type="text/javascript">
  •  
  • // Build our sample AJAX data.
  • var arrData = [
  • {
  • checkbox: "chb1",
  • span: "Here is some sample text"
  • },
  • {
  • checkbox: "chb2",
  • span: "Here is some sample text"
  • },
  • {
  • checkbox: "chb3",
  • span: "Here is some sample text"
  • }
  • ];
  •  
  •  
  •  
  • // When the DOM has loaded, initialize.
  • $( InitForm );
  •  
  • // Initializes the form.
  • function InitForm(){
  • // Get a handle on the form.
  • jForm = $( "form" );
  •  
  • // Add the dynamic elements.
  • $.each(
  • arrData,
  • function( intIndex, objValue ){
  • // Create a paragraph to hold teh elements.
  • var jPara = $( "<p />" ).addClass( "field" );
  •  
  • // Create the checkbox.
  • var jCheckbox = $(
  • "<input type='checkbox' name='" +
  • objValue.checkbox +
  • "' />"
  • );
  •  
  • // Create the span.
  • var jSpan = $(
  • "<span>" +
  • objValue.span +
  • "</span>"
  • );
  •  
  • // Merge all the elements.
  • jPara
  • .append( jCheckbox )
  • .append( " " )
  • .append( jSpan )
  • .appendTo( jForm )
  • ;
  • }
  • );
  •  
  •  
  • // Now that our elements have been added to the page,
  • // let's find a click handler on each checkbox.
  • jForm.find( ":checkbox" ).click(
  • function( objEvent ){
  • // Get the parent paragraph.
  • jParent = $( this ).parents( "p.field" );
  •  
  • // Check to see if the field is checked. If
  • // so, then add the selected class, otherwise
  • // remove it.
  • if (this.checked){
  •  
  • // Add the selected class.
  • jParent.addClass( "selected-field" );
  •  
  • } else {
  •  
  • // Remove the selected class.
  • jParent.removeClass( "selected-field" );
  •  
  • }
  • }
  • );
  •  
  •  
  • // Now, bind a click handler to the span so that when
  • // it is clicked, it triggers the click on the checkbox.
  • jForm.find( "span" ).click(
  • function( objEvent ){
  • // Get a jquery wrapper for clicked span.
  • var jThis = $( this );
  •  
  • // Get a reference to the checkbox.
  • var jCheckbox = jThis.prev( ":checkbox" );
  •  
  • // Because clicking the checkbox is a default
  • // event, it does not mesh quite well with the
  • // order of events. Therefore, let's manually
  • // check the box and then trigger the handler
  • // (but NOT the default event).
  • jCheckbox[ 0 ].checked = !jCheckbox[ 0 ].checked;
  •  
  • // Trigger click event on sibling checkbox.
  • jCheckbox.triggerHandler( "click" );
  • }
  • );
  • }
  •  
  • </script>
  • </head>
  • <body>
  •  
  • <h1>
  • jQuery triggerHandler() Demo
  • </h1>
  •  
  • <form>
  • <!--- Elements to be added dynamically. --->
  • </form>
  •  
  • </body>
  • </html>

After we build the form elements based on the JSON data (what I assume is coming back in the AJAX call - although if it was HTML, it wouldn't make a difference), we bind a click handler for the checkbox and click handler for the span. The click handler on the checkbox is simple - if the checkbox is checked, then add a selected class to the parent element; if the checkbox is not checked, then remove the selected class from the parent.

The click handler on the span is a bit more complicated. Ideally, what we want to do is simply trigger the click event on the checkbox if the span is clicked. However, what I found out was that the order of "Default Behavior" is not the same. When you click a checkbox, the default behavior (toggling the checked attribute) happens before the click handler is called. However, if you trigger the click handler using jQuery, the default behavior (toggling the checked attribute) happens after the click handler is called. This creates a problem because our click handler on the checkbox is going to assume that the checked attribute has already been toggled.

To get around this issue, what we have to do is toggle the checked attribute of the checkbox from within the span's click handler; then, once this is done, we have to trigger ONLY the jQuery handlers on the checkbox's click event. To do this, rather than call .click() on the checkbox, we call .triggerHandler():

 Launch code in new window » Download code as text file »

  • jCheckbox.triggerHandler( "click" );

This will execute the click handlers without triggering the default behavior (toggling the checkbox) or bubbling the event.

I think there are ways to make this solution a bit simpler, but I hope this points you in the right direction.

Download Code Snippet ZIP File

Post Comment  |  Ask Ben  |  Permalink  |  Other Searches  |  Print Page




Learning ColdFusion 9 - ColdFusion 9 tutorials, samples, examples, demos

Reader Comments

Mar 12, 2009 at 12:24 PM // reply »
111 Comments

@Ben:

The better solution would be to use the <label /> tag instead of a <span />. When you map the "for" attribute of the <label /> tag to a checkbox it'll automatically fire the checkbox's events as if you clicked the checkbox directly.

This not only would resolve your workaround for this solution, but would also be semantically correct.


Mar 12, 2009 at 12:29 PM // reply »
6,371 Comments

@Dan,

Absolutely; and, funny you bring it up because after this was posted, I emailed back and forth with the "askee" - I suggested that Label would solve the problems.

But, regardless, I am sort of glad that I went down this path as I think it uncovered a bit of a bug.


Matthew Abbott
Mar 12, 2009 at 2:36 PM // reply »
41 Comments

Yeah I originally had the label tag, but switched to a span tag. I switched back to the label tag and did in fact get it working great.

Thanks!


hootoo
Jul 7, 2009 at 2:08 AM // reply »
1 Comments

thanks for sharing.


Post Comment  |  Ask Ben

Recent Blog Comments
Nov 7, 2009 at 5:53 PM
Ask Ben: Javascript String Replace Method
You can find here an advanced function that prepared with javascript replace function. This can make the first letters of words, sentences, lines and whatever you define automatically: http://www.m ... read »
Andrew Neely
Nov 7, 2009 at 4:56 PM
A Moment That Touched Me - The Fountainhead
Ben, Glad you enjoyed the podcast. Yeah, the Tank Riot guys can get really chatty during the episodes, but that's part of the charm of it for me. They've covered everything from Nichola Tesla to Cha ... read »
Nov 7, 2009 at 4:43 PM
Building A Fixed-Position Bottom Menu Bar (ala FaceBook)
Is it possible to make some more MenĂ¼`s ? ... read »
Jill
Nov 7, 2009 at 11:40 AM
How To Unformat Your Code (Like A Pro)
Derek, I think you might be right - sweet! Thanks for the link :) ... read »
Nov 7, 2009 at 11:25 AM
How To Unformat Your Code (Like A Pro)
I think it would be way easier to just use this http://www.logichammer.com/html-formatter/ He just released v3 and it rocks. ... read »
Jill
Nov 7, 2009 at 7:58 AM
How To Unformat Your Code (Like A Pro)
LMAO - this was pretty funny! I have to admit - I also love to reformat code so I can read it. My boss used to tell me to leave my OCD at home. Now I don't feel so bad after reading everyone else' ... read »
Nov 6, 2009 at 10:10 PM
How To Unformat Your Code (Like A Pro)
The timing of this post is just uncanny. I spent the last 15-20 minutes manually un-formatting my "Ben Nadel" style code within a CFC of mine. I was really digging the readability a few weeks ago, bu ... read »
Roe
Nov 6, 2009 at 5:11 PM
Passing Arrays By Reference In ColdFusion - SWEEET!
ArraySort also reorders the results of these java obj's ... read »