JavaScript Press Enter to Submit 背后的那些事

darkbaby123 · 2014年02月10日 · 最后由 Tony612 回复于 2014年02月11日 · 5572 次阅读

前言

这篇 blog 是从我的 个人博客 里转过来的,考虑到只贴前面一点内容加个 link 的方式有点不厚道,而且都 markdown 格式,复制粘贴也方便,就全部弄过来了。

最近碰到一些 form 提交的一些恼人问题,虽然最后把问题解决了,但对 form 如何响应 Enter 键提交的规律还是不够清楚。专门花时间研究了一下,就有了这篇文章。先看看以下几个问题,如果你也不能确定的解答以下问题,那么这篇文章就是为你准备的。

  1. 按下 Enter 后,form 的 submit 事件一定会触发吗?
  2. 按下 Enter 后,提交按钮的事件会触发吗?如果会,哪种提交按钮的事件才会触发?
  3. 输入框可以阻止 Enter 触发吗?如果能,该怎么做到?

前置准备

我在浏览器上做了一系列实验去比较彼此的差异。因为条件有限(懒),仅仅在 Mac 系统的 Chrome,Safari,和 iOS 的 Mobile Safari 上测试过。我想在 Firefox 上也不会有什么差别。IE …… 我们还是先忽略它吧。想了解更多浏览器兼容性的同学,最下面有篇国外文章,作者在各大浏览器上都测试过,可以看一下。

做实验之前,我们先看看要用到的几个 html 标签和事件。

第一类是 <form>,它可以响应 submit 事件,我们用任意方式提交 form 时都会触发,比如点击提交按钮(待会再说是哪种按钮),或者在某个输入框下按 Enter 键。

第二类是 <input type="text">。标准的输入框,它可以响应 keydownkeypress,和 keyup 事件。我们前期不会用到这些,尽量把事情简单化。

第三类是各种按钮,我们会用到三种按钮 <input type="button"><input type="submit">,和 <button>。他们都能响应 click 事件。

大概就这些了,可以开始对比了。每次实验中我会把 html 和 js 片段(我用 CoffeeScript)写下来,因为太简单,就不特别说明怎么用了。

实验开始

1. 当 <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 事件被触发了。

2. 当 <form> 里含有两个 <input type="text">

<form id="form">
  <input type="text">
  <input type="text">
</form>
$('#form').on 'submit', -> console.log 'submit'; false

在任何一个输入框里按下 Entersubmit 事件都没有触发。

3. 当 <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 事件触发。

4. 当 <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'

按下 Enterclick 事件先触发,然后是 submit 事件;按下按钮,结果相同。

5. 当 <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'

按下 Enterclick 事件先触发,然后是 submit 事件;按下按钮,结果相同。

6. 当 <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 事件。

7. 当 <form> 里含有一个 <input type="text">,和两个 <button>

结果和第六个相同。

关于 form 的小结

经过以上几个实验,我们可以得出以下下结论:

  1. 按下 Enter 时,如果 <form> 要触发 submit 事件,要符合两种情况之一:只有一个输入框(可以没有提交按钮),或者至少有一个提交按钮。<input type="submit"><button> 都可以看成是提交按钮。jQuery 的 submit() API 也对此有所提及。

  2. 达到第一条触发 submit 的条件后,不管是按提交按钮,还是按下 Enter<form> 都会执行一样的流程:先触发按钮的 click 事件(如果有按钮),然后触发 submit 事件。这算是提交的标准流程。

  3. 但如果提交按钮有多个,<form> 会把第一个出现的按钮作为默认提交按钮,按下 Enter 触发 click 事件时,也只会触发这一个按钮的事件。想触发其他按钮的 click 事件,只能去手动点击。

这里有一个坑,一般情况下我们都不会去在一个 form 里写两个 <input type="submit">,但有可能会去写两个 <button> 。这种情况因为 <button> 就跟提交按钮无异。那怎么避免这种问题呢?我查了一下 button 的文档。原来它还有一个 type 属性,可以设置成 buttonresetsubmit 。但 submit 是默认值,所以 <input type="submit"> 等价于 <button type="submit">。如果要让它表现得像普通按钮,记得加上 type="button"

如果为提交按钮加入 preventDefault 呢?

我们知道有些 HTML 标签会有默认行为,很明显提交按钮的默认行为就是提交表单。如果这时候在提交按钮的 click 事件中使用 preventDefault,像这样:

$('#save').on 'click', (e) -> e.preventDefault()

那么不管是 Enter 还是直接点击,都不会触发 form 的 submit 事件了。

那如果为输入框加入 preventDefault 呢?

既然按钮可以 preventDefault,那么更进一步的想,对输入框使用 preventDefault 会发生什么事情?这个解释起来比按钮复杂一点,因为输入框可以响应 keydownkeypress,和 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 或者点击按钮,事件执行顺序为:

  1. keydown
  2. keypress
  3. click
  4. submit
  5. keyup

从这里可以看到只有 keydownkeypress 触发在 click 之前,那么我们试试对这两个事件用一下 preventDefault。过程就不写了,结果如下:

keydown 加上 preventDefault 时, keypressclicksubmit 都不会触发。 当 keypress 加上 preventDefault 时, clicksubmit 都不会触发。

因此可知, keypress 使用 preventDefault 的效果是取消了 click,进而引发了 submit 也不会触发。而 keydown 则是因为取消了 keypress 导致没有触发 form 提交。

参考资料

MDN HTML button tag How Forms Submit When Pressing Enter jQuery API .submit()

赞~ 只是我看的有点疑惑,为什么后来 submit 都用 click 事件来监听了?

$('#form').on 'click', -> console.log 'submit'; false // 这里为啥不是 `on 'submit'`?
$('#save').on 'click', -> console.log 'save'

@ruohanc 笔误……还好你注意到了,谢谢指正!

button 的 type="submit" 是默认值好像有兼容性问题,时间有点久了,我去查查看

时间太久了,实在找不到了,只记得当时某些浏览器并不会把省略 type 属性的 button 认为是 submit 按钮,所以一直以来写 HTML 我都不会省略掉 type 属性

具体是哪个浏览器实在找不到了,不过从 jQuery 的 API 文档里面可以看到一些历史的痕迹 http://api.jquery.com/submit-selector/

Note that some browsers treat element as type="submit" implicitly while others (such as Internet Explorer) do not. To ensure that markup works consistently across all browsers and guarantee that it is possible to consistently select buttons that will submit a form, always specify a type property.

@edokeh 好像是的。关于老浏览器,文章最下面有篇链接 How Forms Submit When Pressing Enter,里面作者做了一个 Test page 。里面例子 3 就是你说的情况。

IE 6 & 7 会在点击时只触发 click 而不是 submit。但是按 Enter 时只触发 submit ……

赞一下,楼主研究的还挺仔细的。

需要 登录 后方可回复, 如果你还没有账号请 注册新账号