先说下js事件中几个重要的概念:事件对象、目标元素(事件源)、冒泡事件流、捕获事件流。
事件对象:事件对象只有在事件发生时才会产生,是用来记录事件发生时相关信息的对象,只能在事件处理函数内部访问。目标元素: 任何一个事件的目标元素都是最开始的那个元素,并且它在事件对象中以属性的形式出现。使用事件代理的话我们可以把事件处理器添加到一个元素上,等待事件从它的子级元素里冒泡上来,并且可以很方便地获取这个事件的目标元素。js中目标元素又称为事件源。
冒泡事件流: 当一个元素上的事件被触发的时候,比如说鼠标点击了一个按钮,同样的事件将会在那个元素的所有祖先元素中被触发,从原始元素开始一直冒泡到DOM树的最上层,这一过程被称为事件冒泡。在冒泡过程中的任何时候都可以终止事件的冒泡,在现代浏览器里可以通过调用事件对象上的stopPropagation()方法,在IE里可以通过设置事件对象的cancelBubble属性为true。也就是说,如果不停止事件的传播,由于js事件冒泡的机制,子元素会触发父级的事件,一直冒泡到最上层的元素(事件将一直通过DOM冒泡直至到达文档根)。 总结:冒泡型事件按照从最特定的事件目标到最不特定的事件目标的顺序触发(冒泡是从下级元素到上级元素)。捕获事件流:事件的处理将从DOM层次的根开始,而不是从触发事件的目标元素开始,事件从目标元素的所有祖先元素依次往下传递。在这个过程中,事件会从文档根到事件目标元素之间各个继承派生的元素所捕获,如果事件监听器在被注册时设置了useCapture属性为true,那么它们可以被分派给这期间的任何元素以对事件做出处理;否则,事件会被接着传递给派生元素路径上的下一元素,直至目标元素。事件到达目标元素后,它会接着通过DOM节点再进行冒泡。 总结:捕获型事件按照从最不特定的事件目标到最特定的事件目标的顺序触发(与冒泡型事件顺序相反,捕获是从上级元素到下级元素)。 IE浏览器中的事件流:只支持冒泡事件流,不支持捕获事件流。 现代浏览器中的事件流:同时支持两种事件模型:捕获型事件和冒泡型事件,且捕获型事件先发生。 window->document->html->body->div->body->html->document->window 所以对于事件目标是接受了2次事件,一次是捕获过程,一次是冒泡过程。 事件冒泡的优点和缺点 想象一下现在我们有一个10列、100行的HTML表格(采用div-ul-li合成的表格),你希望在用户点击表格中的某一单元格(li)的时候做点什么,比如需要让表格中的每一个单元格在被点击的时候变成可编辑状态。如果把事件处理器加到这1000个单元格会产生一个很大的性能问题,有可能导致内存泄露甚至是浏览器的崩溃。相反地,使用事件代理的话,你只需要把一个事件处理器添加到表格div上就可以了,这个函数可以把点击事件给截下来,并且判断出是哪个单元格被点击了。 我们为这个表格div添加一个事件处理器以调用clickFun函数,此函数需要先判断出传到div来的事件的目标元素,然后再根据具体的条件去做相应的处理即可:所以总结下冒泡的优点有:
1.需要创建的以及驻留在内存中的事件处理器少了,提高了性能,并降低了崩溃的风险。 2.在DOM更新后无须重新绑定事件处理器。如果你的页面是动态生成的,你不再需要在元素被载入或者卸载的时候添加或者删除事件处理器。 缺点:你的事件管理代码有成为性能瓶颈的风险,所以尽量使它能够短小精悍。 并不是所有的事件都能冒泡,blur、focus、load和unload不能像其它事件一样冒泡。 停止事件冒泡 IE:window.event.cancelBubble=true; 现代浏览器:e.stopPropagation(); 阻止事件的默认行为 IE:window.event.returnValue=false; 现代浏览器:e.preventDefault(); 事件处理函数 IE中: obj.attachEvent(evtype,fn)---IE提供的添加事件处理函数的方法。obj是要添加事件的dom对象,evtype是事件类型,带on前缀,fn是事件处理函数。缺点:IE不支持事件捕获,且使用IE的attachEvent方法时在事件处理函数内部,this指向了window,而不是obj!解决attachEvent中this指向问题的方法是: obj.attachEvent("on"+evtype,function(){fn.call(obj)}); obj.detachEvent(evtype,fn,)---IE提供的删除事件处理函数的方法,evtype包含on前缀。 现代浏览器: obj.addEventListener(evtype,fn,useCapture)——W3C提供的添加事件处理函数的方法。obj是要添加事件的对象,evtype是事件类型,不带on前缀,fn是事件处理函数,如果useCapture是true,则事件处理函数在捕获阶段被执行,否则在冒泡阶段执行 obj.removeEventListener(evtype,fn,useCapture)——W3C提供的删除事件处理函数的方法 obj.event=fn这样事件处理函数会被添加到冒泡阶段 attachEvent和addEventListener方法的使用:addEventListener参数说明
addEventListener有三个参数:第一个参数表示事件名称(不含on,如"click");第二个参数表示要接收事件处理的函数;第三个参数为useCapture,下面就讲解它:上述是我们测试的代码,根据info的显示来确定触发的顺序,有三个addEventListener,而useCapture可选值为true和false,所以2*2*2,可以得出8段不同的程序:
全为false时,触发顺序为:inDiv、middleDiv、outDiv; 全为true时,触发顺序为:outDiv、middleDiv、inDiv; outDiv为true,其他为false时,触发顺序为:outDiv、inDiv、middleDiv; middleDiv为true,其他为false时,触发顺序为:middleDiv、inDiv、outDiv; …… 最终得出如下结论: true的触发顺序总是在false之前; 如果多个均为true,则外层的触发先于内层; 如果多个均为false,则内层的触发先于外层。 附:几个常用的事件相关的处理方法附:js常用事件