在我们的SPA项目中(只针对IE浏览器),需要禁止backspace按键的返回上一页面的功能。可是在一次重构之后,好好的功能就突然失效了。在检查过程中,对JS中怎样prevent default action of browser有了点更多的理解。
知识背景
to register event handler, you have 3 options, take onkeydown event as example:
IE prior to IE9 | Module Browser | |
target.onkeydown = handler | Y | Y |
target.attachEvent('onkeydown', handler) | Y | N |
target.addEventListener('keydown', handler, false/true) | N | Y |
to disable default event behavior of browser, you have 3 options to do in event handler:
IE prior to IE9 | Module Browser | |
return false; | Y | Y |
event.returnValue = false; | Y | Y(deprecated) |
event.preventDefault(); | N | N |
解释:
attachEvent, event.returnValue这些都是老旧IE的方式,现在浏览器都使用addEventListener和preventDefault。所以虽然现在浏览器也支持event.returnValue,但是不鼓励这么做。
在event handler中return false的方式来prevent default action,仅仅针对你的handler是通过target.ondkeydown = handler这种方式注册的,其他方式不生效。
我们的项目
重构前能正常工作的代码:
-使用return false的方式prevent default action
-document.onkeydown = function() { /*... */ }的方式处理backspace按键事件
- 项目使用dojo
-在JSP中先引入dojo,在require一些dojo组建,最后embed script中有document.onkeydown = function() {} 的代码
重构后不能工作的代码:
-抽离出一个JS文件,base.js把一些之前embed在JSP中的代码移近去,包括document.onkeydown那段
-在引入dojo.js后,require dojo组建前,引入base.js
- 结果就失效了
原因:
在引入dojo组建时,对dojo里的一段代码对IE浏览器的事件绑定进行了fix (fixAttach),使得document.onkeydown直接对应的代码段为dojo的代码。
而在重构前的代码中,我们的document.onkeydown发生在dojo组建的引用之后,因此我们的代码实际上覆盖了dojo中的代码。于是,该事件绑定是我们通过document.οnkeydοwn=handler的方式做的,因此在里面的return false;能把backspace的behavior阻止掉。
在重构之后,base.js的代码先于require dojo组建,因此document.onkeydown 被dojo重写。dojo的重写不会使得我们的代码调不到,而是使用他们的方式,将之前注册的handler放到一个数组中,然后在一次调用,因此这里就改变了document.onkeydown = handler的方式,但内容受影响。于是,在事件出发后,我们的return false;就不能正常工作了。
解决问题
最好的prevent default action应该这么写(来自犀牛)
function cancelHanlder(event) {
var event = event || window.event;
/* ... */
if(event.preventDefault) event.preventDefault();
if(event.returnValue) event.returnValue = false;
return false;
}