Workaround: Form Submit Method Doesn’t Fire Submit Event

May 14th, 2009 by Brian Leave a reply »

Recently I've become even more frustrated than I've been in the past with the disparity between Javascript Events and Javascript Event Methods.  This time it was specifically related to form submission.  Currently, despite the specification stating otherwise, browsers do not fire a submit event when a form is submitted via the submit() method.  Of course, this means if you have an event listener tied to that submit event (say, for form validation) it will never happen because the event hasn't been fired.  However, we can work around this limitation using a little Javascript magic!

Essentially, we can overcome this limitation by using the dynamic nature of Javascript to our advantage.  Even methods that are "native" to the browser can be extended by wrapping them.  As usual, there are some differences between browsers we have to account for.  Most notably, Firefox and other browsers that adhere to the DOM Level 2 Specification support reading and writing to the HTMLFormElement object.  IE, naturally, does not allow this so we have to take a more direct approach. In the example code, we use Prototype's Element#fire method which only allows for custom events. However, you could replace that by generating a standard event manually.

function fakeSubmit(event) {
    var target = event ? event.target : this;
    /* Fire a submit event */
    $(target).fire("form:submit");
    /* Call the real submit function */
    this._submit();
}

if(window.HTMLElement){
    /* DOM-compliant Browsers */
    HTMLFormElement.prototype._submit = HTMLFormElement.prototype.submit;
    HTMLFormElement.prototype.submit = fakeSubmit;
} else {
    /* IE does not expose it's HTMLElement object or its children
        Let the document load so all forms are accounted for */
    document.observe("dom:loaded", function(){
        for(var i=0; i<document.forms.length; i++){
            document.forms[i]._submit= document.forms[i].submit;
            document.forms[i].submit = function(event){
                var target = event ? event.target : this;
                $(target).fire("fake:submit")
                /* Call the real submit function */
                this._submit();
            }
        }
    });
}

With this in place, we can simply watch for our fake:submit event to fire and act upon it.  As I said previously, you could generate the submit event and fire it manually if you want to stick to using standard events--but this solution takes some of the cross-platform issues out of the way for you.

Edit: I've updated this code to fix a bug in IE6.  

  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Reddit
  • StumbleUpon
  • Technorati
  • Twitter
  • DZone
  • HackerNews
Advertisement
  • Hi Paul,

    I'm glad you found this solution helpful! Two things:

    1. Yes, that was a typo.

    2. In my original implementation, the two functions were not identical due to a mistake on my part in the implementation for IE (that worked about 50% of the time). When I went back and corrected the problem, I didn't look to see if the two were identical (which, minus the typo, they were indeed).
  • Nice article; this is exactly the problem I'm dealing with today. Well written with a solid solution.

    I notice that in your code you are form a 'form:submit' event for DOM compliant browsers and a 'fake:submit' event for IE. I believe this is a typo?

    In in the process of incorporating this to my project, I made a couple of additional tweaks. (1) I added the ability to stop the event in one of the handlers; the browser submit fires either way in the above code. (2) The inline function you use to attach to each form is (if my above typo assumption is correct) identical to fakeSubmit, so I just use a reference to that function which is a bit more DRY.

    Here is the revised code:


    function fakeSubmit(event) {
    var target = event ? event.target : this;
    /* Fire a submit event */
    var fakeEvent = $(target).fire("form:submit");
    if (fakeEvent.stopped == false) {
    /* Call the real submit function */
    this._submit();
    }
    }

    if(window.HTMLElement){
    /* DOM-compliant Browsers */
    HTMLFormElement.prototype._submit = HTMLFormElement.prototype.submit;
    HTMLFormElement.prototype.submit = fakeSubmit;
    } else {
    /* IE does not expose it's HTMLElement object or its children
    Let the document load so all forms are accounted for */
    document.observe("dom:loaded", function(){
    for(var i = 0; i < document.forms.length; i ){
    document.forms[i]._submit = document.forms[i].submit;
    document.forms[i].submit = fakeSubmit;
    }
    });
    }
blog comments powered by Disqus