JWeb前端的知识之旅哟—— 事件的绑定与解除

事件是交互体验的核心功能,非常非常重要。

事件是每一个浏览器本来就有的,我们只是给相应的事件添加了一个回调函数

下面先看一个拖拽事件的例子来感受一下事件:



1. div.addEventListener(‘mousedown’, function (e){
2. var disX = e.clientX - parseInt(getStyle(this, ‘left’)),
3. disY = e.clientY - parseInt(getStyle(this, ‘top’));
4. document.addEventListener(‘mousemove’, mouseMove, false);
5. div.addEventListener(‘mouseup’, function (e) {
6. document.removeEventListener(‘mousemove’, mouseMove, false);
7. }, false);
8. }, false);
9.  
10. function mouseMove(e) {
11. div.style.left = e.clentX - disX + ‘px’;
12. div.style.top = e.clientY - disY + ‘px’;
13.  
14. }
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)

我们首先看一下如何绑定事件,这里我们以点击事件为例。

绑定事件

1.句柄方式



1. var div = document.getElementsByTagName(‘div’)[0];
2. div.onclick = function (e) {
3. console.log(‘a’);
4. }
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)

后面的这个函数就叫做事件处理函数,也称回调函数,当我们点击事件触发的时候,就会执行后面的处理函数。

事件可以持续监听,并不是执行完一次就失效,因此这个事件监听部分并不属于js引擎,因为js引擎是单线程的,事件监听属于内核的其他模块部分,一旦事件触发,事件监听就会把处理函数放入执行队列,等待js引擎来执行。

虽然句柄方式的兼容性很好,但是一个元素的一种事件只能绑定一个函数

基本等同于写在行间的事件。



1. <div onclick=console.log(‘a’)></div>
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)

这两种写法是一致的。

一些比较简单的函数可以用这种方式来写。

2.ele.addEventListener(type, handle, false)方法。



1. div.addEventListener(‘click’, function(e) {
2. console.log(‘a’);
3. }, false);
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)

这里面有三个参数,第一个参数是事件类型,第二个参数是处理函数,第三个参数是是否捕获。

处理函数可以直接在addEventListener方法里面写一个匿名函数,也可以在外面写一个命名函数,然后在放法里面写函数的引用。

这种方法更加通用常见,而且一种事件可以绑定多个函数,但是同一个处理函数只能绑定一次



1.  
2. function test1 () {
3. console.log(‘a’);
4. }
5. function test2() {
6. console.log(‘a’);
7. }
8. div.addEventListener(‘click’, test1, false);
9. div.addEventListener(‘click’, test2, false);
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)

点击一次同时打印a和b。

这是唯一一个有事件捕获的方法。

注意:即使函数体相同,两个函数也不是一个函数。

不过很遗憾的是,这种方法在IE9以下不兼容

3.ele.attachEvent(‘on’ + type, handle)

这个方法是IE独有的方法,一个事件同样可以绑定多个处理函数



1.  
2. div.attachEvent(‘onclick’, function (){
3. console.log(‘a’);
4. });
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)

基本和addEventListener差不多,但是有一点区别是,当同一个函数绑定多次的时候,addEventListener是只执行一次,但是attachEvent会绑定几次执行几次



1.  
2. function test () {
3. console.log(‘a’);
4. }
5. div.attachEvent(‘onclick’, test);
6. div.attachEvent(‘onclick’, test);
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)

现在点击一次会打印两个a。

现在这里有一个笔试常考的绑定事件的题:使用原生js,addEventListener,为每一个li绑定一个点击事件,输出他们的顺序。

这里就要注意这个题考察的不仅仅是绑定事件,更多的是闭包的运用。



1.  
2. var $Li = document.getElementsByTagName(‘li’);
3. for (var i = 0, len = $Li.length; i < len; i++) {
4. (function (n) {
5. $Li[n].addEventListener(‘click’, function () {
6. console.log(n);
7. },false);
8. } (i))
9. }
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)

事件处理程序的运行环境

1.句柄绑定方式中,函数里面的this指向元素本身

2.addEventListener方式中,函数里面的this也是指向元素本身

3.attachEvent中,函数里面的this指向的是window而不是元素本身,这算是IE的一个BUG。针对这种情况,我们就需要把函数提取出来,然后在attachEvent的时候用call来改变函数内部this的指向。



1. div.attachEvent(‘onclick’, function () {
2. test.call(div);
3. }, false);
4.  
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)

有了以上的知识,我们就可以封装一个兼容性的事件绑定函数了。



1.  
2. function attachEvent(ele, type, handle) {
3. if (ele.addEventListener) {
4. ele.addEventListener(type, handle, null);
5. }else if (ele.attachEvent) {
6. ele.attachEvent(‘on’ + type, function () {
7. handle.call(ele);
8. });
9. }else {
10. ele[‘on’ + type] = handle;
11. }
12. }
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)

这样事件绑定基本就完成了,但是还可以优化一下,在第二个IE的方法中我们用了一个匿名函数,这样这个函数就无法解除绑定了,因此可以优化成命名函数。



1.  
2. function attachEvent(ele, type, handle) {
3. if (ele.addEventListener) {
4. ele.addEventListener(type, handle, null);
5. }else if (ele.attachEvent) {
6. ele[‘temp’ + type + handle] = handle;
7. ele[type + handle] = function () {
8. ele[‘temp’ + type + handle].call(ele);
9. };
10. ele.attachEvent(‘on’ + type, ele[type + handle]);
11. }else {
12. ele[‘on’ + type] = handle;
13. }
14. }
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)

这种写法应该是业内公认的事件绑定方式。这里专门处理了IE方法中的匿名函数问题,我们用元素自身的一个属性来保存了这个处理函数。

解除事件处理程序

1.句柄方式

ele.onclick=null

这样很简单的就可以解除绑定的事件处理函数了。

2.ele.removeEventListener(type, handle, false)

针对的addEventListener的解除绑定。

但是这里要注意,只有命名函数才可以解除绑定,当绑定的函数是匿名函数的时候,是没有办法解除绑定的。



1.  
2. div.addEventListener(‘click’, function (){console.log(‘a’);}, false);
3. div.removeEventListener(‘click’, function (){console.log(‘a’);}, false);
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)

这是没有办法解除绑定的,因为这是两个匿名函数。



1.  
2. function test() {
3. console.log(‘a’);
4. }
5. div.addEventListener(‘click’, test, false);
6. div.removeEventListener(‘click’, test, false);
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)

必须改成这种方式才可以解除绑定。

3.ele.detachEvent(‘on’ + type, handle)

针对IE的attachEvent的解除绑定。

也是同一个函数才可以解除绑定,匿名函数无法解除绑定

封装兼容性的解除绑定函数:



1.  
2. function remvoeEvent(ele, type, handle) {
3. if(ele.removeEventListener) {
4. ele.removeEventListener(type, handle, false);
5. }else if (ele.detachEvent) {
6. ele.detachEvent(‘on’ + type, handle);
7. }else {
8. ele[‘on’ + type] = null;
9. }
10. }
JavaScript; “复制代码”); “查看纯文本代码”); “返回代码高亮”)

今天的事件只是就到这里哟~