The form that left

The other day, I encountered a form submission behavior I didn’t expect, nor don’t have any solution for.

Take this obtrusive code example (same thing happens with unobtrusive approaches):

<span onclick="document.getElementById('da-form').submit();">Spank the monkey</span>

<form id="monkey" action="somewhere.php" method="get" onsubmit="return alert('Spanked')">

When the submit() method of a form element is called, it doesn’t trigger the onsubmit event handler(s) applied to the form. This goes for all web browsers, quirks and standards mode.

Yes, it really is so

After discussing with lots of people, who were as surprised as me, I found this excerpt from David Flanagan’s excellent JavaScript book, JavaScript – The Definitive Guide:

Calling the submit( ) method of a form does not trigger the onsubmit handler.

So, this behavior is confirmed, although I can’t seem to find any explanation as to why it is so. And this really baffles me, since there are definitely scenarios where one would like to validate the actual form input before submitting it.

Where it becomes a real problem

But how about just validating it before you call the submit() method, you say?

In an application where you have control, sure. But consider all .NET and MOSS applications out there which rely on a hideous feature called __doPostBack. It’s a JavaScript function, probably written in the last century, which calls the submit() methods of form elements to submit them, and, trust me, this usage is very well spread over the Internet.

And in those scenarios, there’s no way to perform a proper client-side validation, unless you rely on some of those frameworks’ own validation methods, which completely takes away the chance to write general and optimized validation scripts, centrally located in a JavaScript file which will be cached for all pages on the web site in question

An alternative solution

I was talking to my Canadian friend Jonathan Snook, and he proposed a solution:

Override the __doPostBack function in every page, but save a reference to it, so you can call it later. For example:

var oldDoPostBack = __doPostBack;
function __doPostBack (eventTarget, eventArgument) {
    // Do your own validation first
    if (itsActuallyvalid()) {
        oldDoPostBack(eventTarget, eventArgument);
    }
}

Happy workarounding! 🙂

13 Comments

  • I had a similar situation which I dealt with by for any form (f) I wanted to validate adding this.

    <code>f._submit = f.submit;

    f.submit = function() { if ( validator.validate( this ) ) { this._submit() } };</code>

    Then if the <code>form.submit</code> method is required (and some of the older parts of the app I work on do use it) the validation would still kick in.

  • I have a variant of Simon's.

    // Make sure that onsubmit is called even if f.submit() is called.

    f._submit = f.submit;

    f.submit = function() {

    if (f.onsubmit) {

    var retVal = f.onsubmit();

    if (typeof retVal == "boolean" && retVal == false) {

    return false;

    }

    }

    this._submit();

    };

    Overwrite the submit() function with a function that checks to see if there is an onsubmit function. If there is, run onsubmit() then f._submit() if false isn't returned.

    The problem is that I don't think with __doPostBack you may want to call your form validation. The __doPostBack function may be called to retrieve some information, fill in a list, … Is there a way to know if __doPostBack is doing the final submit, which is where you would want to do your validation?

  • Milo says:

    Calling click() on a button also doesn't fire the button's OnClick handler. Changing selectedIndex on a select also doesn't fire the select's OnChange handler.

    Event handlers simply don't fire unless the User performs the action. C'est la vie.

  • Phil Nash says:

    I came across this exact problem yesterday myself. Not to the extent of the .NET issue, but I was surprised to see that calling form.submit() didn't trigger onsubmit.

    Even weirder was the fact that I was using an object that I'd called validator and its method validate() as in Simon Proctor's comment. If I'd just read this post a day earlier I'd have had no problem!

    I do like the idea of hijacking the form.submit() function to include the validation. It seems less obtrusive than just ensuring you validate before you submit, which was the solution I finished with yesterday.

  • Andy says:

    Lo and behold, the one-liner:

    <code>if (form.onsubmit()) form.submit();</code>

  • Robert Nyman says:

    Simon, Tanny,

    Your approach is definitely better. Override the <code>submit</code> to do what you want,k such as validation, and then perform a call, through a saved reference, to the native method.

    Milo,

    Very true and very interesting. I mean, I agree, that's life, but since this is apparently intentional behavior, I would really like to learn the mindset behind it.

    Phil,

    Hope it helps!

    Andy,

    Well, that is clever, but it takes for granted that a <code>onsubmit</code> event has been applied; therefore, a check should be first if any event handler exists. And, what happens if you have multiple event handlers? (just talking and thinking, without actually testing now 🙂 )

  • Diego Perini says:

    Simon,

    your method is the working one, I was battling with that too in my event delegation code. The requested functionality here is being able to catch the "form.submit()" that may have been invoked by javascript programmatically, so overwriting that method may be the best cross-browser solution.

    Robert,

    for W3C browsers that exposes this method it will be possible to prototype the HTMLFormElement, this could be a solution when having multiple forms to fix on the same page; no way to do that in IE that I know.

  • Robert Nyman says:

    Diego,

    Absolutely, using the <code>HTMLFormElement</code> is a great way to do it, but always leaves us with IE and how to solve it there. I don't know of an equivalent workaround either.

  • Diego Perini says:

    Robert,

    I was reviewing some of my old work on forms and found something which is usable. It turns out that in IE this trick is also possible.

    Just go through all the forms in the document and overwrite the "submit" methods to refer to the validator function. This must be done before "onload".

    If you want to cancel the "onsubmit", capture the "onclick" event at the document level and cancel it's "default action" by setting:

    event.returnValue = false;

    Trapping "mousedown" or "mouseup" will not work as with "onclick".

    I will soon post my "formsCop" in my lab site, I have actually revived it.

    Diego Perini

  • Robert Nyman says:

    Diego,

    Sounds interesting.

    However, this brings an extra dimension, that for one isn't that good, to event delegation, just to solve a fairly small problem in a general way.

    However, there are cases where it could indeed be proved useful, so please let me know about your solution when it is put together.

  • Diego Perini says:

    Robert,
    here are my tests as promised, I know you are busy now, so these can wait until you are back.

    Anyway if somebody else reading can give a look and help find some inconsistency it would let me improve it, maybe other situation that it doesn’t actually cover.

    http://javascript.nwbox.com/formsCop/

    There are two versions, the oldest one (0.5) that I tweaked a bit to log information, and the newer one (0.8) that I am in the process of updating as soon as I find more misbehaviors / bugs.

    Both will now log to Firebug console and window status, so enable “writing to status bar” in your browser if you want to see some info
    or open the Firebug console.


    Diego Perini

  • Robert Nyman says:

    Diego,

    Thanks for sharing!

    I'll try to take a look at in the future.

  • […] The form that left – Robert’s talk – Web development and Internet trends […]

Leave a Reply

Your email address will not be published. Required fields are marked *