刚学JS的时候就用到了事件,可是却一直没去弄懂是什么原理。各种笔面试中不乏有这问题,整理一下。
一、什么是JS的事件
事件的定义
惯例,事件什么?
...
...
...
来个严谨一点的定义:
事件,就是文档或浏览器窗口中发生的一些特定的交互瞬间。
OK,JS是事件呢。其实就是我们经常接触到的,点击按钮,鼠标悬停之类。
二、事件流
在事件流的概念上,最初两大浏览器厂商IE和Netscape的定义截然不同。
1、事件冒泡
事件冒泡是IE定义的事件流,即事件开始由最具体的元素(例如点击事件中的点击对象)接收,然后逐级向上传播。
2、事件捕获
事件捕获却是认为最不具体的节点最早接收到事件,然后逐级向下传播。
3.DOM事件流
现在新版本的浏览器都采用了DOM事件流的概念。
包括三个阶段:
- 事件捕获阶段
- 处于目标阶段
- 事件冒泡阶段
按顺序执行以上各阶段。
三、事件处理的四个方式
1、HTML事件处理程序
接在HTML代码中添加事件处理程序
<input id="btn" type="button" onclick="clickme(this)">
<script> var btn = document.getElementById("btn"); btn.value = "点我阿~~~"; var text = ["咋了", "点我干嘛", "还点", "不理你了"]; var time = 0; function clickme(e) { if (time > 3 && time < 12) { e.value = "哼"; } else if (time > 11) { e.value = "fuck"; } else { for (var i in text) { e.value = text[time]; } } time++; } </script>
2、DOM0级事件处理程序
相比第一种,DOM0级事件处理直接对于制定对象添加事件。
<input id="btn" type="button">
<script> var btn = document.getElementById("btn"); btn.value = "点我阿~~~"; var text = ["咋了", "点我干嘛", "还点", "不理你了"]; var time = 0; btn.onclick = function(){ if (time > 3 && time < 12) { this.value = "哼"; } else if (time > 11) { this.value = "fuck"; } else { for (var i in text) { this.value = text[time]; } } time++; } </script>
-
如若要删除该事件,可直接将其值设为null
btn.onclick = null;
3、DOM2级事件处理程序
DOM2级事件处理程序定义了两个方法,addEventListener()和removeEventListener().字面即可理解为事件的添加和删除。
<button id="btn-bind">绑定事件</button> <script> function clickme() { alert("美丽最傻"); } document.getElementById("btn-bind").addEventListener("click", clickme, false); document.getElementById("btn-bind").removeEventListener("click", clickme, false); </script>
显而易见。这两个方法都带有三个参数:
- 要处理的事件名
- 作为事件处理程序的函数
- 一个布尔值
解释一下这几个参数:
第一个处理的事件名同上两个处理程序相比,事件名不带on,即点击事件是'click'。
第二个参数是一个函数,这里有一个注意的地方,如果是是采用function(){...}的方式作为第二个参数传入,那是无效的。因为实际上是传入了两个完全不同的函数。
第三个参数,当布尔值为true的时候,表示在事件捕获阶段调用该处理程序,反之则表示在事件冒泡阶段调用。一般建议添加在事件冒泡阶段,为了更好的兼容浏览器。
关于事件捕获和事件冒泡等下说。
4、IE事件处理程序
IE的事件处理程序类似DOM,拥有两个方法,attchEvent()和detachEvent()
<button id="btn-bind">绑定事件</button> <script> function clickme() { alert("美丽最傻"); } document.getElementById("btn-bind").attachEvent("click",clickme); document.getElementById("btn-bind").detachEvent("click",clickme); </script>
有没有发现,其实和DOM2处理程序很相像,只是少了一个参数,由于次方法是针对于IE8及其更早版本的浏览器,所以只支持事件冒泡。
现在浏览器普遍只是DOM2级事件处理,但是由于要兼顾兼容性的问题,还是需要了解这个方法。
四、事件的浏览器兼容性问题
上一节了解了四种处理程序的方法。下面就总结一下,贴一个兼容各浏览器的代码。
var EventUtil = { addHandler:function(element,type,handler) { //body }, getEvent:function(event){ return event ? event : window.event; }, getTarget:function(event){ return event.target || event.srcElement; }, preventDefault:function(event){ if(event.preventDefault){ event.preventDefault(); }eles{ event.returnValue = false; } }, removeHandler:function(element,type,handler){ //body }, stopPropagation:function(event){ if(event.stopPropagation){ event.stopPropagation(); }else{ event.cancelBubble = true; } } };
代码来自《Javascript高级程序设计》(第3版)
五、事件冒泡引发的问题
先贴一段代码
<div id="wrap"> <div id="content"> <div id="father"> Father <div id="son">Son</div> </div> </div> </div> <script> var father = document.getElementById("father"); var son = document.getElementById("son"); father.onclick = function() { alert("I'm father"); } son.onclick = function() { alert("I'm son"); } </script>
上面的栗子中,当我点击了id为son的div之后,点击事件往上传递,这时候就会有引发一个问题,我在id为father上也加了一个点击事件,当son的冒泡事件往上传递到father的时候,同时也触发了father的点击事件,所以会分别alert出“im son”和“i'm father“
在实际应用当中也会有这种困扰,所以这时候我们需要阻止事件冒泡。
<div id="wrap"> <div id="content"> <div id="father"> Father <div id="son">Son</div> </div> </div> </div> <script> function stopEventBubble(event){ var e=event || window.event; if (e && e.stopPropagation){ e.stopPropagation(); //针对除IE外浏览器 } else{ e.cancelBubble=true; //针对IE浏览器 } } var father = document.getElementById("father"); var son = document.getElementById("son"); father.onclick = function() { alert("I'm father"); } son.onclick = function(e) { alert("I'm son"); stopEventBubble(e) } </script>