第七章 事件处理
7.1 典型用法
作者首先给了个我们一个处理事件的方法。看起来也没啥俩样,不过后来给出的优化方法很值得学习:
// 不好的写法
function handleClick(event) {
var popup = document.getElementById("popup");
popup.style.left = event.clientX + "px";
popup.style.top = event.clientY + "px";
pop.className = "reveal";
}
// 一个事件绑定事件(第5章的)
addListener(element, "click", handleClick);
作者补充了一句:这段代码只用到了event对象的俩个属性:clientX和clientY。在将元素显示在页面里之前先用这俩个属性给它做定位。
尽管这段代码开起来非常简单且没有什么问题,但实际上是不好的写法,因为这种做法有其局限性。
7.2 规则 1:隔离应用逻辑
首先,这段代码的应用逻辑(application logic)和用户行为混在一起了。就是说:这个事件的处理方法和点击事件连在一起了,从而导致耦合度的提高。
这个情况其他语言中也很常见,通常我们将处理逻辑封装起来从而可以把代码重用,所以作者提出了很重要的思想:
将应用逻辑从所有事件处理程序中抽离出来的做法是一种最佳实践。
将代码重构一下变成这样:
// 好的写法 - 拆分应用逻辑
var MyApplication = {
handleClick: function(event){
this.showPopUp(event);
},
showPopUp: function(event) {
var popup = document.getElementById("popup");
popup.style.left = event.clientX + "px";
popup.style.top = event.clientY + "px";
popup.className = "reveal";
}
};
addListener(element, "click", function(event) {
MyApplication.handleClick(event);
});
7.3 规则2:不要分发事件对象
上述代码还存在一个问题:即event对象被无节制地分发。event对象上包含很多的和事件相关的额外信息,而这段代码只用到了其中俩个而已。
作者提供了俩个理由:
- 方法接口并没有表明那些数据是必要的。好的API一定是对于期望和依赖都是透明的。(译者在此也加了译注:大多数情况并不理想,主要看第二点)。
- 方便测试
最佳的办法是让事件处理程序使用event对象来处理事件,然后拿到所有需要的数据传给应用逻辑。
将代码重构一下:
// 好的写法
var MyApplication = {
handleClick: function(event){
this.showPopUp(event.clientX,event.clientY); // 这里标明所期待传入的参数
},
showPopUp: function(x,y) { // 这里的参数就不再依赖event咯
var popup = document.getElementById("popup");
popup.style.left = x + "px";
popup.style.top = y + "px";
popup.className = "reveal";
}
};
addListener(element, "click", function(event) {
MyApplication.handleClick(event);
});
测试时或者代码的任意位置都很轻易调用这段逻辑:
// 这样调用非常棒
MyApplication.showPopUp(10, 10);
最后,当处理事件时,最好让事件处理程序成为接触到event对象的唯一的函数。事件处理程序应当在进入应用逻辑之前针对event对象执行任何必要的操作,包括阻止默认事件或阻止事件冒泡,都应当直接包含在事件程序中。
// 好的写法
var MyApplication = {
handleClick: function(event){
// 假设事件支持DOM Level2
event.preventDefault();
event.stopPropagation();
// 传入应用逻辑
this.showPopUp(event.clientX,event.clientY);
},
showPopUp: function(x,y) {
var popup = document.getElementById("popup");
popup.style.left = x + "px";
popup.style.top = y + "px";
popup.className = "reveal";
}
};
在这段代码中,MyApplication.handleClick()是事件处理程序,因此它在数据传入应用逻辑之前调用 event.preventDefault()和 event.stopPropagation(),这清楚的展示了事件处理程序和应用逻辑之间的分工。因为应用逻辑不需要对event产生依赖,进而在很多地方都可以轻松地使用相同的业务逻辑,包括写测试代码。