直观的说就是网页上发生的事情,按照个人概括来讲就是:动做、变化、加载完成。具体来讲就是鼠标点击某些内容,通过某些元素,键盘按下按键,web页面加载完成,以及浏览器窗口大小变化,滚动条滚动......javascript
事件是构成动态网页交互必不可少的条件,经过使用 JavaScript ,你能够监听特定事件的发生,并规定让某些事件发生以对这些事件作出响应。css
其事件类型分为 “输入事件(如onclick)” 和“语义事件(如onsubmit)”。html
经过javascript指定原始事件处理程序的方式,就是将一个函数值赋值给一个事件处理程序属性,每一个元素(包括window 和 document)都有本身的事件处理程序属性,这些程序一般所有小写(如:onclick)。将这些属性的值设置成一个函数,便可指定事件处理程序。以下所示:前端
1 var test1 = document.getElementById("test1"); 2 test1.onclick = function(){ 3 alert("绑定点击事件"); 4 }
或者直接经过JS代码做为HTML性质值完成事件绑定:java
<a id="test1" href="javascript:void(0)" onclick="alert('点击事件绑定')">test1</a>
经过给当前元素的属性指定事件处理程序,是在当前元素的做用域中运行,所以程序中的this引用的是当前元素,再看个例子:node
1 var test1 = document.getElementById("test1"); 2 test1.onclick = function(){ 3 alert(this.id); //"test1" 4 }
由于事件的处理程序是做为JS的属性,所以可使用JS属性显式调用:web
document.getElementById("test1").onclick();//窗口弹出对话框:"点击事件绑定"
由于事件的处理程序是做为JS的属性,所以也能够经过将属性值设置为null 的方式来达到删除事件程序的目的chrome
document.getElementById("test1").onclick = null ; //删除点击事件
*优势:全部浏览器都兼容浏览器
*缺点:1)逻辑与显示没有分离;dom
2)相同事件的监听函数只能绑定一个,后绑定的会覆盖掉前面的,如:a.onclick = func1; a.onclick = func2;将只会执行func2中的内容。
3)没法经过事件的冒泡、委托等机制 完成更多事情。
在当前web程序模块化开发以及更加复杂的逻辑情况下,这种方式显然已经落伍了,因此在真正项目中不推荐使用,平时写点博客小例子啥的却是能够,速度比较快。
在IE事件模型中,事件的表示做为全局函数window的一个属性event。在ie中(ie8及更早),默认的全局属性event值为null,而后在进入事件处理程序中时,系统将window.event 的值设置为当前事件类型,而且在每次事件处理完成以后 又将window.event 的值设置成null。为了求证,咱们在IE8下运行以下代码:
1 console.log(window.event); //null 2 window.onload = function(){ 3 console.log(window.event) // Event {isTrusted: true, type: "load", target: document, currentTarget: Window, eventPhase: 2…} 4 setTimeout(function(){console.log(window.event);},100) //null 5 }
所以咱们能够得知
1.在html运行时,window.event 被定义,初始值为null。
2.在咱们进入window.onload 函数中时,window.event 的值为当前类型的事件。
3.当咱们完成事件过程后,window.event 又被设置为null。
IE的事件模型执行过程只有两步,对应DOM2事件的 2(事件处理),3(事件冒泡) 阶段。先执行元素的监听函数,而后事件沿着父节点一直冒泡到document。
IE模型下的事件绑定方式与DOM不一样,绑定监听函数的方法是:attachEvent( "eventType","handler"),其中evetType为事件的类型,如onclick,注意要加’on’。解除事件监听器的方法是 detachEvent("eventType","handler" )
与原始事件模型在当前元素上定义事件不一样,使用attachEvent()方法的状况下,事件的处理程序会在全局做用域中运行,所以 this 等于 window。
1 /*IE*/ 2 var test1 = document.getElementById("test1"); 3 test1.attachEvent("onclick",function(){ 4 alert(this === window); //true 5 }) 6 //attachEvent 方法能够为一个元素添加多个事件处理程序,后添加的先执行 7 test1.attachEvent("onclick",function(){ 8 alert("helloWord"); //先helloWord后true 9 })
*IE的事件模型已经能够解决原始模型的三个缺点,但其本身的缺点就是兼容性,只有IE系列浏览器才能够这样写。
此模型是W3C指定的标准模型,所以咱们如今使用的现代浏览器(IE6~IE8 除外),都遵循标准事件模型,在DOM2模型中事件有一个特殊的传播过程,分为三个阶段:
(1)capturing phase:事件捕获阶段。事件被从document一直向下传播到目标元素,在这过程当中依次检查通过的节点是否注册了该事件的监听函数,如有则执行。
(2)target phase:事件处理阶段。事件到达目标元素,执行目标元素的事件处理函数.
(3)bubbling phase:事件冒泡阶段。事件从目标元素上升一直到达document,一样依次检查通过的节点是否注册了该事件的监听函数,有则执行。
全部的事件都会经历事件捕获阶段(capturing phase),可是只有部分事件会经历事件冒泡(bubbling phase),例如:submit就不会被事件冒泡。
“DOM2事件模型”,定义了两个方法用来指定和删除事件处理程序:
addEventListener("eventType","handler","true|false");和 removeEventListner("eventType","handler","true!false");
其中eventType指事件类型,注意不要加‘on’前缀,与IE下不一样。第二个参数是处理函数,第三个即用来指定是否在捕获阶段进行处理,通常设为false来与IE保持一致。
1 var test1 = document.getElementById("test1"); 2 test1.addEventListener("click",function(e){ 3 console.log(e); //MouseEvent 4 console.log(this); //当前监听的元素 test1对应的标签 5 },false)
*当咱们了解了事件的三种模型以后,咱们发现他们种会存在区别。为了更好的兼容各类浏览器版本,而且保证代码能在大多数浏览器下一致地运行,咱们只须要关注冒泡阶段,而且整理一份跨浏览器的事件处理程序:
1 var EventUtil = { 2 /** 3 * 添加事件 4 * @param {Object} element 5 * @param {Object} type 6 * @param {Object} hander 7 */ 8 addHander: function(element, type, hander) { 9 if (element.addEventListener) { 10 element.addEventListener(type, hander, false); 11 } else if (element.attachEvent) { 12 element.attachEvent("on" + type, hander); 13 } else { 14 element["on" + type] = hander; 15 } 16 }, 17 /** 18 * 移除事件 19 * @param {Object} element 20 * @param {Object} type 21 * @param {Object} hander 22 */ 23 removeHander: function(element, type, hander) { 24 if (element.removeEventListener) { 25 element.removeEventListener(type, hander, false); 26 } else if (element.detachEvent) { 27 element.detachEvent("on" + type, hander); 28 } else { 29 element["on" + type] = null; 30 } 31 }, 32 /** 33 * 取得当前事件对象 34 * @param {Object} event 35 */ 36 getEvent: function(event) { 37 return event ? window.event : event; 38 }, 39 /** 40 * 取得触发事件的目标元素对象 41 * @param {Object} event 42 */ 43 getTarget: function(event) { 44 return event.target || event.srcElement; 45 }, 46 /** 47 * 阻止默认的事件触发 48 * @param {Object} event 49 */ 50 preventDefault: function(event) { 51 if (event.preventDefault) { 52 event.preventDefault(); 53 } else { 54 event.returnValue = false; 55 } 56 }, 57 /** 58 * 阻止事件冒泡 59 * @param {Object} event 60 */ 61 stopPropagation: function(event) { 62 if (event.stopPropagation) { 63 event.stopPropagation(); 64 } else { 65 event.cancelBubble = true; 66 } 67 } 68 }
前文屡次使用到事件(event)的target 属性,这里不得不提到与之相相似的 currentTarget属性,那么他们分别表明什么,而且有什么做用和区别?下面咱们先看《javascript高级程序设计》中对着两个属性的说明:
属性/方法 | 类型 | 读/写 | 说明 |
target | Element | 只读 | 事件的目标 |
currentTarget | Element | 只读 | 其事件处理程序当前正在处理事件的那个元素 |
在事件处理程序内部,对象的this始终等于currentTarget的值,而target则只包含事件的实际目标。若是将事件处理程序指定给了目标元素,则 this、currentTarget、target 包含相同的值。以下例子:
1 <div id="btnPanel" style="background: #ccc;padding: 40px;"> 2 <input type="button" name="btn" id="btn" value="肯定" /> 3 </div>
1 var btn = document.getElementById("btn"); 2 btn.onclick = function(event) { 3 alert(event.currentTarget === this); //true 4 alert(event.target === this); //true 5 }
因为 click 事件的目标是按钮,而且咱们将点击事件的处理也是指定给了按钮。所以 currentTaget 、target 、 this 值相等。
思考:若是咱们将 click 事件绑定到 按钮父容器上,那么咱们在点击按钮,因为事件冒泡 触发父容器 click事件时。 这三者有什么变化?修改上面js部分代码以下:
1 var btnPanel = document.getElementById("btnPanel"); 2 btnPanel.onclick = function(event) { 3 alert(event.currentTarget === btnPanel); //true 4 alert(this === btnPanel); //true 5 alert(event.target === document.getElementById("btn")); //true 6 }
当咱们点击这个例子中的按钮时,this和currentTarget 都等于 这个div,由于事件处理程序是注册到这个div上的 。 最终 target 等于按钮元素,是由于 咱们在点击按钮时,因为按钮上没有注册 click 事件处理程序, 结构 click 事件就冒到了 父容器div 并触发了click。这个按钮 是 click 事件的真正目标。
*总结:经过如上两个例子,咱们能够这样理解:
1.target 永远 等于 事件的真正目标 (如click事件中,target永远等于咱们鼠标点击的最上层元素);
2.currentTaget 始终等于this。等于 事件处理程序绑定的这个元素。(如: 谁.onclick = hander, “谁”就是currentTaget,也就是最终要冒泡到“谁”上触发事件的元素);
stopPropagation() 方法用于当即中止事件在DOM层次中的传播,即取消进一步的事件捕获和冒泡。
1 var btn = document.getElementById("btn"); 2 btn.onclick = function(event) { 3 alert("click for btn"); 4 event.stopPropagation(); 5 } 6 7 var btnPanel = document.getElementById("btnPanel"); 8 btnPanel.onclick = function(event) { 9 alert("click for div"); 10 }
和以前例子的不一样是,咱们在btn和div上都绑定了click事件,而且咱们在btn的事件程序中调用event.stopPropagation()。当咱们点击按钮时,页面只弹出一个提示框“click for btn”,是因为咱们在进入btn的事件时,阻止了事件在dom中的传播,所以不会触发 div上绑定的点击事件。若是去掉event.stopPropagation(),则弹出两个提示对话框 先触发 btn事件,再经由事件冒泡 触发div事件。
只有cancelable 属性为true的事件,才可使用preventDefault() 来取消其默认行为。
1 <form action="http://www.baidu.com" method="post"> 2 <a id="baiduA" href="http://www.baidu.com">百度</a> 3 <input id="submitBtn" type="submit" value="提交"/> 4 </form>
咱们都知道,a标签在点击的时候 会触发默认的跳转事件,在form中点击submit类型按钮,会自动post而且跳转到action指定的页面。咱们如今要求,点击a标签或者submit按钮时,只提示 不作跳转 或者 提交。使用preventDefault();取消其默认事件的行为:
1 var subBtn = document.getElementById("submitBtn"); 2 subBtn.onclick = function(e){ 3 alert("subBtn"); 4 e.preventDefault(); 5 } 6 7 var baiduA = document.getElementById("baiduA"); 8 baiduA.onclick = function(e){ 9 alert("baiduA"); 10 e.preventDefault(); 11 }
注:在IE事件对象中,也有取消默认事件,和阻止事件冒泡的属性。
window.event.cancelBubble = true 对应 stoppropagation();
window.event.returnValue = false 对应 preventDefault();
事件常常有用户操做火经过其余浏览器的功能来触发,好比点击某些按钮,输入某些值等等,可是,在一些特定的状况下,咱们须要咱们的事件在任意时刻都能经过javascript来触发。而且某些事件仍是咱们本身定义的,这个时候咱们可使用IE下fireEvent方法,高级浏览器(chrome,firefox等)有dispatchEvent方法。
例如在ie下看看这个例子:
1 //document上绑定自定义事件ondataavailable 2 document.attachEvent('ondataavailable', function(event) { 3 alert(event.eventType); 4 }); 5 var obj = document.getElementById("obj"); 6 //obj元素上绑定click事件 7 obj.attachEvent('onclick', function(event) { 8 alert(event.eventType); 9 }); 10 //调用document对象的createEventObject方法获得一个event的对象实例。 11 var event = document.createEventObject(); 12 event.eventType = 'message'; 13 //触发document上绑定的自定义事件ondataavailable 14 document.fireEvent('ondataavailable', event); 15 //触发obj元素上绑定click事件 16 document.getElementById("test").onclick = function() { 17 obj.fireEvent('onclick', event); 18 };
再看看高级浏览器(chrome,firefox等)的例子:
1 //document上绑定自定义事件ondataavailable 2 document.addEventListener('ondataavailable', function(event) { 3 alert(event.eventType); 4 }, false); 5 var obj = document.getElementById("obj"); 6 //obj元素上绑定click事件 7 obj.addEventListener('click', function(event) { 8 alert(event.eventType); 9 }, false); 10 //调用document对象的 createEvent 方法获得一个event的对象实例。 11 var event = document.createEvent('HTMLEvents'); 12 // initEvent接受3个参数: 13 // 事件类型,是否冒泡,是否阻止浏览器的默认行为 14 event.initEvent("ondataavailable", true, true); 15 event.eventType = 'message'; 16 //触发document上绑定的自定义事件ondataavailable 17 document.dispatchEvent(event); 18 var event1 = document.createEvent('HTMLEvents'); 19 event1.initEvent("click", true, true); 20 event1.eventType = 'message'; 21 //触发obj元素上绑定click事件 22 document.getElementById("test").onclick = function() { 23 obj.dispatchEvent(event1); 24 };
在以上两个例子中,咱们都会发现,在事件触发的时候,咱们须要对触发的方法传一个事件对象进去,而且这个事件对象使咱们经过js本身建立的。
在ie 中为:document.createEventObject();
在常规浏览器中:document.createEvent('HTMLEvents');
createEvent()方法返回新建立的Event
对象,支持一个参数,表示事件类型,具体见下表:
参数 | 事件接口 | 初始化方法 |
---|---|---|
HTMLEvents | HTMLEvent | initEvent() |
MouseEvents | MouseEvent | initMouseEvent() |
UIEvents | UIEvent | initUIEvent() |
对于标准浏览器,其提供了可供元素触发的方法:element.dispatchEvent()
. 不过,在使用该方法以前,咱们还须要作其余两件事,及建立和初始化。所以,总结说来就是:
1 document.createEvent() 2 event.initEvent() 3 element.dispatchEvent()
*总结,对于事件的触发,dispatchEvent 或者 fireEvent 也只是了解了对事件触发的调用方式,本篇不做过多的介绍。应该会在以后单独起一篇,事件模拟,自定义事件的文章。
javascript事件是全部网页互动性的根本,所以在咱们编写前端交互的时候,事件绑定做为再普通不过的交互操做了。在传统的事件处理中,你根据需求给每个元素添加或者 删除事件处理器,然而事件处理器能够致使内存泄露或者性能降低(你用的越多风险越大)。例如如下例子
一:当咱们点击每一个li时,控制台打出 当前点击元素的 innerHtml
1 <ul id="ul"> 2 <li>aaaaaaaa</li> 3 <li>bbbbbbbb</li> 4 <li>cccccccc</li> 5 </ul>
传统的作法:为每一个li绑定点击事件:
1 window.onload = function(){ 2 var ul = document.getElementById("ul"); 3 var lis = ul.getElementsByTagName("li"); 4 for(var i = 0 ; i < lis.length; i ++){ 5 lis[i].onclick = function(){ 6 console.log(this.innerHTML); 7 } 8 } 9 }
这样咱们就能够作到li上面添加鼠标事件,可是若是说咱们可能有不少个li用for循环的话就比较影响性能。
实际开发状况中,咱们的li大多数是根据数据集合,动态组装生成的。此时就须要咱们每生成一个li 就给这个li 绑定一个点击事件。效率低,容易出错
思考:1.有没有一种可行的方法让我每次只需完成一次事件绑定的操做?
2.或者我在li的父容器上,加入某种事件,让我在每次点击li时,由父容器的事件判断我当前点击的哪一个li元素,并执行特定的操做?
答:事件代理。
下面咱们能够用事件代理的方式来实现这样的效果。html不变
1 window.onload = function(){ 2 var ul = document.getElementById("ul"); 3 var lis = ul.getElementsByTagName("li"); 4 5 ul.onclick = function(e){ 6 /* 7 这里要用到事件源:event 对象,事件源,无论在哪一个事件中,只要你操做的那个元素就是事件源。 8 ie:window.event.srcElement 9 标准下:event.target 10 nodeName:找到元素的标签名 11 */ 12 var e = e || window.event; 13 var target = e.target || e.srcElement; 14 if(target.nodeName.toUpperCase() == "LI"){ 15 alert(target.innerHTML); 16 } 17 } 18 }
这样,咱们就使用事件代理完成在父容器绑定点击事件,当咱们点击子元素li时,根据事件源获得当前点击的target元素。这样就算li是动态生成的,在点击的时候也会去获取到新的节点li。并执行对应操做。
原理:事件冒泡以及目标元素。当一个元素的事件被触发时,一样的事件将会在这个元素的全部祖先元素中被触发,这一过程称之为事件冒泡。这个事件从原始元素一直冒泡到dom树最上层。而且全部的事件触发的目标元素都是最开始的元素(target || srcElement)所以咱们能够根据这一原理来达到触发原始元素事件的目的。
这样作的好处:
1.将全部同类元素的事件操做代理给其父元素,减小了事件绑定过程,避免了100个li,1000个li 或者更多li的循环绑定事件,有效减小内存占用,避免元素过多致使浏览器内存泄露,提升效率。
2.在DOM更新后无须从新绑定事件处理器了。
接下来咱们利用事件代理封装一个通用的方法:
1 <!DOCTYPE html> 2 <html> 3 4 <head lang="en"> 5 <meta charset="UTF-8"> 6 <title></title> 7 <style type="text/css" rel="stylesheet"> 8 p, 9 h3 { 10 width: 200px; 11 height: 100px; 12 border: 1px solid pink; 13 margin: 20px auto; 14 } 15 </style> 16 </head> 17 18 <body> 19 <div id="pElement"> 20 <p>p的点击事件</p> 21 <h3>h3的双击事件</h3> 22 </div> 23 </body> 24 </html>
1 var pElement = document.getElementById('pElement'); 2 //事件代理整理 3 EventTarget.prototype.on = function(eventType, selector, callback) { 4 var _event = function(e) { 5 var target = event.target || event.srcElement; 6 if (target === selector) { 7 callback(e); 8 } 9 } 10 if (this.addEventListener) { 11 this.addEventListener(eventType, _event, false); 12 } else if (this.attachEvent) { 13 this.attachEvent("on" + eventType, _event); 14 } else { 15 this["on" + eventType] = _event; 16 } 17 } 18 19 //可扩展的选择器 添加id class 属性等筛选 20 function _getTarget(targetName) { 21 return document.getElementsByTagName(targetName)[0]; 22 } 23 24 //给p元素绑定点击事件 25 pElement.on('click', _getTarget("p"), function(e) { 26 alert(e.target.innerText) 27 }); 28 29 //给h3 绑定双击事件 30 pElement.on('dblclick', _getTarget("h3"), function(e) { 31 alert(e.target.innerText) 32 });