HTML表单中默认的提交按钮是如何确定的?

246

如果表单提交不是通过特定的按钮提交的,例如

  • 通过按下Enter
  • 在JS中使用HTMLFormElement.submit()

那么浏览器应该如何确定哪个(如果有)提交按钮要用作按下的按钮?这在两个方面都很重要:

  • 调用附加到提交按钮的onclick事件处理程序
  • 发送回Web服务器的数据

到目前为止,我的实验表明:

  • 按下Enter键时,Firefox、Opera和Safari使用表单中的第一个提交按钮
  • 按下Enter键时,IE根据我无法解决的条件使用第一个提交按钮或根本不使用任何按钮
  • 所有这些浏览器都不使用JS提交

标准怎么说?

如果需要,这是我的测试代码(PHP仅与我的测试方法相关,与我的问题本身无关)

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
                      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Test</title>
</head>

<body>

<h1>Get</h1>
<dl>
<?php foreach ($_GET as $k => $v) echo "<dt>$k</dt><dd>$v</dd>"; ?>
</dl>

<h1>Post</h1>
<dl>
<?php foreach ($_POST as $k => $v) echo "<dt>$k</dt><dd>$v</dd>"; ?>
</dl>

<form name="theForm" method="<?php echo isset($_GET['method']) ? $_GET['method'] : 'get'; ?>" action="<?php echo $_SERVER['SCRIPT_NAME']; ?>">
    <input type="text" name="method" />
    <input type="submit" name="action" value="Button 1" onclick="alert('Button 1'); return true" />
    <input type="text" name="stuff" />
    <input type="submit" name="action" value="Button 2" onclick="alert('Button 2'); return true" />
    <input type="button" value="submit" onclick="document.theForm.submit();" />
</form>

</body></html>
16个回答

127
如果您通过JavaScript提交表单(即formElement.submit()或任何等效方法),则所有提交按钮都不被视为成功,并且它们的值都不包括在提交的数据中。(请注意,如果您通过submitElement.click()使用提交,则被引用的提交被视为活动状态;这并不属于您的问题范围,因为此处提交按钮是明确的,但我想为人们包括它,以便了解如何通过JavaScript表单提交使提交按钮成功。当然,表单的onsubmit处理程序仍将以这种方式触发,而如果使用form.submit()则不会,所以还有另一个问题...)
如果在非文本区域字段中按Enter键提交表单,则实际上取决于用户代理决定其想要什么。 规格说明 并没有说在文本输入字段中使用Enter键提交表单(如果您切换到按钮并使用空格或其他方式激活它,则没有问题,因为该特定提交按钮是明确使用的)。它只是说必须在提交按钮被激活时提交表单。甚至不要求在文本输入框中按Enter键将提交表单。
我相信Internet Explorer选择出现在源代码中的第一个提交按钮;我认为Firefox和Opera选择具有最低tabindex的按钮,如果没有其他定义,则回退到第一个定义。还有一些关于提交是否具有非默认值属性的复杂性(如我所知)。

重点是这里没有定义的标准,完全取决于浏览器 - 因此在任何情况下,请尽量避免依赖特定的行为。如果你真的想知道,你可能可以找到各种浏览器版本的行为,但是我之前调查时发现了一些相当复杂的条件(当然,这些条件可能会随着新的浏览器版本而改变),我建议您尽可能避免使用它!


1
我的基本问题是是否在任何规范中有关于“如果浏览器提供了提交表单的方式而不需要指定提交控件...”的内容。但从戴维的回答中我推断出并没有这样的规定。我提到IE的问题是因为有时候按下Enter键会提交表单,而不会设置任何提交按钮。一个结果是,如果你有多个表单提交到同一个脚本,你不能依赖提交按钮来区分它们。这在我正在进行的项目中遇到过。 - Stewart
1
非常有用的第一段括号部分 - 谢谢。 - rbassett

80
HTML 4没有明确规定。HTML 5规定第一个提交按钮必须是默认的

4.10.21.2 隐式提交

form元素的默认按钮是该form元素中form owner为该form元素的tree order中的第一个submit button

