Forum Moderators: open

Message Too Old, No Replies

That Nasty onsubmit() Problem

Ya Learn Something New Every Day. Now, How Can I Work Around It?

         

cmarshall

9:18 pm on Jan 31, 2007 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



I just learned the other day that relying on a form.onsubmit() handler has a nasty issue: It won't get called if JavaScript is used to call form.submit().

So far, that hasn't been a problem for me, but that can be attributed to sheer blind luck more than anything else. If I had encountered this, there would be a big bloody dent in my wall.

So, given this code:

<form action="#" method="get" id="my_form"><div>
<input type="button" id="fake_submit_button" value="Submit By JavaScript" onclick="this.form.submit()" />
<input type="submit" id="submit_button" value="Submit For Real" />
<script type="text/javascript">
// <![CDATA[
function onsubmithandler(){ alert ( "Submit Event Caught!" ); }
document.getElementById('submit_button').form.onsubmit=onsubmithandler;
// ]]>
</script>
</div></form>

The onsubmithandler() function will not be called when you press the "Submit By JavaScript" button.

I'd like to be able to wire the form object to call the onsubmit() handler by default, so I have a "generic" capability. Otherwise, I have to do this:

<form action="#" method="get" id="my_form"><div>
<input type="button" id="fake_submit_button" value="Submit By JavaScript" onclick="[i][b]this.form.onsubmit();[/b][/i]this.form.submit()" />
<input type="submit" id="submit_button" value="Submit For Real" />
<script type="text/javascript">
// <![CDATA[
function onsubmithandler(){ alert ( "Submit Event Caught!" ); }
document.getElementById('submit_button').form.onsubmit=onsubmithandler;
// ]]>
</script>
</div></form>

Any ideas?

UPDATE

This will work (in FF2, I need to test it in a bunch of other browsers):

<form action="#" method="get" id="my_form"><div>
<input type="button" id="fake_submit_button" value="Submit By JavaScript" onclick="this.form.submit()" />
<input type="submit" id="submit_button" value="Submit For Real" />
<script type="text/javascript">
// <![CDATA[
function onsubmitrouter(){ this.onsubmit();this.originalsubmit() }
function onsubmithandler(){ alert ( "Submit Event Caught!" ); }
document.getElementById('submit_button').form.onsubmit=onsubmithandler;
document.getElementById('submit_button').form.originalsubmit = document.getElementById('submit_button').form.submit;
document.getElementById('submit_button').form.submit = onsubmitrouter;
// ]]>
</script>
</div></form>

coopster

11:00 pm on Jan 31, 2007 (gmt 0)

WebmasterWorld Administrator 10+ Year Member



Must be referring to this discussion
[webmasterworld.com...]

Why not just use a general checkForm() function for both onsubmit() and onclick() actions for any/all the buttons? Within the checkForm() you could execute functions for specific buttons by checking if they were clicked. Wouldn't that be easier? Or am I missing something here?

cmarshall

3:02 am on Feb 1, 2007 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



I'm not worried as much about "easier," as I am about "complete." I write code that is often used by others, and I would hate to give them something that might cause agita.

I boiled it down to something like this:

<form action="#" method="get" id="my_form"><div>
<input type="button" id="fake_submit_button" value="Submit By JavaScript" onclick="this.form.submit()" />
<input type="submit" id="submit_button" value="Submit For Real" />
<script type="text/javascript">
// <![CDATA[
function onsubmithandler(){ alert ( "Submit Event Caught!" ); }
function SetOnSubmitHandler(inForm, inHandler){
inForm.onsubmit=inHandler;
inForm.originalsubmit = inForm.submit;
inForm.submit = new Function('this.onsubmit();this.originalsubmit()');
}
SetOnSubmitHandler(document.getElementById('submit_button').form,onsubmithandler);
// ]]>
</script>
</div></form>

This makes it fairly straightforward. I need to test on a bunch of browsers. My biggest concern is that the onsubmit() handler is only called once.

