jQuery's Event Triggering, Order Of Default Behavior, And triggerHandler()

Posted March 12, 2009 at 10:20 AM by Ben Nadel

Tags: Javascript / DHTML

I ran into a strange, rather undesirable behavior this morning when dealing with jQuery's manual event triggering. It seems that when you trigger the click event on a checkbox, the default behavior (toggling the checked attribute) takes place after event handlers fire. This is opposite of what happens when the browser naturally handles the checkbox click event - the checked attribute is toggled and then the event handlers are executed.

This difference in the order of operations between inherent clicking and jQuery's event triggering can cause a big problem if the event handler depends on the status of checkbox in anyway (which, if you have a click handler on a checkbox to begin with means it probably does).

To see this in action, take a look at this simple code:

  • <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  • <html>
  • <head>
  • <title>jQuery Trigger and Default Behavior Demo</title>
  •  
  • <script type="text/javascript" src="jquery-1.3.2.js"></script>
  • <script type="text/javascript">
  •  
  • // When the DOM is ready, initialize.
  • $(
  • function(){
  •  
  • $( "input" ).click(
  • function( objEvent ){
  •  
  • // Alert the checkboxed status.
  • alert( this.checked );
  •  
  • }
  • );
  •  
  •  
  • $( "button" ).click(
  • function( objEvent ){
  •  
  • // Trigger click on the checkbox.
  • $( "input" ).click();
  •  
  • }
  • );
  •  
  • }
  • );
  •  
  • </script>
  • </head>
  • <body>
  •  
  • <h1>
  • jQuery Trigger and Default Behavior Demo
  • </h1>
  •  
  • <form>
  • <input type="checkbox" />
  • <button>click checkbox</button>
  • </form>
  •  
  • </body>
  • </html>

If I refresh this above code and click on the checkbox directly, it alerts:

true

... because the checkbox's checked attribute is toggled before the click handler executes. If, however, I refresh the page (hard refresh to reset form) and click on the button to manually trigger the click handler on the checkbox, it alerts:

false

This is because the manually triggered event handlers fire before the default behavior takes place.

To get around this, I had to manually change the checkbox and then trigger ONLY the event handlers:

  • <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  • <html>
  • <head>
  • <title>jQuery Trigger and Default Behavior Demo</title>
  •  
  • <script type="text/javascript" src="jquery-1.3.2.js"></script>
  • <script type="text/javascript">
  •  
  • // When the DOM is ready, initialize.
  • $(
  • function(){
  •  
  • $( "input" ).click(
  • function( objEvent ){
  •  
  • // Alert the checkboxed status.
  • alert( this.checked );
  •  
  • }
  • );
  •  
  •  
  • $( "button" ).click(
  • function( objEvent ){
  •  
  • // Toggle checkbox checked status.
  • $( "input" )[ 0 ].checked = !$( "input" )[ 0 ].checked;
  •  
  • // Trigger ONLY click event hanlders on the checkbox.
  • $( "input" ).triggerHandler( "click" );
  •  
  • }
  • );
  •  
  • }
  • );
  •  
  • </script>
  • </head>
  • <body>
  •  
  • <h1>
  • jQuery Trigger and Default Behavior Demo
  • </h1>
  •  
  • <form>
  • <input type="checkbox" />
  • <button>click checkbox</button>
  • </form>
  •  
  • </body>
  • </html>

jQuery's triggerHandler() method allows us to trigger the jQuery-bound event handlers without triggering the default behavior of the browser (toggle checked status). In doing so, the click handler on the checkbox itself can now function uniformly no matter how it is triggered.

I love jQuery event binding and triggering; I think this is the only place where this anomaly will actually matter as this is the only event type that I can think of that has to change the DOM before it executes its handlers.




Reader Comments

Mar 18, 2009 at 10:18 AM // reply »
1 Comments

Hi,

Thanks I think that's exactly what I need. I guess nobody is aware of a jQuery plugin solution to this problem so far?


