如何从表单提交事件中获取触发提交的按钮?

126
我想要找到触发表单提交的提交按钮的值。
$("form").submit(function() {

});

我可以为每个按钮触发$("input[type=submit]").click()事件并设置一些变量,但这似乎不如在提交时从表单中某处获取按钮优雅。


2
请注意,并不一定有这样的按钮。脚本可能会执行formobj.submit()。我认为点击事件是最好的方法。 - Jason Orendorff
2
对于现代浏览器,现在有event.submitter,它可以提供被点击的<button><input type="submit"> - gfv
2
event.originalEvent.submitter - Empty Brain
2
请注意,苹果Safari浏览器在这里也不应被视为现代浏览器! - Mikko Rantalainen
22个回答

115

我利用了document.activeElement,正如在这个答案中所描述的:How to get the focused element with jQuery?

$form.on('submit', function() {
    var $btn = $(document.activeElement);

    if (
        /* there is an activeElement at all */
        $btn.length &&

        /* it's a child of the form */ 
        $form.has($btn) &&

        /* it's really a submit element */
        $btn.is('button[type="submit"], input[type="submit"], input[type="image"]') &&

        /* it has a "name" attribute */
        $btn.is('[name]')
    ) {
        console.log("Seems, that this element was clicked:", $btn);
        /* access $btn.attr("name") and $btn.val() for data */
    }
});

我利用按钮在点击后始终处于焦点状态这一事实。如果您在单击后立即执行blur(),则此方法将不起作用

@Doin发现了另一个缺点。如果用户通过在文本字段中按enter提交表单,则document.activeElement未设置。您需要自己处理input[type="text"]和类似元素上的keypress事件,以避免出现此问题。

更新2017-01:对于我的库Hyperform,我选择不使用activeElement,而是捕获所有导致表单提交的事件。代码在Github上

如果您恰好使用Hyperform,则可以通过以下方式访问触发提交的按钮:

$(form).on('submit', function(event) {
  var button = event.submittedVia;
});

1
MDN 表示支持。 (FF3, IE4, Chrome 2, Safari 4, Opera 9) - Boldewyn
1
这应该是答案。 - Royi Namir
2
这个表单如果通过[Enter]提交,会起作用吗?规范说明应该等同于点击默认的提交按钮,但我不确定它是否仍然保持为活动元素。 - Doin
9
很遗憾,你不能仅依靠“activeElement”,因为“提交者”可以通过以下方式定义:A. 通过真实的点击激活(直接用鼠标或键盘空格/回车键点击);B. 通过合成点击激活(buttonElement.click()buttonElement.dispatch(new MouseEvent(...)),不获取焦点);C. 通过表单隐式提交(与其他表单字段的键盘交互);D. 不提交到任何地方,当使用formElement.submit() - mems
7
提醒一下,为了保持最新状态。截至目前,当在Safari浏览器上点击提交按钮时,activeElement会被设置为文档主体。我在Safari 9.1上遇到了这个问题。 - coderkevin
显示剩余9条评论

34

我实现了这个,我想它会起作用。