However, you told me about this issue. Is there a form.checkForm() method? I was always told to use the onsubmit() method for form checking.

Fotiman

3:36 pm on Feb 1, 2007 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



This might be a dumb question... but why are you using JavaScript to submit the form in this case? Why not just make it a submit button?

cmarshall

3:55 pm on Feb 1, 2007 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Not a dumb question at all. The answer is not obvious.

I tend to write sites that I want to hand to others to use. Basically, they act as "platforms." The better a job I do of providing a usable site, the less likely I am to be plagued with questions and support requests.

I also like to have an array of tools on hand that can be repurposed.

You can do a lot of kewl trix with JavaScript. I don't like to rely on it as the only answer to navigation, but you can offer enhanced functionality and a slicker user experience.

The most common and obvious use is to have a form submit when a popup is changed. I hide a submit input in <noscript></noscript> elements for the non-JavaScript-studly visitor.

geofflee

10:50 pm on Feb 1, 2007 (gmt 0)

10+ Year Member



Sometimes, you will want your onsubmit function to return false so that you stop the browser from submitting. This is used in user input validation scripts. I've slightly modified your code to handle this (tested in IE7):


<form action="about:blank" method="get" id="my_form"><div>
<input type="button" id="fake_submit_button" value="Submit By JavaScript" onclick="this.form.submit()" />
<input type="submit" id="submit_button" value="Submit For Real" />
<script type="text/javascript">
// <![CDATA[
function onsubmithandler(){ alert ( "Submit Event Caught!" ); return false; }
function SetOnSubmitHandler(inForm, inHandler){
inForm.onsubmit=inHandler;
inForm.originalsubmit = inForm.submit;
inForm.submit = new Function('return this.onsubmit() && this.originalsubmit();');
}
SetOnSubmitHandler(document.getElementById('submit_button').form,onsubmithandler);
// ]]>
</script>
</div></form>

cmarshall

11:38 pm on Feb 1, 2007 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Excellent note Geoff! I appreciate it.

geofflee

11:46 pm on Feb 1, 2007 (gmt 0)

10+ Year Member



Hmmm, a nasty side-effect I noticed is that my changes will stop the submission if there is no return line in the onsubmit function. When this happens, the function will return undefined, so we can fix things by simply taking this extra case into account:


<form action="about:blank" method="get" id="my_form"><div>
<input type="button" id="fake_submit_button" value="Submit By JavaScript" onclick="this.form.submit()" />
<input type="submit" id="submit_button" value="Submit For Real" />
<script type="text/javascript">
// <![CDATA[
function onsubmithandler(){ alert ( "Submit Event Caught!" ); return false; }
function SetOnSubmitHandler(inForm, inHandler){
inForm.onsubmit=inHandler;
inForm.originalsubmit = inForm.submit;
inForm.submit = new Function('var onsubmit; var originalsubmit;' +
'return ((onsubmit = this.onsubmit()) ¦¦ onsubmit == undefined) && ' +
'((originalsubmit = this.originalsubmit()) ¦¦ originalsubmit == undefined);');
}
SetOnSubmitHandler(document.getElementById('submit_button').form,onsubmithandler);
// ]]>
</script>
</div></form>

Note that an intended side-effect of this code is that the original onsubmit function will not execute if the new onsubmit function returns false. This is because the && operator sees false in the left-operand and knows that the expression will evaluate to false regardless of the value of the right-operand. This side-effect can be removed by simply evaluating both operands before plugging it into the && operator.

If you wanted this code to be even more powerful, you could create separate functions that insert the new onsubmit function either at the beginning or end because order matters. Something like 'SetOnSubmitHandlerBefore' and 'SetOnSubmitHandlerAfter', and then functions to remove those handlers. That would be library-quality code, imho. However, if you want to do this, it would be better to put all the onsubmit functions in a list rather than generating a new function each time, or it would be inefficient (more function calls than necessary) and difficult to do the removal functions.