Javascript事件处理进阶
2015-04-15 15:21
120 查看
这篇文章是我在看乌龟书《编写可维护的Javascript》发现的一篇写的非常好的章节,在这里我并不会教大家什么是绑定事件等比较基础的事。有兴趣了解DOM事件的同学们,可以去w3cschool查阅。
上面写了一个兼容性强的DOM事件绑定,但希望大家要注意:DOM0级的事件只可单次绑定,再绑定会导致覆盖的问题出现。
乍一看,这是没什么问题的。确实它可以运行的很好,这毋庸置疑。可是你是否有考虑过,你的事件处理程序中,却包含了与用户行为无关的应用逻辑,也就是指弹框这个行为。或许当我们使用mousemove事件时,也需要这段应用逻辑,并可以为此单独拓展一些行为时,我们就只能通过copy同样的应用逻辑并在里面添加自己需要的代码。
而另一个同样明显的缺点,则是关于测试的。如果测试需要通过模拟触发事件的形式进行的,现有的测试框架并不是特别稳定和理想。而将隔离出应用逻辑,可以使我们直接触发功能代码。
我们将应用逻辑转移到了showPopup函数中,这样使得弹出框的应用逻辑独立了出来,我们就可以在事件处理上更加灵活。例如我在handlerClick加了一个alert函数,这并不会影响到handlerMousemove里弹框的操作。当然这只是拆解程序代码的第一步。
若我们不明确应用逻辑要做什么事情时,直接传入event参数,无可厚非。可是这一次我们其实是明确知道我们仅仅是需要一个x坐标和y坐标,所以我们可以通过事件处理函数对event进行筛选。
那这样写之后,当我们要对这个应用逻辑进行测试时,就不再依赖event对象了,我们可以直接传入应用逻辑所期盼的参数。在这里就是有关x,y的坐标。
当event被提取出来后,应用逻辑则更为独立,不过还有一些小细节要处理。就是关于阻止默认行为
最后我们把事件处理程序和应用逻辑之间的分工清晰的分开后,我们可以在很多地方轻松使用相同的业务逻辑,包括前端开发者最头疼的测试代码。
事件绑定
大家都知道前端开发中,事件处理是非常重要的。我们在view层的交互,都是通过绑定事件到UI上,然后我们再处理这些事件。让我们写一个事件绑定的函数先。var addListener = function(target, type, listener){ if(target.addEventListener){ //2级的DOM事件绑定 target.addEventListener(type, listener, false); }else if(target.attchEvent){ //IE事件绑定 target.attchEvent("on" + type, listener); }else{ //0级的DOM事件绑定,也是最可靠的,但只可单次绑定 target["on" + type] = listener; } }
上面写了一个兼容性强的DOM事件绑定,但希望大家要注意:DOM0级的事件只可单次绑定,再绑定会导致覆盖的问题出现。
常见用法
当事件触发后,会有一个event回调参数传入事件处理中。而所有的有关事件的信息都会储存在event对象中。我们假设需要做一个点击,并在鼠标的位置弹出框。我们是这样写的。var handlerClick = function(event){ var popup = document.getElementById("popup"); popup.style.left = event.clientX; popup.style.top = event.clientY; popup.className = "active"; } addListener(element, "click", handlerClick);
乍一看,这是没什么问题的。确实它可以运行的很好,这毋庸置疑。可是你是否有考虑过,你的事件处理程序中,却包含了与用户行为无关的应用逻辑,也就是指弹框这个行为。或许当我们使用mousemove事件时,也需要这段应用逻辑,并可以为此单独拓展一些行为时,我们就只能通过copy同样的应用逻辑并在里面添加自己需要的代码。
而另一个同样明显的缺点,则是关于测试的。如果测试需要通过模拟触发事件的形式进行的,现有的测试框架并不是特别稳定和理想。而将隔离出应用逻辑,可以使我们直接触发功能代码。
规则1:隔离应用逻辑
那我们应该如何拆分应用逻辑和事件处理代码呢?首先我们使用模块模式,将所有有关事件处理的函数放在里面,也能防止全局污染。一起来看看:var MyAppEvent = { handlerClick: function(event){ this.showPopup(event); alert("click it!"); }, handlerMousemove: function(event){ this.showPopup(event); } showPopup: function(event){ var popup = document.getElementById("popup"); popup.style.left = event.clientX; popup.style.top = event.clientY; popup.className = "active"; } } //绑定事件 addListener(element, "click", function(event){ MyAppEvent.handlerClick(event); })
我们将应用逻辑转移到了showPopup函数中,这样使得弹出框的应用逻辑独立了出来,我们就可以在事件处理上更加灵活。例如我在handlerClick加了一个alert函数,这并不会影响到handlerMousemove里弹框的操作。当然这只是拆解程序代码的第一步。
规则2:不要分发事件对象
我们可以看到在MyAppEvent中,event参数传的到处都是。这其实是没问题的,只是有点无节制的分发罢了。因为我们在showPopup函数中,仅仅只是用到了event对象里的clientX和clientY。其它的都是不必要的。我们应该认为,关于应用逻辑,它其实是不能依赖太多东西的。包括触发的事件(这个我们刚才已经妥善处理了),还有就是回调参数event。若我们不明确应用逻辑要做什么事情时,直接传入event参数,无可厚非。可是这一次我们其实是明确知道我们仅仅是需要一个x坐标和y坐标,所以我们可以通过事件处理函数对event进行筛选。
var MyAppEvent = { handlerClick: function(event){ this.showPopup(event.clientX, event.clientY); //改变的地方 alert("click it!"); }, handlerMousemove: function(event){ this.showPopup(event.clientX, event.clientY); //改变的地方 } showPopup: function(x, y){ //改变的地方 var popup = document.getElementById("popup"); popup.style.left = event.x; popup.style.top = event.y; popup.className = "active"; } } //绑定事件 addListener(element, "click", function(event){ MyAppEvent.handlerClick(event); })
那这样写之后,当我们要对这个应用逻辑进行测试时,就不再依赖event对象了,我们可以直接传入应用逻辑所期盼的参数。在这里就是有关x,y的坐标。
// API的参数更加明确,不再依赖event MyAppEvent.showPopup(10, 10);
当event被提取出来后,应用逻辑则更为独立,不过还有一些小细节要处理。就是关于阻止默认行为
event.preventDefault()与阻止冒泡行为
event.stopPropagation(),则也需要在事件处理函数中提前处理。
最后我们把事件处理程序和应用逻辑之间的分工清晰的分开后,我们可以在很多地方轻松使用相同的业务逻辑,包括前端开发者最头疼的测试代码。
相关文章推荐
- JavaScript:事件流与事件处理程序
- JavaScript_form表单验证:采用focus()事件处理;
- 基于HTML标签的JavaScript触发事件处理
- javascript同一个事件如何注册多个处理函数
- javascript的闭包中保存的是引用与循环中事件函数处理
- 2015/12/12--javascript事件处理和部分高级javascript实例
- javascript 处理鼠标右键事件
- JavaScript移除绑定在元素上的匿名事件处理函数
- JavaScript移除绑定在元素上的匿名事件处理函数
- 探究JavaScript中的五种事件处理程序
- javascript的事件处理(一)——基础原理
- Javascript中的事件处理程序
- javascript学习-事件处理
- javascript 事件处理程序介绍
- javascript 跨浏览器事件处理
- javascript 事件处理程序介绍
- 跨浏览器的事件处理方法(Professional JavaScript for Web Developers)
- JavaScript中的事件处理(一)
- javascript事件处理
- JavaScript中的事件处理