如果用户代理支持让用户隐式提交表单(例如,在某些平台上,当文本控件处于focused状态时按“enter”键会隐式提交表单),则对于具有activation behavior且未disableddefault button的表单进行隐式提交必须使用户代理在该default buttonfire a click event

如果你想在保持视觉顺序的同时影响哪一个是“第一”,那么有几种方法可以采取。
一个屏幕外的、无法聚焦的按钮。

.hidden-default {
    position: absolute;
    left: -9999px;
}
<form>
  <input name="text">
  <button name="submit" value="wanted" class="hidden-default" tabindex="-1"></button>
  <button name="submit" value="not wanted">Not the default</button>
  <button name="submit" value="wanted">Looks like the default</button>
</form>

Flexbox顺序:

.ordering {
    display: inline-flex;
}

.ordering button {
    order: 2;
}

.ordering button + button {
    order: 1;
}
<form>
  <input name="text">
  <div class="ordering">
    <button name="submit" value="wanted">First in document order</button>
    <button name="submit" value="not wanted">Second in document order</button>
  </div>
</form>


1
@user5480949 — 确实如此,但与问题无关。 - Quentin
@user5480949 — type 属性的默认值是 submit。这个答案是关于哪个提交按钮是默认的,而不是关于你可以创建提交按钮的不同方式。 - Quentin
@user5480949 — 正如我所说,这个答案并不是在教你什么是提交按钮。 - Quentin
我对规范中的“提交按钮”感到困惑。是指 <input type=submit /> 吗?还是包括 <button type=submit /> - Jack Lu

67

Andrezj的回答 已经基本解决了问题... 但是这里有一个简单的跨浏览器解决方案。

使用如下表单:

<form>
    <input/>
    <button type="submit" value="some non-default action"/>
    <button type="submit" value="another non-default action"/>
    <button type="submit" value="yet another non-default action"/>

    <button type="submit" value="default action"/>
</form>

并重构为:

<form>
    <input/>

    <button style="overflow: visible !important; height: 0 !important; width: 0 !important; margin: 0 !important; border: 0 !important; padding: 0 !important; display: block !important;" type="submit" value="default action"/>

    <button type="submit" value="some non-default action"/>
    <button type="submit" value="another non-default action"/>
    <button type="submit" value="yet another non-default action"/>
    <button type="submit" value="still another non-default action"/>

    <button type="submit" value="default action"/>
</form>

由于W3C规范表示多个提交按钮有效,但忽略了用户代理应如何处理它们的指导,因此浏览器制造商将按照他们认为合适的方式进行实现。我发现他们要么提交表单中的第一个提交按钮,要么提交当前具有焦点的表单字段之后的下一个提交按钮。
不幸的是,简单地添加 display:none; 样式不起作用,因为 W3C 规范表明任何隐藏的元素都应该排除在用户交互之外。因此,不妨将其藏在明处!
以上是我最终投入生产的解决方案示例。按下回车键会触发默认的表单提交行为,即使其他非默认值存在且在 DOM 中位于默认提交按钮之前。对于鼠标/键盘交互和明确的用户输入,而避免使用 JavaScript 处理程序,也可获得奖励。
注意:通过表单进行制表将不会显示任何视觉元素的焦点,但仍会导致选择不可见按钮。为避免此问题,只需相应地设置tabindex属性并省略不可见提交按钮上的tabindex属性即可。虽然将这些样式提升为!important可能看起来有些奇怪,但它们应该可以防止任何框架或现有按钮样式干扰此修复程序。此外,这些内联样式绝对是不良形式,但我们在这里证明概念……而不是编写生产代码。

5
谢谢。我也遇到了“display: none”这个问题。除了最小化按钮,你还可以通过用<div style="position: fixed; left: -1000px; top: -1000px />把它移到页面外部来隐藏。但需要注意一点:仅使用CSS最小化或移动按钮可能会引起WAI问题,因为屏幕阅读器仍然会看到默认按钮。 - Stefan Haberl
1
这对我有用,谢谢。我不得不添加按钮的 border:none; 以使其完全消失。 - Macumbaomuerte
4
你可以将隐藏按钮的tabindex属性设置为-1。 - tvanc
3
那上面的<input/>是用来做什么的? - Sz.
1
@lunakid <input /> 是用来展示输入级表单元素(文本框、单选框、复选框、输入框等)与提交按钮之间的位置关系。 - James
显示剩余2条评论

