事件用于监听浏览器的操作行为,浏览器触发动作时被捕捉到而调用相应的函数。
事件执行三个阶段
① 事件捕获阶段 会从document开始触发,一级一级往下传递,依次触发,直到真正事件目标为止。
当某个元素触发事件时,顶级对象document发出一个事件流,顺着DOM树的节点向触发它的目标节点流去,知道到达目标节点,这个向下找目标的过程为事件的捕获阶段。
② 处于目标阶段
到达目标节点后,会执行绑定在此元素上相应的事件函数。
③ 事件冒泡阶段 从当前触发的事件目标一级一级往上传递,依次触发事件,直到document为止。
页面上有好多事件,也可以多个元素响应一个事件.假如:
<BODY onclick="alert('aaa');"> <div onclick="alert('bbb');"> <a href="#" class="cooltip" title="这是我的超链接提示1。" onclick="alert('ddd');"> 提示 </a> </div> </BODY>
上面这段代码一共有三个事件,body,div,a都分别绑定了单击事件。在页面中当单击a标签会连续弹出3个提示框。这就是事件冒泡引起的现象。事件冒 泡的过程是:a --> div --> body 。a冒泡到div冒泡到body。
本来在上面的代码中只想触发<a>元素的onclick事件,然而<div>,<body>事件也同时 触发了。因此我们必须要对事件的作用范围进行限制。当单击<a>元素的onclick事件时只触发<a>本身的事件。由于IE- DOM和标准DOM实现事件对象的方法各不相同,导致在不同浏览器中获取事件的对象变得比较困难。如果想阻止事件的传递,我们可以用 event.stopPropagation()阻止事件的传递行为.
1.event.stopPropagation()方法
这是阻止事件的冒泡方法,不让事件向documen上蔓延,但是默认事件任然会执行,当你掉用这个方法的时候,如果点击一个连接,这个连接仍然会被打开
需要兼容IE浏览器,IE阻止事件冒泡的方式是event.cancelBubble=true;
if(event.stopPropagation){ event.stopPropagation(); } else{ event.cancelBubble=true; //兼容IE浏览器 }
事件传播——冒泡与捕获
DOM事件标准定义了两种事件流,分别是冒泡事件流(从里向外)和捕获时间流(从外向里)
默认情况下,事件采用的是事件采用的是冒泡事件流,当然,也可以指定使用捕获事件流,方法时在监听事件时传入useCapture参数,并将该参数设为true。
oDiv1.addEventListener('click',function(){ alert('div1'); },true) oDiv2.addEventListener('click',function(){ alert('div2'); },true)
当一个元素既有冒泡事件又有捕获事件呢??
其他元素捕获阶段事件——>目标元素按代码顺序执行事件——>其他元素冒泡阶段事件。
p.addEventListener('click', function (e) { alert('父节点冒泡') }, false); p.addEventListener('click', function (e) { alert('父节点捕获') }, true); c.addEventListener('click', function (e) { alert('子节点冒泡') }, false); c.addEventListener('click', function (e) { alert('子节点捕获') }, true); //输出结果 父节点捕获 子节点冒泡 子节点捕获 父节点冒泡 p.addEventListener('click', function (e) { alert('父节点冒泡') }, false); p.addEventListener('click', function (e) { alert('父节点捕获') }, true); c.addEventListener('click', function (e) { alert('子节点捕获') }, true); c.addEventListener('click', function (e) { alert('子节点冒泡') }, false); //输出结果 父节点捕获 子节点捕获 子节点冒泡 父节点冒泡
事件委托(事件代理)
事件委托又称事件代理。
事件委托通过监听一个父元素,来给不同的子元素绑定事件,减少监听次数和dom操作,提高速度。
为什么要用事件委托:
一般来说,dom需要有事件处理程序,我们都会直接给它设事件处理程序就好了,那如果是很多的dom需要添加事件处理呢?比如我们有100个li,每个li都有相同的click点击事件,可能我们会用for循环的方法,来遍历所有的li,然后给它们添加事件,那这么做会存在什么影响呢?
在javascript中,添加到页面上的事件处理程序数量将直接关系到页面的整体运行性能,因为需要不断与dom进行交互,访问dom的次数越多,引起浏览器重排和重绘的次数也越多,就会延长整个页面的交互时间,如果用事件委托,与dom的交互操作就只需要一次,这样大大减少了与dom的交互次数,提高性能。
Event对象提供了一个属性叫target,可以返回事件的目标节点,称为事件源。,也就是说,target就可以表示为当前的事件操作的dom,但是不是真正操作dom,当然,这个是有兼容性的,标准浏览器用ev.target,IE浏览器用event.srcElement,此时只是获取了当前节点的位置
事件委托的原理:
利用事件冒泡的原理来实现的
$(function(){ var oUl = document.getElementById("ul1"); oUl.onclick=function(ev){ var ev=ev||window.event; var target=ev.target||ev.srcElement; if(target.nodeName.toLowerCase()=='li'){ target.style.background='red'; console.log(target.index); } } })
事件委托的优点:
1、可以节省大量内存占用,减少事件注册。
2、当新增对象时,无需再对其进行事件绑定
当页面上有大量元素需要绑定事件时,并不是所有的事件都会被触发,而这时对所有需要监听的元素都绑定事件处理器无疑是要花费代价的,而通过事件委托的方式可以很好的解决性能的问题,不需要为每个元素都绑定事件监听器。但需要逻辑代码来判断事件源。