Spot the error - it may not be what you think

I just helped a coworker diagnose this issue and it can be incredibly subtle if you aren't paying attention. Consider the following simple form:

<html>

<head> <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script> <script> $(document).ready(function() { $("#myButton").click(function() { $.post("test.cfc?method=whatever&returnformat=json", {}, function(res) { console.log('back from cfc'); },"json"); }); });

</script> </head>

<body>

<form method="post"> <input type="button" id="myButton" value="Click me like you mean it."> </form>

</body> </html>

I've got a button that - when clicked - will fire off an event jQuery is listening to. This event handler fires off a post to a CFC with the result then logged to the console. Works perfectly. Now let's tweak it just a tiny bit...

<html>

<head> <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script> <script> $(document).ready(function() { $("#myButton").click(function() { $.post("test.cfc?method=whatever&returnformat=json", {}, function(res) { console.log('back from cfc'); },"json"); }); });

</script> </head>

<body>

<form method="post"> <input type="image" src="meatwork.jpg" id="myButton" value="Click me like you mean it." > </form>

</body> </html>

Can you guess what happened here? Try to before scrolling down...






















So - when clicked - if you had your network tools open in Chrome or Firefox, you would see a quick glimpse of a request and then it would go away. Why? The image input type is actually like a submit button. Unlike type=button that does - well - nothing - the image type actually works much like a submit button. What happened was that the entire form posted. Easy to miss especially if you are testing locally. A quick fix is to just prevent the default behavior:

$("#myButton").click(function(e) { e.preventDefault(); $.post("test.cfc?method=whatever&returnformat=json", {}, function(res) { console.log('back from cfc'); },"json"); });

Anyone else ever get bitten by this?

Archived Comments

Comment 1 by todd sharp posted on 4/12/2011 at 9:09 PM

Wow - I'd have never spotted that. I mean, I noticed the src attribute, but I'd not have known it submits the form.

Side note - instead of e.preventDefault() could you not just return false in the click handler?

Comment 2 by Jeremy Battle posted on 4/12/2011 at 9:25 PM

I guessed right!

@todd - I think the general reason to e.preventDefault(); instead of return false; is that e.preventDefault() will not stop event propagation where as returning false will.

I might be wrong, but that is my understanding. In this situation though returning false would work just the same (no other events to propagate)

Comment 3 by Raymond Camden posted on 4/12/2011 at 9:28 PM

@Jeremy - that's my understanding too.

Comment 4 by todd sharp posted on 4/12/2011 at 9:30 PM

Ahh... fantastic - did not know that. Thanks guys.

Comment 5 by Mihai Baboi posted on 4/12/2011 at 10:36 PM

I've had something that's a bit like this. I was firing a .click() function on a check box and it was conflicting with the native click event in the browser. Had a bit of a headache before figuring it out.

Comment 6 by Peter Boughton posted on 4/13/2011 at 1:44 AM

There's another similar problem, if you use the button tag.

Change this code:
<input type="button" id="myButton" value="Click me like you mean it.">

To this:
<button id="myButton">Click me like you mean it.</button>

And you have the same issue.

For some stupid reason, the idiots at the W3C defined the default type for button tag as submit, so to get a non-submitting button you need to specify the type:

<button type="button" id="myButton">Click me like you mean it.</button>

Which is annoyingly repetitive.

Comment 7 by Mike posted on 4/13/2011 at 7:00 PM

Couldn't you also just remove the input type and just have an image with that ID, of course you would have to style it for the pointer but I think it would work the same wouldn't it ?

Comment 8 by Peter Boughton posted on 4/13/2011 at 7:04 PM

It wont work if JS is disabled, whereas with input type=image you can put fallback functionality behind the form submission.

Comment 9 by Rodion Bykov posted on 4/13/2011 at 9:34 PM

Interesting what statistic there is on 'JS disabled'. To me it has no sense in year 201x. I also agree on simpler <img onclick=""> solution.

Comment 10 by Raymond Camden posted on 4/13/2011 at 9:36 PM

Just to be clear - it wasn't so much that there was an easy work around - but that the initial issue was easy to miss. :)

Comment 11 by Edward - Florida SEO posted on 4/15/2011 at 4:06 AM

Here's an apropos link to a great post on event.preventDefault() vs return false ... it simply supports what Jeremy stated and even goes a bit further to show how return false actually fires three bubbling events in it's wake ...

1. event.preventDefault();
2. event.stopPropagation();
3. Stops callback execution and returns immediately when called.

Here's the link for more FYI ...

http://fuelyourcoding.com/j...