常年做前端,哪能不恶心?虽然这两年浏览器兼容性是越来越好了,但还是会偶尔遇到一些其他问题,让你欲哭无泪,求死不能。今天我们就来八一八浏览器中跟聚焦(focus)有关的事件。
这个世界上有两种人:使鼠标的和使键盘的。鼠标是二维定位,精确度高;键盘是十个输入(像我爸爸那样的修炼二指禅的除外),速度非常。正因为各有利弊,所以虽然双方都看对方不太顺眼,也只能凑合着过。
焦点这一设计是为键盘使用者准备的:当你双手飞速敲打键盘的时候,有一个问题也就来了:电脑怎么知道你在往哪儿输入呢?于是,在网页中,我们定义了一个激活元素(active element),键盘输入都是发往这个元素的。当用户用鼠标点击另一个可聚焦的元素或者按Tab键时,网页激活的元素也会响应的发生变化。

在浏览器中,跟焦点有关的API有以下这些(见 HTML5 Editor’s draft):
1个属性——document.activeElement,用于获取文档中当前获得焦点的元素
1个方法——element.focus(),用于让元素获得焦点
若干 事件——blur, focus, focusin, focusout

现在让我们看看各浏览器的表现吧。为了易于对比,笔者使用了Chrome 50, Firefox 46, IE 11和Edge 20作为测试对象。Safari因为跟Chrome采用同样的引擎,因此结果与Chrome 50完全一致,这里不单独列出。测试代码见 这里

测试1 鼠标点击改变焦点

初始时焦点位于输入框1上,通过点击输入框2将焦点移到它上面。

各浏览器中事件触发先后顺序如下:

浏览器 事件链
Chrome 50 输入框2上mousedown -> 输入框1上blur -> 输入框1上focusout -> 输入框2上focus -> 输入框2上focusin -> 输入框2上mouseup -> 输入框2上click
Firefox 46 输入框2上mousedown -> 输入框1上blur -> 输入框2上focus -> 输入框2上mouseup -> 输入框2上click
IE 11 输入框2上mousedown -> 输入框1上focusout -> 输入框2上focusin -> 输入框1上blur -> 输入框2上focus -> 输入框2上mouseup -> 输入框2上click
Edge 20 输入框2上mousedown -> 输入框1上focusout -> 输入框1上blur -> 输入框2上focus -> 输入框2上focusin -> 输入框2上mouseup -> 输入框2上click

所有浏览器的焦点转移行为都发生在鼠标压下的阶段(mousedown),鼠标抬起(mouseup)对焦点没有任何影响。
可以看出主要分歧在于focusout和focusin的触发时间,Chrome认为两者只是相应事件(blur和focus)的冒泡版本,因此在相应事件后触发;而IE认为两者其实是预览事件(preview),因此在相应事件前触发;Edge是一个折中;而Firefox干脆就没有这两个事件(莫非因为看不清楚趋势,先明哲保身一下?)。
值得一提的是,还未定稿的 界面事件规范中的定义,似乎与IE的行为更为一致。

那么,在这么多事件当中(mousedown / mouseup / click,focus / blur / focusin / focusout),焦点到底是什么时候转移的呢?
这个问题上,浏览器自动分为了两个阵营:IE(包括Edge)一方当focusout触发的时候,焦点就已经转移到了新的输入框上;非IE(Firefox / Chrome)一方在失去焦点阶段内,焦点先回到默认值body上,而在获得焦点阶段,焦点才会转移到新的输入框上。

与所有其他浏览器都不同,IE 11默认情况下所有display为block或者table的元素都可以获得焦点。但Edge却与Chrome等非IE浏览器一致,典型的兄弟反水啊。

测试2 Tab键改变焦点

初始时焦点位于输入框1上,通过按下键盘上Tab键将焦点移到下一个输入框(输入框2)上

各大浏览器的事件触发与鼠标改变焦点基本一致,只不过触发的是keydown和keyup,而不是mousedown或mouseup,也没有click事件。不过这中间还是有两点值得注意的。

  1. Firefox 46会在紧随着keydown之后,再触发一次keypress;而其他浏览器不会。
  2. 如果是按住Tab键进行连续的焦点改变时,会在每个聚焦元素上分别触发keydown(Firefox会额外触发keypress),但是除最后一次外不会触发keyup事件。

笔者试图寻找规范中的定义,可惜没有找到。其实这也正常,定义规范本就是一个特别繁杂的工程,难免挂一漏万。更何况规范定义好了,各家浏览器厂商是否执行还是个未知数。HTML5搞出来两套不就是因为大家没法一起玩的结果么?论天下大事,分久必合,合久必分,习惯就好。