这篇 blog 是从我的 个人博客 里转过来的,考虑到只贴前面一点内容加个 link 的方式有点不厚道,而且都 markdown 格式,复制粘贴也方便,就全部弄过来了。
最近碰到一些 form 提交的一些恼人问题,虽然最后把问题解决了,但对 form 如何响应 Enter 键提交的规律还是不够清楚。专门花时间研究了一下,就有了这篇文章。先看看以下几个问题,如果你也不能确定的解答以下问题,那么这篇文章就是为你准备的。
我在浏览器上做了一系列实验去比较彼此的差异。因为条件有限(懒),仅仅在 Mac 系统的 Chrome,Safari,和 iOS 的 Mobile Safari 上测试过。我想在 Firefox 上也不会有什么差别。IE …… 我们还是先忽略它吧。想了解更多浏览器兼容性的同学,最下面有篇国外文章,作者在各大浏览器上都测试过,可以看一下。
做实验之前,我们先看看要用到的几个 html 标签和事件。
第一类是 <form>
,它可以响应 submit 事件,我们用任意方式提交 form 时都会触发,比如点击提交按钮(待会再说是哪种按钮),或者在某个输入框下按 Enter 键。
第二类是 <input type="text">
。标准的输入框,它可以响应 keydown, keypress,和 keyup 事件。我们前期不会用到这些,尽量把事情简单化。
第三类是各种按钮,我们会用到三种按钮 <input type="button">
,<input type="submit">
,和 <button>
。他们都能响应 click 事件。
大概就这些了,可以开始对比了。每次实验中我会把 html 和 js 片段(我用 CoffeeScript)写下来,因为太简单,就不特别说明怎么用了。
<form>
里仅含有一个 <input type="text">
时<form id="form">
<input type="text">
</form>
$('#form').on 'submit', ->
console.log 'submit'
false # 最后 return false 以免页面刷新
按下 Enter,console 里打印出了 submit,说明 <form>
的 submit 事件被触发了。
<form>
里含有两个 <input type="text">
时<form id="form">
<input type="text">
<input type="text">
</form>
$('#form').on 'submit', -> console.log 'submit'; false
在任何一个输入框里按下 Enter, submit 事件都没有触发。
<form>
里含有一个 <input type="text"
>,和一个 <input type="button">
时<form id="form">
<input type="text">
<input id="save" type="button" value="Save">
</form>
$('#form').on 'submit', -> console.log 'submit'; false
$('#save').on 'click', -> console.log 'save'
按下 Enter,只有 submit 事件触发;按下按钮,只有 click 事件触发。
<form>
里含有一个 <input type="text">
,和一个 <input type="submit">
时<form id="form">
<input type="text">
<input id="save" type="submit" value="Save">
</form>
$('#form').on 'submit', -> console.log 'submit'; false
$('#save').on 'click', -> console.log 'save'
按下 Enter, click 事件先触发,然后是 submit 事件;按下按钮,结果相同。
<form>
里含有一个 <input type="text">
,和一个 <button>
时<form id="form">
<input type="text">
<button id="save">Save</button>
</form>
$('#form').on 'submit', -> console.log 'submit'; false
$('#save').on 'click', -> console.log 'save'
按下 Enter, click 事件先触发,然后是 submit 事件;按下按钮,结果相同。
<form>
里含有一个 <input type="text">
,和两个 <input type="submit">
时<form id="form">
<input type="text">
<input type="submit" id="save" value="Save">
<input type="submit" id="cancel" value="Cancel">
</form>
$('#form').on 'submit', -> console.log 'submit'; false
$('#save').on 'click', -> console.log 'save'
$('#cancel').on 'click', -> console.log 'save'
按下 Enter,第一个按钮的 click 事件被触发,然后是 submit 事件. 按下任何一个按钮,该按钮的 click 事件被触发,然后是 submit 事件。
<form>
里含有一个 <input type="text">
,和两个 <button>
时结果和第六个相同。
经过以上几个实验,我们可以得出以下下结论:
按下 Enter 时,如果 <form>
要触发 submit 事件,要符合两种情况之一:只有一个输入框(可以没有提交按钮),或者至少有一个提交按钮。<input type="submit">
和 <button>
都可以看成是提交按钮。jQuery 的 submit() API 也对此有所提及。
达到第一条触发 submit 的条件后,不管是按提交按钮,还是按下 Enter ,<form>
都会执行一样的流程:先触发按钮的 click 事件(如果有按钮),然后触发 submit 事件。这算是提交的标准流程。
但如果提交按钮有多个,<form>
会把第一个出现的按钮作为默认提交按钮,按下 Enter 触发 click 事件时,也只会触发这一个按钮的事件。想触发其他按钮的 click 事件,只能去手动点击。
这里有一个坑,一般情况下我们都不会去在一个 form 里写两个 <input type="submit">
,但有可能会去写两个 <button>
。这种情况因为 <button>
就跟提交按钮无异。那怎么避免这种问题呢?我查了一下 button 的文档。原来它还有一个 type 属性,可以设置成 button, reset 和 submit 。但 submit 是默认值,所以 <input type="submit">
等价于 <button type="submit">
。如果要让它表现得像普通按钮,记得加上 type="button"
。
我们知道有些 HTML 标签会有默认行为,很明显提交按钮的默认行为就是提交表单。如果这时候在提交按钮的 click 事件中使用 preventDefault,像这样:
$('#save').on 'click', (e) -> e.preventDefault()
那么不管是 Enter 还是直接点击,都不会触发 form 的 submit 事件了。
既然按钮可以 preventDefault,那么更进一步的想,对输入框使用 preventDefault 会发生什么事情?这个解释起来比按钮复杂一点,因为输入框可以响应 keydown, keypress,和 keyup 三个事件。
我们先来看看提交 form 时,这几个事件的执行顺序:
<form>
<input type="text">
<button>Save</button>
</form>
$('form').on 'submit', -> console.log 'submit'; false
$('input').on 'keydown', -> console.log 'keydown'
$('input').on 'keypress', -> console.log 'keypress'
$('input').on 'keyup', -> console.log 'keyup'
$('button').on 'click', -> console.log 'click'
按下 Enter 或者点击按钮,事件执行顺序为:
从这里可以看到只有 keydown 和 keypress 触发在 click 之前,那么我们试试对这两个事件用一下 preventDefault。过程就不写了,结果如下:
当 keydown 加上 preventDefault 时, keypress, click, submit 都不会触发。 当 keypress 加上 preventDefault 时, click 和 submit 都不会触发。
因此可知, keypress 使用 preventDefault 的效果是取消了 click,进而引发了 submit 也不会触发。而 keydown 则是因为取消了 keypress 导致没有触发 form 提交。
MDN HTML button tag How Forms Submit When Pressing Enter jQuery API .submit()