26

现在可以使用Flexbox来解决这个问题:

HTML

<form>
    <h1>My Form</h1>
    <label for="text">Input:</label>
    <input type="text" name="text" id="text"/>

    <!-- Put the elements in reverse order -->
    <div class="form-actions">
        <button>Ok</button> <!-- Our default action is first -->
        <button>Cancel</button>
    </div>
</form>

CSS

.form-actions {
    display: flex;
    flex-direction: row-reverse; /* Reverse the elements inside */
}

解释

使用Flexbox,我们可以通过使用CSS规则flex-direction: row-reverse来反转使用display:flex的容器中元素的顺序,这不需要任何CSS或隐藏元素。对于不支持Flexbox的旧浏览器,它们仍然能够得到一个可行的解决方案,但元素将不会被反转。

演示

http://codepen.io/Godwin/pen/rLVKjm


Bootstrap还有一个方便的类:flex-row-reverse - undefined

17

如果有人想使用jQuery完成这个任务,我认为这篇帖子会很有帮助:

http://greatwebguy.com/programming/dom/default-html-button-submit-on-enter-with-jquery/

基本解决方案如下:

$(function() {
    $("form input").keypress(function (e) {
    if ((e.which && e.which == 13) || (e.keyCode && e.keyCode == 13)) {
        $('input[type=submit].default').click();
        return false;
    }
    else {
        return true;
    }
    });
});

还有一个我喜欢的是:

jQuery(document).ready(function() {
    $("form input, form select").live('keypress', function (e) {
        if ($(this).parents('form').find('button[type=submit].default, input[type=submit].default').length <= 0)
            return true;

        if ((e.which && e.which == 13) || (e.keyCode && e.keyCode == 13)) {
            $(this).parents('form').find('button[type=submit].default, input[type=submit].default').click();
            return false;
        }
        else {
            return true;
        }
    });
});

jQuery.Event 总是具有其 .which 属性。此外,无需回退到 DOM 的 .keyCode(或 .charCode)属性。 - Arjan
关于jQuery的另一个问题是不必要的双重查询。由于没有必要返回true,此外,在Arjan建议的集成减少方面,jQuery版本变得非常清晰和简短。 - soletan
我使用了keydown而不是keypress(避免等待keyup),并在此函数的开头添加了e.preventDefault();以避免执行“真正”的默认按钮。效果非常好。 - Matt Roy
链接已损坏(404)。 - Peter Mortensen

7

我曾有一个表单上有11个提交按钮,当用户按 Enter 键时,它总是使用第一个提交按钮。我在其他地方读到说,在一个表单上有超过一个提交按钮不是一个好主意(不良实践),最好的方法是将你想要的按钮作为默认按钮,作为表单上唯一的提交按钮。其他按钮应改为 "TYPE = BUTTON",并添加一个onClick事件来调用JavaScript中的自定义提交程序。类似这样:

<SCRIPT Language="JavaScript">
function validform()
{
  // Do whatever you need to validate the form, and return true or false accordingly
}

function mjsubmit()
{
  if (validform()) { document.form1.submit(); return true;}
  return false;
}
</SCRIPT>
<INPUT TYPE=BUTTON NAME="button1" VALUE="button1" onClick="document.form1.submitvalue='button1'; return mjsubmit();">
<INPUT TYPE=BUTTON NAME="button2" VALUE="button2" onClick="document.form1.submitvalue='button2'; return mjsubmit();">
<INPUT TYPE=SUBMIT NAME="button3" VALUE="button3" onClick="document.form1.submitvalue='button3'; return validform();">
<INPUT TYPE=BUTTON NAME="button4" VALUE="button4" onClick="document.form1.submitvalue='button4'; return mjsubmit();">

这里,button3 是默认选项,尽管你正在使用其他按钮以编程方式提交表单,但是 mjsubmit 程序会对它们进行验证。