Mar 21, 2009 at 5:58 AM // reply »
1 Comments

Hi,

Thanks for sharing this precious information, I was banging my head against the wall to solve this nagging problem. I think that's exactly what I needed.

Thanks once again!

Mohammed.


Mar 21, 2009 at 2:00 PM // reply »
11,246 Comments

@Mohammed,

Glad to help out :)


Mar 28, 2009 at 6:51 AM // reply »
2 Comments

another way:

$(document).ready(function(e){
$("body").append("<input type='checkbox'/>");

$(":checkbox").click(function(e){
this.checked=!this.checked;
}).click(function(e){
alert(this.checked);
}).click(function(e){
this.checked=!this.checked;
});

$("body").append("<input type='button' value='toggle'/>");
$(":button").click(function(e){
$(":checkbox").click();
});
});


Mar 28, 2009 at 6:56 AM // reply »
2 Comments

sorry,it seems not work correctly when actually click the checkbox.


Aug 28, 2009 at 10:29 AM // reply »
1 Comments

Exactly the information I was looking for.

Toggling the checked state, then using triggerHandler() worked perfectly.

Thanks for this.


Sep 2, 2009 at 9:38 AM // reply »
11,246 Comments

@llade,

No worries my man. It's a weird situation.

@Jeff,

Glad to help out.


Nov 24, 2009 at 11:50 AM // reply »
1 Comments

Thanks Ben, exactly the explanation I was looking for -- and not the first time you've helped me out here.


Nov 24, 2009 at 1:21 PM // reply »
11,246 Comments

@John,

Sweeeet. Always glad to help out.


Jan 12, 2010 at 6:03 AM // reply »
1 Comments

Thank you for clarification of this weird behavior.

However, since my case is using radio button and can't use yours approach (having to uncheck all other radio, not just toggle "checked" status)

=======================================

For folks who came across same problem. try this

$("yours_selector").each(function(){this.click()});

or

$("yours_selector")[0].click();

---------------------------------------------------------------------

it's click function as in "mouseclick in browser" and will behave as you would expected

=======================================


Jan 13, 2010 at 8:18 PM // reply »
11,246 Comments

@WallSky,

If you want to check the radio button, you can also just check it explicitly if you want:

$("yours_selector").attr( "checked", "true" );

... that should work for all of them. Of course, you lose the ability to perform additional logic in thee iteration.

You can also reach back into the raw DOM element:

$("yours_selector").each(function(){
this.checked = true;
});

Just presenting options (not saying any of them is any better). All depends on what you want to do.


Feb 4, 2010 at 3:19 PM // reply »
1 Comments

thanks for the tip! I run into the same problem.


Mar 3, 2010 at 6:43 PM // reply »
1 Comments

Has anyone else tried this under firefox? firefox resets the checkbox to false after the message box, IE works fine.

Great Article!


Mar 18, 2010 at 4:04 PM // reply »
1 Comments

Tks! You saved-me today.

it can be chained into one statement:

