Workaround: Form Submit Method Doesn't Fire Submit Event

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.