23
不好的想法。如果禁用JS,您的表单将无法正常工作。应该以不引人注目的方式使用JS。 - BalusC
7
几乎没有浏览器默认关闭JS,即使是iPod上的浏览器也不例外!就我引用的那个表单而言,如果禁用JS,它就无法使用了!很多功能都依赖于JS的运行。 - graphic
9
默认关闭或不关闭,那些禁用JS的人不应该为了解决这样一个愚蠢的小问题而被迫打开它。 - Stewart
2
什么样的表单有11个提交按钮?(好奇心作祟 - Ben
3
我认为验证不显眼的Javascript最大的原因是,显眼的Javascript往往很棘手。我不确定如何描述我的想法,但当我看到页面绝对依赖于JS时,我也打赌我可以在DOM上做一些棘手的事情,并在服务器上执行一些意外的操作。 - Samantha Branham
显示剩余4条评论

3
<form onsubmit="alert('submit');return false;">
    <input name="username">
    <input name="password" type="password">
    <button onclick="if(document.activeElement === this){alert('button 1');}else{default_submit.click();}return false;">button 1</button>
    <button onclick="if(document.activeElement === this){alert('button 2');}else{default_submit.click();}return false;">button 2</button>
    <input id="default_submit" type="submit">
</form>

如果您从文本输入框按下“Enter”键,则按钮将不会获得焦点。然后我们忽略此点击并单击默认提交,但是如果您使用鼠标单击按钮,则它将获得焦点,然后我们会应用此次点击。
演示:https://codepen.io/remon-reffat/pen/mdRaXbp

2
我一直在思考同样的问题,因为我的表单中间有一个提交按钮,它会将提交重定向到另一个页面,就像这样:
<button type="submit" onclick="this.form.action = '#another_page'">More</button>

当用户按下“Enter”键时,这个按钮被点击了,而不是另一个提交按钮。因此,我进行了一些基本的测试,创建了一个具有多个提交按钮和不同可见性选项的表单,并在onclick事件中发出警报,指示点击了哪个按钮:https://jsfiddle.net/aqfy51om/1/。我用于测试的浏览器和操作系统如下:

Windows

OS X

  • Google Chrome 43
  • Safari 7.1.6
这些浏览器中大多数都点击了第一个按钮,尽管应用了可见性选项,但是除了Internet Explorer和Safari,它们点击了第三个按钮,即在“隐藏”容器内“可见”的按钮:
<div style="width: 0; height: 0; overflow: hidden;">
    <button type="submit" class="btn btn-default" onclick="alert('Hidden submit button #3 was clicked');">Hidden submit button #3</button>
</div>

我的建议是,如果您的表单有多个具有不同含义的提交按钮,请在表单开头包含一个默认操作的提交按钮,可以是以下两种情况之一:
1. 完全可见 2. 包含style="width: 0; height: 0; overflow: hidden;"样式的容器
另一个选项可能是将按钮偏移(仍在表单开头),例如style="position: absolute; left: -9999px; top: -9999px;",我在Internet Explorer中尝试过,可以使用,但我不知道它还会影响什么,例如打印。

1

很奇怪,第一个按钮Enter总是转到第一个按钮,不管它是否可见,例如使用jQuery show/hide(). 添加属性.attr('disabled', 'disabled')可以完全阻止接收Enter提交按钮。这是一个问题,例如在记录对话框中调整插入/编辑+删除按钮的可见性时。我发现更少的hackish和简单的方法是将编辑放在插入之前。

<button type="submit" name="action" value="update">Update</button>
<button type="submit" name="action" value="insert">Insert</button>
<button type="submit" name="action" value="delete">Delete</button>

使用JavaScript代码:
$("#formId button[type='submit'][name='action'][value!='insert']").hide().attr('disabled', 'disabled');
$("#formId button[type='submit'][name='action'][value='insert']").show().removeAttr('disabled');

1
当您在单个表单中有多个提交按钮并且用户按Enter键从文本字段提交表单时,此代码将通过调用表单上的提交事件来覆盖默认功能。以下是该代码:
$('form input').keypress(function(e){

    if ((e.which && e.which == 13) || (e.keyCode && e.keyCode == 13)){
        $(e.target).closest('form').submit(); return false;
    }
    else
        return true;
});

简单且修复了问题 - toddmo

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