IE sucks, mousedown事件与focus行为分析

首先这个问题hax也有[url=http://www.iteye.com/topic/191555]相关分析[/url],受益良多。

[b]问题描述[/b](如果你只想看结论,可以跳过此部分)
近些时候发现一个bug:在IE下,当ExtJs中的菜单处于显示状态时,点击TextField或TextArea,发现它的emptyText未移除。(emptyText应该在只有为空,并未激活时显示)
然后对此bug进行了调试追踪,发现TextField并未收到focus事件。
继续制作简单模拟场景以定位排除,最终发现:
当在mousedown事件中改变页面焦点目标时,就会发生问题:界面光标已经在text input中了,但并未发出focus事件;当前的document.activeElement不为text input,而是JS中修改的值。
Ext中Menu会自动将焦点设置为自己(实际上是它下面的一个a元素),然后点击页面其它位置时,它会隐藏。
而[u]隐藏的元素是无法获得焦点的[/u],从而导致焦点变动。

这是简单的bug重现案例:

<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>测试 - oldFocusTarget判断</title>
</head>

<body>
<input type="text" id="test" value="init value" /> <a id="sucks" href="#">sucks</a>
<script type="text/javascript">
var text = document.getElementById("test"),
a = document.getElementById("sucks");
// 监听text focus状态
var handleFocus = function(){
document.title = "focus! "+new Date();
text.value = "";
};
var handleBlur = function(){
document.title = "blur! "+new Date();
text.value = "empty text";
};
text.attachEvent("onfocus", handleFocus);
text.attachEvent("onblur", handleBlur);

// text点击时更改focus对象
text.attachEvent("onmousedown", function(){
a.focus();
});
// 最终,text有输入框,但焦点在a上,并且text未触发focus
</script>
</body>
</html>


[b]简单理解[/b]
开始一直不能理解,看完hax的分析后才恍然大悟,其实就是焦点状态不同步。

当点击鼠标时(mousedown),IE的UI控件(即hax所说的windows基础控件所模拟的)会获得焦点,但在Dom层(即hax所说的html层)如果有在mousedown事件中修改焦点目标,会使得它与UI控件层失去同步。
这时,UI控件层的焦点是点击的控件,而Dom层则是JS操作聚焦后的元素。
就出现了文本框内有输入符,但Dom层焦点又不在它身上的bug。
如果不在mousedown中处理焦点,是没有任何问题的,为什么修改后就会失去同步呢?

[b]探索深层原因[/b]
这里尝试分析IE对UI控件层与Dom层焦点同步的逻辑,目前有了一个猜测:
1. 当鼠标点击时,先记录Dom层焦点(document.activeElement)为oldFocusTarget
2. UI控件层内部处理点击行为 (注:2、3不区分先后次序)
3. Dom层发出mousedown事件,并执行相关事件句柄
4. 如果现在的Dom层焦点与oldFocusTarget一致,则修改Dom层焦点为当前UI控件。

这是一个比较给力的证明(IE6下测试通过):

<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>测试 - oldFocusTarget判断</title>
</head>

<body>
<input type="text" id="test" value="init value" /> <a id="sucks" href="#">sucks</a> <a id="f_uck" href="#">f_uck</a>
<script type="text/javascript">
var text = document.getElementById("test"),
a = document.getElementById("sucks"),
b = document.getElementById("f_uck");
// 初始focus对象设置为a
a.focus();
// text点击时更改focus对象,但最终改回来
text.attachEvent("onmousedown", function(){
b.focus();
a.focus();
});
// text最终还是focus


// 监听事件触发次序,以对照。因为IE中事件句柄后注册先触发,所以这里在最后注册
var sequence = [];
function record(eventName, domName){
return function(){
sequence.push(domName + ' ' + eventName);
}
}
var toTest = ["mousedown", "mouseup", "focus", "blur"], i, n;
for(i=0; i<toTest.length; i++){
n = toTest[i];
text.attachEvent("on"+n, record(n, "text"));
a.attachEvent("on"+n, record(n, "a"));
b.attachEvent("on"+n, record(n, "b"));
}
// 5秒后弹出记录下来的次序
setTimeout(function(){
alert(sequence.join("\n"));
}, 5000);

</script>
</body>
</html>

在这个案例中,初始focus对象为a(注意IE中按F5刷新会保留上次focus目标,所以请F6+回车进行刷新)
然后,点击text input后依次触发:
(点击) text mousedown
(b.focus) a blur, b focus
(a.focus) b blur, a focus
(mousedown完成,对比焦点通过) a blur, text focus

可以看到最后还是触发了text focus,而如果将以上代码的初始focus设置(a.focus())注释掉,就会发现text focus不会发出了。

[b]结束[/b]
IE sucks...
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值