$(document).ready(function() {
    $("form").submit(function() { 

    var val = $("input[type=submit][clicked=true]").val()

    // DO WORK

});

这是设置提交按钮事件的代码

$("form input[type=submit]").click(function() {
    $("input[type=submit]", $(this).parents("form")).removeAttr("clicked");
    $(this).attr("clicked", "true");
});

谢谢回复,但这并不是非常不优雅的...


1
不支持跨浏览器/多个提交按钮。 - user1610743
2
点击事件在表单提交事件之前被触发是有保证的吗?我建议给表单添加一个“doNotSubmit”类,在提交函数内检查该类是否存在(否则不要提交),在点击事件中删除表单类“doNotSubmit”,并在添加提交按钮值作为隐藏字段后执行$(''.myForm').trigger('submit')。 - Hafenkranich
2
@Hafenkranich 在Chrome浏览器上,有时会在提交事件之后触发单击事件,这种情况很少见。 - romanoza
@roamnoza - 没有意义。必须先触发点击事件,以便处理程序有机会防止默认行为。 - Joel
2
@j4k3 有更好的解决方案吗?这个答案已经8年了。 - hunter
显示剩余2条评论

26

现在,在提交事件中有一个标准的submitter属性。
已经在Firefox 75和Chrome / Edge 81中实现!

document.addEventListener('submit',function(e){
    console.log(e.submitter)
})

对于不支持它的浏览器,可以使用这个 polyfill
注意:如果你的目标浏览器是旧版本,则需要polyfill其他东西,比如 closest matches 。并确保在添加提交事件之前加载polyfill。

注:polyfill指的是一种用来模拟浏览器原生API功能的代码库。
!function(){
    var lastBtn = null
    document.addEventListener('click',function(e){
        if (!e.target.closest) return;
        lastBtn = e.target.closest('button, input[type=submit]');
    }, true);
    document.addEventListener('submit',function(e){
        if ('submitter' in e) return;
        var canditates = [document.activeElement, lastBtn];
        lastBtn = null;
        for (var i=0; i < canditates.length; i++) {
            var candidate = canditates[i];
            if (!candidate) continue;
            if (!candidate.form) continue;
            if (!candidate.matches('button, input[type=button], input[type=image]')) continue;
            e.submitter = candidate;
            return;
        }
        e.submitter = e.target.querySelector('button, input[type=button], input[type=image]')
    }, true);
}();

8
请注意,苹果Safari浏览器仍不支持此功能! - Mikko Rantalainen
请注意,上述 polyfill 需要 UA 实现 Element.closest()、Element.matches() 和 Element.querySelector()。还有可能 UA 在调用 您的“submit”处理程序之前调用 polyfill 中的“submit”处理程序,因此您看不到 .submitter 值。 - Mikko Rantalainen
谢谢Mikko,我添加了一条注释来避免这些问题。 - Tobias Buschor
Polyfill 还应该检查按钮是否实际匹配要提交的表单。如果页面包含两个表单,第一个表单的按钮点击可能会被上面的 Polyfill 提交到第二个表单。还要注意,根据 WHATWG 规范,如果直接调用 form.submit(),则可能会缺少 submitter。 - Mikko Rantalainen
是的,我现在将lastBtn重置为null。 我无法理解规范中form.submit()的行为,因为调用form.submit()不会触发submit监听器。 你能指导我查看规范吗? - Tobias Buschor
https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#concept-form-submit 的第4步骤:“如果 submitter 是 form,则将 submitterButton 设为 null。否则,将 submitterButton 设为 submitter。” - Mikko Rantalainen

11

我创建了一个测试表单,使用Firebug发现了获取值的方法:

$('form').submit(function(event){
  alert(event.originalEvent.explicitOriginalTarget.value);
}); 

不幸的是,只有Firefox支持这个事件。


5
IE浏览器没有这个属性(explicitOriginalTarget)。 - andres descalzo
1
老古董IE搞砸了,用Firebug查看了一遍,没找到其他可用的对象值,但是完全忘记了备选浏览器。听到它不支持也不惊讶,得检查一下Chrome。 - Dave Anderson
12
只有 Firefox 支持此事件。 - Andy E
基本上,HTML 中有些东西是 JavaScript 无法做到的... Mozilla 决定实现这个缺失的功能,但是没有其他人跟进吗?这就是为什么我们不能拥有好东西的原因。 - aross

6

以下是我认为更为清晰的方法。

首先,对于所有表单:

$('form').click(function(event) {
  $(this).data('clicked',$(event.target))
});

当表单触发此点击事件时,它只是记录原始目标(可在事件对象中获得),以便稍后访问。这是一个相当广泛的范围,因为它将适用于表单上的任何地方的点击。欢迎优化评论,但我怀疑它不会引起明显的问题。
然后,在$('form').submit()中,您可以查询最后一次单击了什么,例如:
if ($(this).data('clicked').is('[name=no_ajax]')) xhr.abort();

4

一个简单的方法是在每个表单按钮上使用点击事件。以下是一个带有保存、取消和删除按钮的 HTML 表单:

<form  name="formname" action="/location/form_action" method="POST">
<input name="note_id" value="some value"/>
<input class="savenote" type="submit" value="Save"/>
<input class="cancelnote" type="submit" value="Cancel"/>
<input class="deletenote" type="submit" value="Delete" />
</form> 

以下是jQuery代码。根据点击的按钮(“保存”或“删除”),我向同一服务器函数发送相应的“操作”。如果单击“取消”,则只需重新加载页面。
$('.savenote').click(function(){
   var options = {
       data: {'action':'save'}
   };
   $(this).parent().ajaxSubmit(options);
   });


$('.deletenote').click(function(){
   var options = {
       data: {'action':'delete'}
   };
   $(this).parent().ajaxSubmit(options);
   });


$('.cancelnote').click(function(){
   window.location.reload(true);
   return false;
   });

4
根据此链接,事件对象包含一个字段Event.target,它:返回表示触发事件的对象的字符串。 我刚创建了一个页面来测试该值,似乎那个表示是表单本身,而不是所点击的按钮。换句话说,JavaScript不能提供确定所点击按钮的功能。
至于Dave Anderson的解决方案,在使用之前最好在多个浏览器中进行测试。它可能能够正常工作,但我无法肯定。

1
target 返回整个表单,因此不幸的是无法帮助检测正确的提交按钮。 - Hafenkranich
我在我的回答中也这么说,@Hafenkranich。 - cmptrgeekken
Event.target 不是一个字符串。它是一个 EventTarget 对象,这个对象在这种情况下是提交的表单本身。 - akseli

4

表单的SubmitEvent有一个submitter属性。然而,截至目前,Safari浏览器上此功能无法正常工作。

<form id="form">
    <button value="add" type="submit">Add</button>
    <button value="remove" type="submit">Remove</button>
</form>

let form = document.getElementById('form');

form.onsubmit = (event) => {
    e.preventDefault();
    console.log(e.submitter.type);
}

一种可以跨浏览器使用的不同方法。然而,您必须依赖于 form 元素而不是事件对象。这基本上会在表单元素对象上添加一个“submitter”属性,以便在表单提交后可以引用它。

<form id="form">
    <button onclick="this.form.submitter = 'add'" type="submit">Add</button>
    <button onclick="this.form.submitter = 'remove'" type="submit">Remove</button>
</form>

let form = document.getElementById('form');

form.onsubmit = (event) => {
    e.preventDefault();
    console.log(form.submitter);
}

3

(事件)

function submForm(form,event){
             var submitButton;

             if(typeof event.explicitOriginalTarget != 'undefined'){  //
                 submitButton = event.explicitOriginalTarget;
             }else if(typeof document.activeElement.value != 'undefined'){  // IE
                 submitButton = document.activeElement;
             };

             alert(submitButton.name+' = '+submitButton.value)
}


<form action="" method="post" onSubmit="submForm(this, event); return false;">

3

我搜索并找到了几种使用jQuery+AJAX将提交按钮name+value发送到服务器的方法。但我并不是很喜欢它们...

其中最好的一个是hunter在这里提出的解决方案!

但我自己写了另外一个。

我想分享一下,因为它很好用,并且像我需要的那样,也适用于通过ajax加载的表单(在document.ready之后):

$(document).on('click', 'form input[type=submit]', function(){
    $('<input type="hidden" />').appendTo($(this).parents('form').first()).attr('name', $(this).attr('name')).attr('value', $(this).attr('value'));
});

很简单!当提交按钮被点击时,会向表单中添加一个隐藏字段,该字段使用与提交按钮相同的namevalue

编辑:下面的版本更易读。此外,它还负责删除先前附加的隐藏字段(在使用AJAX时可以完全可能多次提交相同的表单)。

改进后的代码:

$(document).on('click', 'form input[type=submit]', function(){
    var name   = $(this).attr('name');
    if (typeof name == 'undefined') return;
    var value  = $(this).attr('value');
    var $form  = $(this).parents('form').first();
    var $input = $('<input type="hidden" class="temp-hidden" />').attr('name', name).attr('value', value);
    $form.find('input.temp-hidden').remove();
    $form.append($input);
});

这对我有用。谢谢! - Mr. Developerdude
看起来这个依赖于所有<button/>共用相同的name属性。我会使用一个类并在你的.remove()中引用它。 - binki
@binki:不,它不会。无论如何,每个开发者都可以根据自己的需求来调整解决方案。 - J. Bruni
http://jsfiddle.net/binki/d8ecv/ 展示了我所说的 name 属性。如果表单被重复使用并包含具有不同名称的提交输入,则旧输入可能会使表单混乱不堪。 - binki
现在我明白了...我会更新我的答案。(事实上,由于你提到了<button/>元素,而代码中使用的是<input type="submit" />,所以我没有太注意你的评论...) - J. Bruni

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接