打印

BX9027: IE 浏览器中 MouseDown 事件中取消浏览器默认行为后 Focus 事件依然被触发

作者:钱宝坤

标准参考

事件处理函数中的 return false 方法最初由 Netscape 制定,当时还没有事件监听注册方法,浏览器只支持为一个标记绑定一个事件处理函数,由此 Netscape 规定如果事件处理函数返回 false ,将就要阻止浏览器的默认事件行为继续执行,这个事实规范在所有浏览器中都被支持,它用于单一事件绑定函数中。

在现行 DOM-Events 规范中已定义了 preventDefault 标准方法来阻止浏览器默认行为,该方法用来在事件监听处理程序中阻止浏览器默认事件触发。而 return false 语句只针对事件绑定函授处理方式生效,在事件监听处理程序中将不会阻止浏览器默认事件触发。

关于浏览器默认行为我们通常可以理解为某此用户操作后浏览器可以自动执行的行为动作,这个行为可能会触发某些固有事件,从而使得事件处理程序可以被执行。比较常见的为:用户点击超链接后浏览器会自动下载目标页面,或者在可输入区域点击鼠标后浏览器自动将输入光标调整到可输入区域内。

关于取消事件的详细描述请参考 DOM-Level-2-Events 规范 1.2.4. Event cancelation

关于 preventDefault 方法的描述请参考 DOM-Level-2-Events 规范 1.4. Event interface

Click 事件发生时,会触发一系列相关的事件消息,他们的执行顺序为 MouseDown、MouseUp、Click,相关说明请参照 DOM-Level-2-Events 规范 1.6.2. Mouse event types 中的 Click 部分。

问题描述

当用户通过鼠标操作触发 click 事件时,基本的事件触发流程为:MouseDown 事件 --> MouseUp 事件 --> Click 事件。若被点击的元素可以获得焦点,并且当前还没有获得焦点时,会在 MouseDown 事件之后默认触发 Focus 事件,再依次触发其后的 MouseUp 和 Click 事件。

如果浏览器将 Focus 作为默认行为处理,那么在MouseDown 事件处理函数内取消浏览器默认行为后,被点击的可以获得焦点的元素,需要阻止其后的 Focus 默认事件行为被触发,并且该元素不能获得焦点。

造成的影响

在 IE 浏览器中,在 MouseDown 事件的处理函数中,取消浏览器默认行为执行后,如果点击的是可获得焦点的元素 (如 INPUT) ,那么他依然会获得焦点并触发 Focus 事件。

而在非 IE 浏览器中,此情况下元素无法获得焦点,也不会触发 Focus 事件。

这个差异可能会导致某些具体操作性质的应用功能在非 IE 浏览器中执行异常。

受影响的浏览器

IE

问题分析

在具体分析此问题之前,需要理解与这个问题有关的两个知识点:

  1. 各浏览器对默认可以通过 Click 事件获得焦点的元素之间的差异:
    SD9027: 各浏览中某些标记通过鼠标触发 click 事件后不会获得焦点并触发 focus 事件
  2. 两种取消浏览器默认行为执行方法的差异和兼容性问题:
    BX9026: 在 IE 的事件监听处理函数中使用 return false 语句依然可以阻止浏览器默认行为执行

现在进入正题,分析以下代码:

<script>
  window.onload = function (){
    function stopDefault(e){
      e = e || window.event;
      (e.preventDefault)
      ?e.preventDefault()
      :e.returnValue = false;
    }
    function addEvent(el, type, fn){
      (el.addEventListener)
      ? el.addEventListener(type, fn, false)
      :(function () {
        el.attachEvent("on" + type, function(e){
          fn.call(el,e);
         });
      })();
    }
    function setEvents(elment){
      addEvent(elment,"mousedown",function (e){
        this.value+=' mousedown 事件被触发';
        stopDefault(e);
      });
      addEvent(elment,"focus",function (e){
        this.value+=' focus 事件被触发';
      });
      addEvent(elment,"mouseup",function (e){
        this.value+=' mouseup 事件被触发';
      });
      addEvent(elment,"click",function (e){
        this.value+=' click 事件被触发';
      });
    }
    setEvents(document.getElementsByTagName("input")[2]);
  }
</script>
<ol>
<li>
  <h2> 鼠标默认行为触发顺序  </h2>
  <input style="width:100%;" onmousedown="this.value+=' mousedown 事件被触发 ';" onfocus="this.value+=' focus 事件被触发 '" onclick="this.value+=' click 事件被触发 '" onmouseup="this.value+=' mouseUp 事件被触发 '">
  <p>点击文本域后显示 click 事件相关联的各个事件触发顺序。</p>
</li>
<li>
  <h2> 使用 return false 语句阻止 MouseDown 后续默认行为触发  </h2>
  <input style="width:100%;" onmousedown="this.value+=' mousedown 事件被触发 ';return false;" onfocus="this.value+=' focus 事件被触发 '" onclick="this.value+=' click 事件被触发 '" onmouseup="this.value+=' mouseUp 事件被触发 '">
  <p>点击文本域后没有继续执行 focuse 事件内程序,则说明成功阻止了默认行为。</p>
</li>
<li>
  <h2>使用 preventDefault 方法阻止 MouseDown 后续默认行为触发 </h2>
  <input style="width:100%;">
  <p>点击文本域后没有继续执行 focuse 事件内程序,则说明成功阻止了默认行为。</p>
</li>
</ol>

上例中,首先验证鼠标点击 INPUT 元素后默认触发的事件执行顺序:

事件触发顺序 1 2 3 4
所有浏览器 MouseDown 事件 Focus 事件 MouseUp 事件 Click 事件

在所有浏览器中均为 MouseDown --> Focus --> MouseUp --> Click ,这与规范描述一致。

确定了 MouseDown 事件之后将要触发的事件消息后,在 INPUT 元素 (该元素在所有浏览器中均可获得焦点) 的 MouseDown 事件内,分别使用单一事件绑定方法和事件监听方法取消浏览器默认行为。

各浏览器中执行效果如下:

  IE Firefox Safari Chrome Opera
MouseDown 触发 触发
Focus 触发 未触发
MouseUp 触发 触发
Click 触发 触发

实验结果表明:

  • 在 Firefox Safari Chrome Opera 中 MouseDown 后 Focus 事件没有被触发,其后的 MouseUp Click 事件函数均被执行,说明 Focus 行为没有被浏览器自动执行。由此可以断定 Focus 行为是这些浏览器的默认行为之一,可以被取消执行。
  • 但是,在 IE 浏览器中,无法阻止 MouseDown 后的默认 Focus 事件行为,元素依然获得了焦点,并触发了 Focus 事件处理函数。这个差异明显说明 IE 并不将 Focus 作为浏览器行为处理,因此不能阻止其事件触发 。

解决方案

如果被点击元素可以获得焦点,并且需要在 Focus 事件处理函数内执行具体业务逻辑,那么:

  • 请在元素的 MouseDown 单一事件绑定处理函数或内联事件处理函数中,避免使用 return false 语句阻止 Focus 默认行为;
  • 请在元素的 MouseDown 事件监听处理函数中,避免使用 preventDefault 方法 阻止 Focus 默认行为。

参见

知识库

相关问题

测试环境

操作系统版本: Windows 7 Ultimate build 7600
浏览器版本: IE6
IE7
IE8
Firefox 3.6.9
Chrome 7.0.503.0 dev
Safari 5.0.1
Opera 10.61
测试页面: mousedown_focus.html
本文更新时间: 2010-09-09

关键字

preventDefault returnValue Browser Befault Behavior mousedown mouseup click focus event