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