$("#x).attr("checked","checked").triggerHandler('click');


Mar 19, 2010 at 8:33 AM // reply »
11,246 Comments

@Alex,

The beauty of jQuery method chaining.


May 9, 2010 at 5:38 PM // reply »
1 Comments

I was fighting the same problem but found a (simpler) solution that seems to work the same. I have tested in Firefox 3 and IE8.

Instead of firing the click() function on the jQuery object by using $(element).click(), I use the javascript native click() trigger on the DOM object: element.click().

This does toggle the checkbox state before running the click event handler.


May 10, 2010 at 9:43 PM // reply »
11,246 Comments

@Bart,

Ahh, good to know, thanks.


May 11, 2010 at 10:45 AM // reply »
1 Comments

This just saved me. Thanks for the write up!


Aug 3, 2010 at 12:19 PM // reply »
1 Comments

@Ben -- thanks, I just ran into this as well, and this saved me from banging my head against the wall.

Funny that they haven't addressed this officially yet, though I haven't checked to see if this issue is in their trac...

@Bart -- interesting...I'll try your suggestion as well


Aug 3, 2010 at 9:30 PM // reply »
11,246 Comments

@Greg,

I go back and forth as to whether this should be considered a bug. In general, I try to shy away from mimicking a user-behavior that is to be generated by the UI.


Jan 10, 2011 at 11:53 PM // reply »
1 Comments

Yes, thanks for writing this up! I combined tips, and changed this code which suffers from the problem:

  • $( '.checkbox-class' ).click();

to this, which doesn't:

  • $( '.checkbox-class' ).each( function() { this.click(); } );


Jan 13, 2011 at 9:51 PM // reply »
1 Comments

What happens if you use the Change event instead of Click? Eg:

$( "button" ).change(


May 5, 2011 at 6:50 AM // reply »
1 Comments

Two sets of thanks to hand out!

Thanks to Ben for being on page 1 when I was Googling this problem, and thanks to Bart for the native JS suggestion.

While both solutions work I think defaulting to a native JS click is cleaner than messing about with manually switching checked status.

Thanks again.


May 12, 2011 at 11:58 AM // reply »
1 Comments

Thanks for the article. This provided a solution to a frustrating problem. Used triggerHandler() and all is well.

Thanks again!


Oct 14, 2011 at 10:31 AM // reply »
1 Comments

Pheiw! I am thankful I found this page. I think jquery should rectify this - but thanks for the solution:


Mar 16, 2012 at 5:45 AM // reply »
1 Comments

will anyone help to solve this solution..

My button in the page contains two event click functions. Defaultly first event is execting first and the second one continues..

But i need to fire the second function first before the first one


Apr 2, 2012 at 3:46 PM // reply »
1 Comments

Thanks!

I had to spend lot of hours trying to figure out different solutions but I ended frustated because I unknown the root of the problem in the first place.

Your explanation help me a lot and save my day :D


Aug 27, 2012 at 4:34 AM // reply »
1 Comments

Thx from Mauritius.



Post A Comment

Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Supported HTML tags for formatting: <strong>bold</strong>   <em>italic</em>   <code>code</code>







  • Help Wanted - Find Your Next ColdFusion Job
Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
May 24, 2013 at 11:21 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@WebManWalking, Ha ha, let's us never speak of justifying "##" notation again :P ... read »
May 24, 2013 at 11:18 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Ben, Ah, so it was indeed how I vaguely remembered it to be: A direct assignment value = users.id[ i ] causes value to retain the sticky datatype of the query column. Although unnecessary in ... read »
May 24, 2013 at 9:11 AM
Preventing Links In Standalone iPhone Applications From Opening In Mobile Safari
@Brandon, Hi, No, I haven't been able to do that. I have just kept it as it is. ... read »
May 23, 2013 at 9:52 PM
Preventing Links In Standalone iPhone Applications From Opening In Mobile Safari
@Muhmmadibn Did you figure out a solution to launching PDFs? I am running into the same issues myself. There is no way to close the PDF or go back once you launch it. Thanks in advance! ... read »
May 23, 2013 at 6:06 PM
The Girl Who Broke My Heart, And Made Me A Better Person
Good day,ladies and gentle men, my name is Dr AMADI the great spell caster in Africa, i have help so many people for different kind of problems,who say there is no solution to problems on earth, that ... read »
May 23, 2013 at 4:26 PM
ColdFusion QueryAppend( qOne, qTwo )
@Heather, Glad people are still getting value out of this! ... read »
May 23, 2013 at 3:49 PM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@WebManWalking, I meant the code at the bottom (not the video). I did try to experiment with an intermediary variable, like: value = users.id[ i ]; arrayContains( userIDs, value ); ... but t ... read »
May 23, 2013 at 11:06 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Ben, Are you talking about As Number: YES As String: YES As Java: YES? If so, that's with 3 different ways of referencing the constant 1, not users.id[1]. Query object references(*) are what seem ... read »
InVision App - Prototyping Made Beautiful With Prototyping Tools