事件是javascript中的核心内容之一,在对事件的应用中不可避免的要涉及到一个重要的概念,那就是事件冒泡,在了解事件冒泡之前,先了解一下事件流
什么是事件流:
从window到document到h1、p、h2、form…等。也就是html元素触发事件,那么这个事件就会在DOM中的触发节点和根节点之间按照一定的顺序传播,所有经过的节点都会接收到被触发的事件,这个传播过程被称之为事件流。
什么是事件冒泡:
就是当一个元素触发一个事件,事件会像是水泡一样,从触发元素向它的所有父节点传播,一直到根节点都会接收到此事件,如果父元素中注册了相应的事件处理函数,那么尽管事件在子节点触发的,在父元素上注册的事件处理函数同样会被触发。
例如:我创建了三个div,给最里头的div加上点击事件(准确的说是给元素加上事件处理函数)
预览效果:
里头的div分别绑定了click事件,然后点击最里头的div3,就会依次弹出div3,div2,div1,这就是事件冒泡,原理是当div3执行完事件处理函数之后,事件就会向父级传播,像冒泡一样所以叫事件冒泡。
假设把div2的事件注释掉1
// dIv2.addEventListener('click',fn);
依然可以弹出div1和div3,事实上div2身上也接收到了默认冒泡事件,只是没有给它事件处理函数,也就是没有告诉它接收到事件之后做什么,所以即使父级不绑定执行的函数,冒泡也默认存在,这就是默认存在的事件冒泡机制。
事件冒泡的影响:
直接举个例子:比如下拉菜单(此处有点丑)
效果图:
需求是点击按钮菜单显示,点击文档区域菜单隐藏,js代码:1
2
3
4
5
6
7
8
9var oBtn=document.getElementById("btn");
var dIv=document.getElementById("div");
oBtn.addEventListener('click', function(){
dIv.style.display="block";
});
document.addEventListener('click',function(){
dIv.style.display="none";
});
那么问题来了,文档还没加上点击事件的时候,按钮点击菜单可以显示,文档加上点击事件之后,按钮点击就点不出菜单了。
这时候给文档点击加上延时执行定时器:1
2
3
4
5document.addEventListener('click',function(){
setTimeout(function(){
dIv.style.display="none";
}, 1000);
});
这时候就可以正常点击。
原理:按钮的点击事件点击之后将事件传递到了document父级,所以按钮点击完成之后由于document也绑定了事件处理函数“菜单隐藏”,所以就直接执行了。
阻止冒泡:
如果在某个地方不想让事件冒泡可以单独阻止,毕竟冒泡事件带来的好处也很大,比如直接触发文档流下的许多元素绑定的事件处理函数
阻止冒泡的方法:在当前要阻止的事件冒泡函数中调用event.cancelBubble=true;
例如将上面的js修改为:1
2
3
4
5
6
7
8oBtn.addEventListener('click', function(ev){
var ev=ev||event;
ev.cancelBubble=true;//阻止当前对象的当前事件的冒泡
dIv.style.display="block";
});
document.addEventListener('click',function(){
dIv.style.display="none";
});
下拉菜单就可以正常跑起来了(虽丑),那么问题又来了,假如按钮身上同时还绑定了一个mouseover的事件,上面设置的阻止冒泡就不生效了,这时候也需要给mouseover事件阻止冒泡,也就是冒泡只能单独阻止。
事件冒泡的用法:
也直接举例子,比如分享功能:
效果图:
需求:鼠标经过的时候从左边出来,鼠标离开的时候只显示分享到部分,传统的做法是分别加上mouseover、mouseout,
但是用冒泡的做法是直接给div去做就行了:
js代码:1
2
3
4
5
6
7var oDiv=document.getElementById("div1");
oDiv.addEventListener('mouseover', function(){
this.style.left="0px";
});
oDiv.addEventListener('mouseout', function(){
this.style.left="-100px";
});
当然,阻止事件冒泡的方法还有两种:
另外两种阻止事件冒泡的方法和用法
方法一:event.stopPropagation();1
2
3$("xxx").click(function(){
event.stopPropagation();
})
方法二:event.target1
2
3
4
5
6
7$(document).ready(function(){
$("#xxx").click(function(event){
if(event.target==this){
$("#xxx").xxx
}
})
})
顺便介绍一下event对象
Event对象
当一个事件发生的时候,和当前这个对象发生的这个事件有关的一些详细的信息都会被临时保存到一个指定地方—event对象,供需要的时候调用
用来获取事件的详细信息。
Event对象的兼容:
1 | ev=ev||window.event |
Event对象下获取鼠标位置:
1 | clientX clientY |
事件对象必须在一个事件调用的函数里面使用才有内容
事件函数
事件调用的函数,取决于被什么时间调用
IE、谷歌和火狐下的ev
iE/chrome:event是一个内置全局对象
Firefox(同时也是标准下):事件对象是通过事件函数的第一个参数传入如function fn(ev)====(如果一个函数是被事件调用的,那么这个函数定义的第一个参数就是事件对象)
ev用的时候(兼容处理):
1 | var ev=ev||evnt |
查看事件:
1 | for(attr in ev){ |
同时联想到一些可能用得上的事件,算了也写在这里吧:
onscroll:当滚动条滚动的时候触发
onresize:当窗口大小发生改变的时候触发
onfocus:获取焦点事件
onblur:失去焦点事件
focus():页面打开给指定元素设置焦点…txt.focus();
blur()取消指定元素的焦点
select()选择指定元素的文本内容(只能操作可交互性的文本内容)
最后再来一个判断是否IE浏览器的方法
js查看浏览器信息1
alert(window.navigator.userAgent);
验证IE1
2
3
4
5if(window.navigator.userAgent.indexOf('MSIE')!=-1){
alert("IE");
}else{
alert("不是IE");
}
那既然写了事件,就多写一些吧(其实这里是因为在这地方突然没网,线上更新不了,用手机开热点手机坏了(已扔下一楼),然后这里顺便扯一些其他的东西)
绑定多个事件
如果多个部门或者多个人都需要对同一个元素绑定事件
A部门绑定click事件A,B部门绑定click事件B,一般如果写成onclick的话,一点击就会覆盖掉另一个部门的东西,解决方案:
1、部门之间开发的时候密切沟通好里头的各种命名以及结构数据等,然后将两个事件合并,那么好,两个部门同时的话,两个部门一起交流沟通,好,这时候如果每个部门的需求是每部门再来个事件,行,那就沟通呗,一个星期达成共识,如果需求里头代码多,变量多,那继续沟通呗,好,老板这时候或许已经在想宣告破产之后的情景了。
2、绑定多个事件用addEventListener(事件,函数处理方法,true或者false(代表捕获与不捕获)),IE下是attachEvent(on+事件,函数处理方法)
那么什么是事件捕获:
捕获机制如果触发的话,事件会从父级元素经过,然后到触发的元素,和冒泡事件不同的是,冒泡是从触发的事件向父级传输
call()方法
attachEvent绑定的事件,this指向不会指向触发事件的元素,而是window,所以要用到call()方法,例如触发fn函数,写成fn.call();
call()方法里头的参数是触发的事件处理函数里头的this指向,如果是多个参数的函数例如fn(obj,attr),就写成1
fn.call(window,oDiv,class);
这里第一个参数依旧是事件处理函数里头的this指向的window,后面两个参数是传给函数方法的参数。
最后封一个绑定多个事件的方法,蛮实用
1 | function bind(obj,eventName,fn){ |
事件绑定的取消
1、事件绑定形式的取消1
2document.onclick=fun;
document.onclick=null;
2、通过addEventListener()取消1
2IE下:obj.detachEvent('onclick',fun)
标准下:obj.removeEventListener(事件名称,事件处理函数,是否捕获)