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,516 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.


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!


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

thanks for sharing.


Post Comment  |  Ask Ben

Recent Blog Comments
Nov 21, 2009 at 6:47 PM
Hal Helms - Real World Object Oriented Development, Sarasota - Day Five
@charlie griefer, Thank you.. ... read »
Nov 21, 2009 at 5:15 PM
Using ColdFusion Structures To Remove Duplicate List Values
@Jose Galdamez, Oh heh yeah I didn't paste the whole code. I should have defined the vars -- my bad. It's fixed thou. Thanks. ... read »
Nov 21, 2009 at 4:49 PM
Styling The ColdFusion 8 WriteToBrowser CFImage Output
Great work yet again Ben! Whilst I didn't use this whole code, I copied some of your regex code for a similar problem with the lack of an alt attribute and unescaped ampersands in CFIMAGE for Railo 3 ... read »
Nov 21, 2009 at 1:13 PM
My First ColdFusion Builder Extension - Encrypting And Decrypting CFM / CFC Files
@Ben, Because I am pedantic, I just want to make sure that everyone knows there is absolutely no encryption going on. There is only encoding and obfuscation. The cfencode tool only obfuscates your C ... read »
Nov 21, 2009 at 12:28 PM
Using ColdFusion Structures To Remove Duplicate List Values
@Jody I can't seem to get your code sample to work. If you are still having problems, try this code out and see if it gets you what you wanted. <!--- Comma delimited list with various duplicates ... read »
Nov 21, 2009 at 11:03 AM
Groovy Operator Overloading Does Not Work In The ColdFusion Context
Hi Ben, Thanks for this informative post. Now I am reading ur old posts too ... read »
Nov 21, 2009 at 10:56 AM
HostMySite.com Has The Best ColdFusion Hosting
@Mehul, Yes very nice people, however several downtimes per day which was not acceptable. Hence we had to move out. I am glad you are having good luck with them so far. ... read »