欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

js(十四)---事件机制

程序员文章站 2024-02-25 21:35:27
...

事件 

    事件机制分为四个环节:1.事件流(冒泡、捕获)2. 事件处理程序  3. 事件对象  4.事件委托

事件流


事件流:指从页面中接收事件的顺序,有冒泡流和捕获流。
当页面中发生某种事件(比如鼠标点击,鼠标滑过等)时,毫无疑问子元素和父元素都会接收到该事件,可具体顺序是怎样 的呢?冒泡和捕获则描述了两种不同的顺序。

那么冒泡和捕获十一什么样的形式发生的呢?接下来,一张图,帮助你来理解:

                                         js(十四)---事件机制
 
 这个图形很清楚的介绍了这个过程。


      你想一下,js的语言特点是什么就知道了,其中一个特点就是事件机制。这样你就知道,这个事件的重要性了,这也是面试老生常谈的话题。

       事件是每一个浏览器本来就有的,我们只是给相应的事件添加了一个回调函数。这样在触发一个函数之后,相应的会进行调用回调函数。那么该有疑问了,事件是用来做什么的,那么用一个小demo来感受一下,这个事件是用来做什么用的。

div.addEventListener('mousedown', function (e){
var disX = e.clientX - parseInt(getStyle(this, 'left')),
	disY = e.clientY - parseInt(getStyle(this, 'top'));//这是获取鼠标点跟元素最左边和最上边的距离
document.addEventListener('mousemove', mouseMove, false);//用document是有原因的,加入你拖动的比较快,会脱离这个元素,如果你用元素的话,这个元素会停止。
	div.addEventListener('mouseup', function (e) {
		document.removeEventListener('mousemove', mouseMove, false);
	}, false);
}, false);
 
function mouseMove(e) {
      div.style.left = e.clentX - disX + 'px';//
      div.style.top = e.clientY - disY + 'px';//必须要减去diX、diY,这样这个鼠标点才会在你指定的地方。
 
}
这只是一个简单的拖拽的例子。里面用到了的事件有onmousedown、onmousemove、onmouseup.这三个兄弟基本是连体的,要用一起用。


事件处理程序

事件的绑定

1.句柄方式

var div = document.getElementsByTagName('div')[0];
div.onclick = function (e) {
      console.log('a');
}
div.onclick = function(){
       console.log("b")
 }
//这样点击之后只会打印出b.

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

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

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

如果用这种方式,绑定多个事件的话,前面的事件就会被覆盖掉的,所以还是相当于就绑定了一个事件。虽然说有好处,但是缺点要比好大的多。

这种方式也可以写在行间样式上面:

<div onclick="console.log('a')"></div>
或者里面写一个函数名,在js中把这个函数给定义出来,也相当于一个执行队列,等待着被调用。

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

div.addEventListener('click', function(e) {
      console.log('a');
}, false);

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

当第三个对象为true时,则就变成了捕获。

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

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

 
function test1 () {
      console.log('a');
}
function test2() {
      console.log('a');
}
div.addEventListener('click', test1, false);
div.addEventListener('click', test2, false);
这样的话,这两个函数都会执行,也不会被覆盖的。

当然它也是唯一一个可以进行捕获的事件绑定的方式。但是兼容性的问题,IE9以下是不兼容的。


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

这种方式是IE独有的一种方式,同样也是可以进行绑定多个回调函数的。

 
div.attachEvent('onclick', function (){
      console.log('a');
});
基本和addEventListener差不多,但是有一点区别是,当同一个函数绑定多次的时候,addEventListener是只执行一次,但是attachEvent会绑定几次执行几次

 
function test () {
      console.log('a');
}
div.attachEvent('onclick', test);
div.attachEvent('onclick', test);
这样回打印两个a。每一种方式还是有一点不同的,所以那一种我们都必须得学习。

接下来,用的比较的多的,也是特别使用的是,在这三个时间处理中的this指向是很重要的。


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

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

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

div.attachEvent('onclick', function () {
      test.call(div);
}, false);
 
有了这些作为知识点,那样的话,我们就可以封装一个函数,来做一下兼容处理:

 
	function attachEvent(ele, type, handle) {
		if (ele.addEventListener) {
			ele.addEventListener(type, handle, false);
		}else if (ele.attachEvent) {
			ele.attachEvent('on' + type, function () {
				handle.call(ele);
			});
		}else {
			ele['on' + type] = handle;
		}
	}
这样还是存在一个小问题,因为第二种方法引用了一个匿名函数,这样我们就解除不了绑定了,所以,我们得把这个匿名函数变成“有名”的不就行了吗。接下来进行优化:

 function attachevent(ele, type, handle){
            if(ele.addEventListener){
                ele.addEventListener(type, handle, false)
            }
            else if(ele.attachEvent){
                ele['similate' + type + handle] = handle;//把这个函数给保存起来
                ele[type + handle] = function(){//给匿名函数一个名字,这样就能找到
                    ele['similate' + type + handle].call(ele);//改变一下this的指向
                }
                ele.attachEvent('on' + type,ele[type + handle])//这样的绑定就可以解除了
            }
            else{
                ele['on' + type] = handle;
            }
        }

这里专门处理了IE方法中的匿名函数问题,我们用元素自身的一个属性来保存了这个处理函数。这样才是最完整的一个方法了,也是经常用的一个方法了。


有绑定,就会有解除绑定的:

1.句柄方式

ele.onclick=null

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

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

针对的addEventListener的解除绑定。

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


 
div.addEventListener('click', function (){console.log('a');}, false);
div.removeEventListener('click', function (){console.log('a');}, false);

这种形式是解除不了绑定的,刚才封装方法的时候,就特别进行优化的。

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

针对IE的attachEvent的解除绑定。

如果不想改变this指向问题,这就无所谓了,但是想要改变this指向,就一定要注意一下了。

封装解除绑定的方法:

 
function remvoeEvent(ele, type, handle) {
      if(ele.removeEventListener) {
            ele.removeEventListener(type, handle, false);
      }else if (ele.detachEvent) {
            ele.detachEvent('on' + type, handle);
      }else {
            ele['on' + type] = null;
      }
}