14

I'm ajaxifying some forms from a PHP application I didn't write. To do this, I came up with this clever solution:

jQuery("form").submit(function(event) {
    // get some values from elements on the page:
    var the_form = jQuery(this);
    var data = the_form.serialize();
    var url = the_form.attr( 'action' );
    var button = event.originalEvent.explicitOriginalTarget;

    data = data + "&" + button.name + "=" + button.value;

    // Send the data using post and put the results in a div
    jQuery.post( url, data, function() {
        //Do something crazy
    });

    // stop form from submitting normally
    if (event.preventDefault) 
    { 
        event.preventDefault(); 
    } 
    else 
    {
        event.returnValue = false; 
    }
});

Which works perfectly. I went away rejoicing. The problem is, I inadvertently used a Mozilla/Gecko only property to determine which button was clicked. (event.originalEvent.explicitOriginalTarget) Which means this only works in Firefox. :-(

All of this is necessary because the web app I'm augmenting relies on the button name/value being in the post data to process the form correctly. So, my question in simple terms would be:

What is the best, cross-browser way to determine which button was clicked in jQuery's submit event?

Edit: And here is my solution.

jQuery("some selector that targets your form").find(":submit").click(function(event) {
    // get some values from elements on the page:
    var the_form = jQuery(this).parents("form");
    var data = the_form.serialize();
    var url = the_form.attr( 'action' );
    var button = event.target;

    data = data + "&" + button.name + "=" + button.value;

    // Send the data using post and put the results in a div
    jQuery.post( url, data, function() {
        //Do something crazy
    });

    // stop form from submitting normally
    if (event.preventDefault) 
    { 
        event.preventDefault(); 
    } 
    else 
    {
        event.returnValue = false; 
    }
});
clifgriffin
  • 2,008
  • 3
  • 21
  • 41

4 Answers4

4

See this question: Crossbrowser equivalent of explicitOriginalTarget event parameter

You're going to have to attach the event listeners to the buttons instead of the form to get a good reliable way of determining which one fired the submit.

Community
  • 1
  • 1
greggreg
  • 11,945
  • 6
  • 37
  • 52
  • Thanks for the feedback, Greg...I thought about switching to this method, but it just feels messy to me. I'd rather not try to predict every element that could cause a form submit and attach an event to it if I don't have to. Just seems like something jQuery/JavaScript/browsers should support. – clifgriffin Apr 20 '11 at 15:38
  • 1
    Well, the outcome you're getting is what the functionality should be. You're asking for an event triggered by the form, not an event triggered by the button. The form listens for the button's event and then fires it's own. The proper thing to do in this case, would be to have the buttons fire an event, then for you to intercept that event and trigger the form. – greggreg Apr 20 '11 at 15:48
  • Fine, I agree...you're right. Thanks ;) – clifgriffin Apr 20 '11 at 17:44
1

Here is a function I used to "ajaxify" my forms with jQuery.

function ajaxifyForm(form, callback)
{
    var clicked

    form.find("button").click(function()
    {
        if (clicked != null) clicked.removeAttr("data-clicked")
        clicked = $(this)
        $(this).attr("data-clicked", 1)
    })

    form.submit(function(event)
    {
        var data = {}
        var name = ""

        form.find(":input").each(function()
        {
            var input = $(this)

            if (!(name = input.attr("name"))) return

            switch (input.attr("type"))
            {
                case "radio":
                case "checkbox":
                    if (input.attr("checked")) data[name] = input.val()
                    break
                case "submit":
                    if (input.attr("data-clicked")) data[name] = input.val()
                    break
                default:
                    data[name] = input.val()
            }
        })

        $.ajax({
            url: form.attr("action"),
            success: function(data)
            {
                if (typeof callback == "function") callback("success")
            },
            error: function()
            {
                if (typeof callback == "function") callback("error")
            },
            data: data,
            type: form.attr("method")
        })

        return false
    })

    return form
}
Shaddy Zeineddine
  • 467
  • 1
  • 5
  • 8
1

http://api.jquery.com/event.target/

jquery.event.target should work because it is normalised for most browsers.

jquery.event.currentTarget can be used to retrieve the current item in the event bubbling chain.

Edit-- After some reflection and @greg's suggestion: I've posted a code snippet on jsfiddle.

leon
  • 762
  • 1
  • 10
  • 24
  • event.target is the form...not the button that triggered the submit. – clifgriffin Apr 20 '11 at 15:22
  • (and event.target.currentTarget is undefined. event.currentTarget is also the form) – clifgriffin Apr 20 '11 at 15:30
  • I see I overlooked the event listner- I was assuming you were listening on the button and then submitting the form- my bad. As @greg mention, think it would even read better to listen on the button click. – leon Apr 20 '11 at 15:31
1

Using click handlers to submit the form is problematic beacuse you cannot use submit event handlers for validation (which is the way pretty much any validator plugin does it). Also, when you are not using AJAX to post, disabling submit buttons can have weird effects in some browsers if done in the click event and not the submit event.

The way jQuery.Form solves this is to set up a click handler which stores the clicked button (and then clears it with a small timeout), and use the submit handler to actually send the form contents via AJAX.

Tgr
  • 27,442
  • 12
  • 81
  • 118