js(十四)---事件机制
事件
事件流
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.这三个兄弟基本是连体的,要用一起用。事件处理程序
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。每一种方式还是有一点不同的,所以那一种我们都必须得学习。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;
}
}