-
Notifications
You must be signed in to change notification settings - Fork 2
Description
事件流
- 事件冒泡(常用)
- 事件捕获(少用)
- DOM事件流
| 事件冒泡 | 事件开始由最具体的元素接收,逐级向上传播到不具体的节点(文档) 元素 → html → document |
|---|---|
| 事件捕获 | 不太具体的元素更早接收事件,最具体的元素最后接收事件document →html →具体元素 |
| DOM事件流 | 事件捕获阶段 → 处于目标阶段 → 事件冒泡阶段捕获阶段不会涉及事件目标 |
事件处理程序
- HTML事件处理程序(不推荐)
- DOM0级事件处理程序
- DOM2级事件处理程序
- IE事件处理程序
- 跨浏览器的事件处理程序
事件对象
- DOM中的事件对象
- IE中的事件对象
- 跨浏览器的事件对象
事件类型
- UI事件
- 焦点事件
- 鼠标与滚轮事件
- 键盘与文本事件
- 复合事件
- 变动事件
- HTML5事件
- 设备事件
- 触摸与手势事件
内存和性能
- 事件委托
- 移除事件处理程序
模拟事件
- DOM中的是事件模拟
- IE中的事件模拟
事件处理程序
1. HTML事件处理程序(不推荐)
<a href="" onclick="showMessage()"></a> 调用
<a href="" onclick="alert('clicked')"></a> 转义缺点:
1. 时差问题。<a href="" onclick="try{showMessage();}catch(ex){}"></a>
2. 扩展事件处理程序的作用域链在不同的浏览器中会导致不同结果
3. HTML和JavaScript代码紧密耦合。
2.DOM0级事件处理程序
通过JavaScript指定事件处理程序的传统方式:将一个函数赋值给一个事件处理程序属性。
必须要获取一个操作对象的引用。
优点:
- 简单
- 跨浏览器
每个元素都有自己的事件处理程序属性(onclick),将这个属性设置为一个函数,就可以指定事件处理程序。
var btn = document.getElementById("btn");通过文档对象获取按钮的引用
btn.onclick = function(){ 指定onclick事件处理程序
alert(1);
}使用DOM0级指定的事件处理程序被认为是元素的方法
事件处理程序是在元素的作用域中运行 this引用当前元素
var btn = document.getElementById("btn");
btn.onclick = function(){
alert(this.id); //"btn"
}可以通过this访问元素的任何属性和方法
会在事件流的冒泡阶段被处理
删除事件处理程序属性的值
btn.onclick = null; //删除事件处理程序
3.DOM2级事件处理程序
指定事件处理程序:addEventListener(事件名,事件处理程序的函数,布尔值)
删除事件处理程序:removeEventListener(事件名,事件处理程序的函数,布尔值)
true:在捕获阶段调用事件处理程序
false:在冒泡阶段调用事件处理程序(默认)
通过addEventListener()添加的事件只能通过removeEventListener()来移除。
移除时传入的参数与添加处理程序时使用的参数相同。
(通过addEventListener()添加的匿名函数无法移除)
var btn = document.getElementById("btn");
btn.addEventListener("click",function(){
alert(this.id);
},false);
btn.addEventListener("click",function(){
alert(“可以添加多个事件处理程序”);
},false);//移除匿名函数没有用
btn. removeEventListener ("click",function(){
alert(this.id);
},false);
//有效
var btn = document.getElementById("btn");
var handler = function(){
alert(this.id);
}
btn.addEventListener("click",handler,false);
btn.removeEventListener("click",handler,false);优点:
1.可以添加多个事件处理程序,按照添加顺序触发。
4. IE事件处理程序
指定事件处理程序:attachEvent(事件名,事件处理程序的函数)
删除事件处理程序:detachEvent(事件名,事件处理程序的函数)
IE8及之前版本只支持事件冒泡,通过attachEvent()添加的事件处理程序都会被添加到冒泡阶段
var btn = document.getElementById("btn");
btn.attachEvent("onclick",function(){
alert(this === window); //true
})
匿名函数不能被移除区别:
1.事件处理程序会在全局作用域中运行。
2.按照相反的添加顺序触发。
优点:
1.可以添加多个事件处理程序,按照相反的添加顺序触发。
支持IE事件处理程序的浏览器:IE Opera
5. 跨浏览器的事件处理程序
1.使用隔离浏览器差异的JavaScript库
2.自己开发(使用能力检测),只需关注冒泡阶段。
第一步:创建方法addHandler(元素,事件名称,事件处理程序函数)
第二步:创建方法removeHandler(元素,事件名称,事件处理程序函数)
第三步:创建对象EventUtil
var EventUtil = {
addHandler:function(element,name,handler){
if(element.addEventListener){ //DOM2级事件处理程序
element.addEventListener(name,handler,false);
} else if(element.attachEvent){ //IE事件处理程序
element.attachEvent("on"+name,handler);
} else {
element["on"+name] = handler; //DOM0级事件处理程序
}
},
removeHandler:function(element,name,handler){
if(element.removeEventListener){ //DOM2级事件处理程序
element.removeEventListener(name,handler,false);
} else if(element.detachEvent){ //IE事件处理程序
element.detachEvent("on"+name,handler);
} else {
element["on"+name] = null; //DOM0级事件处理程序
}
}
};使用
var btn = document.getElementById("btn");
var handler = function(){
alert(this.id);
};
EventUtil.addHandler(btn,"click",handler);
EventUtil.removeHandler(btn,"click",handler);没有考虑到所有浏览器问题(IE中作用域问题)
DOM0级对每个事件只支持一个事件处理程序
事件对象
1.DOM中的事件对象
var btn = document.getElementById("btn");
btn.onclick = function(event){
alert(event.type); //"click"
}
btn.addEventListener("click",function(){
alert(event.type); //"click"
},false);<a href="" onclick="alert(event.type)"></a>
Event属性和方法:触发的事件类型不一样,属性和方法不一样
具体看附件。
bubbles
cancelable
currentTarget
defaultPrevented
detail
eventPhase
preventDefault()
stopImmediatePropagation()
stopPropagation()
target
trusted
type
view
document.body.onclick = function(event){
alert(event.currentTarget === document.body); //true
alert(this === document.body);//true
alert(event.target === document.body.getElementById("btn"));//true
}//处理多个事件
var btn = document.getElementById("btn");
var handler = function (event) {
switch (event.type){
case "click":
alert("click"); break;
case "mouseover":
event.target.style.backgroundColor = "red"; break;
case "mouseout":
event.target.style.backgroundColor = ""; break;
}
};
btn.onclick = handler;
btn.onmouseover = handler;
btn.onmouseout = handler;// preventDefault()阻止特定事件的默认行为
//只有cancelable属性为true才可以
var link = document.getElementById("link");
link.onclick = function(event){
event.preventDefault(); //阻止点击链接的默认行为
}//stopPropagation()立即停止事件在DOM层次中的传播,取消进一步的事件捕获或冒泡。
var btn = document.getElementById("btn");
btn.onclick = function (event) {
alert("clicked");
event.stopPropagation(); //click事件不会传到document.body 避免触发注册在document.body上的事件处理程序
};
document.body.onclick = function (event) {
alert("body clicked");
}//eventPhase 确定事件当前正位于事件流的哪个阶段
1- 捕获阶段
2- 处于目标
3- 冒泡阶段
var btn = document.getElementById("btn");
btn.onclick = function (event) {
alert(event.eventPhase); //2
};
document.body.addEventListener("click",function (event) {
alert(event.eventPhase); //1
},true)
document.body.onclick = function (event) {
alert(event.eventPhase); //3
}只有在事件处理程序执行期间,event对象才存在;执行完即销毁。
2.IE中的事件对象
1.在使用DOM0级方式添加事件处理程序,event对象作为window对象的一个属性存在。
2.通过attachEvent()添加,会有一个event对象作为参数传入事件处理程序函数中。
3.通过HTML特性指定的事件处理程序,通过event变量来访问
//1. 使用DOM0级方式添加事件处理程序
var btn = document.getElementById("btn");
btn.onclick = function () {
var event = window.event;
alert(event.type); //"click"
};
//2. 通过attachEvent()添加事件处理程序
var btn = document.getElementById("btn");
btn.attachEvent("onclick",function (event) {
alert(event.type); //"click"
})
//3. 通过HTML特性指定的事件处理程序
<a href="" onclick="alert(event.type)"></a>//事件处理程序的作用域是根据指定他的方式来确定的,this不会始终等于事件目标。
var btn = document.getElementById("btn");
btn.onclick = function () {
alert(window.event.srcElement === this); //true
};
btn.attachEvent("onclick",function (event) {
alert(event.srcElement === this); //false
})属性和方法
cancelBubble →DOM stopPropagation()
returnValue →DOM preventDefault()
SRCElement
type
//returnValue 取消给定事件的默认行为
var link = document.getElementById("link");
link.onclick = function(){
window.event.returnValue = false; //阻止点击链接的默认行为
}//cancelBubble立即停止事件在DOM层次中的传播,取消进一步的事件捕获或冒泡。
var btn = document.getElementById("btn");
btn.onclick = function () {
alert("clicked");
window.event.cancelBubble = true; //click事件不会传到document.body 避免触发注册在document.body上的事件处理程序
};
document.body.onclick = function () {
alert("body clicked");
}3.跨浏览器的事件对象
DOM和IE中的event对象不同,IE中event对象的全部信息和方法DOM对象中都有,实现方式不同。
var EventUtil = {
getEvent:function (event) {
return event ? event : window.event;
},
getTarget:function (event) {
return event.target || event.srcElement;
},
preventDefault:function (event) {
if(event.preventDefault){
event.preventDefault();
}else{
event.returnValue = false;
}
},
stopPropagation:function (event) {
if(event.stopPropagation){
event.stopPropagation();
}else{
event.cancelBubble = true;
}
}
};
//使用
btn.onclick = function (event) {
event = EventUtil.getEvent(event);
var target = EventUtil.getTarget(event);
}内存和性能
1.事件委托
事件委托:对事件处理程序过多问题的解决
利用事件冒泡,只指定一个事件处理程序就可以管理某一类的所有事件。
适合事件委托技术的事件:click、mousedown、mouseup、keydown、keyup、keypress
var links = document.getElementById("links");
EventUtil.addForceTouchHandler(links,"click",function (event) {
event = EventUtil.getEvent(event);
var target = EventUtil.getTarget(event);
switch (target.id){
case "doSomething":
document.title = "i changed the document's title";
break;
case "goSomewhere":
document.href = "http://www.baidu.com";
break;
case "sayHi":
alert("hi");
break;
}
});2. 移除事件处理程序
在不需要的时候移除事件处理程序
1.从文档中移除带有事件处理程序的元素时。
2.卸载页面的时候。
<div id="myDiv">
<input type="button" id="btn">
</div>
var btn = document.getElementById("btn");
btn.onclick = function () {
btn.onclick = null; //移除事件处理程序
document.getElementById("myDiv").innerHTML = "processing...";
}2.在卸载页面之前通过onunload事件处理程序移除所有事件处理程序。
事件委托优点(需要跟踪的事件处理程序越少,移除越容易)
说说JQuery中的事件
1. bind()
直接绑定在元素上
$("ul li").bind("click", function () {
alert($(this).text());
})- 这里用了隐式迭代的方法,如果匹配到的元素特别多的时候,比如如果我在ul里放了50个li元素,就得执行绑定50次。对于大量元素来说,影响到了性能。
但是如果是id选择器,因为id唯一,用bind()方法就很快捷了。 - 对于尚未存在的元素,无法绑定。(动态加载进来的元素)
随着DOM结构的复杂化和Ajax等动态脚本技术的运用,有了较多的动态添加进来的元素,直接用JQ添加click事件会发现新添加进来的元素并不能直接选取到,在这里就需要用到事件委托方法,JQ为事件委托提供了live()、dalegate()和on()方法。
事件委托: DOM在为页面中的每个元素分派事件时,相应的元素一般都在事件冒泡阶段处理事件。在类似 body > div > a 这样的结构中,如果单击a元素,click事件会从a→div→body(即document对象)。因此,发生在a上面的单击事件,div和body元素同样可以处理。而利用事件传播(这里是冒泡)这个机制,就可以实现事件委托。
具体来说,事件委托就是事件目标自身不处理事件,而是把处理任务委托给其父元素或者祖先元素,甚至根元素(document)。
2. live()
$("ul.list li").live("click",function(){ });
// jQuery 1.3缺点
-
$()函数会找到当前页面中的所有li元素并创建jQuery对象,
但在确认事件目标时却不用这个li元素集合,
而是使用选择符表达式与event.target或其祖先元素进行比较,
因而生成这个jQuery对象会造成不必要的开销; -
默认把事件绑定到$(document)元素
(如果DOM嵌套结构很深,事件冒泡通过大量祖先元素会导致性能损失) -
只能放在直接选择的元素后面,不能在连缀的DOM遍历方法后面使用,
即$(" ul.list li ").live...可以,但$( ul.list").find("li").live...不行 -
收集li元素并创建jQuery对象,但实际操作的却是$(document)对象.
解决:
- 避免生成不必要的jQuery对象
(function($){
$("#info_table td").live("click",function(){ });
})(jQuery);前提:脚本必须是在页面的head元素中链接和(或)执行的
“早委托”的hack,即在$(document).ready()方法外部调用.live()。
这时候刚好document元素可用,而整个DOM还远未生成。这个匿名函数不会等到DOM就绪就会执行。
- 为了避免事件冒泡造成的性能损失 jQuery 1.4
$("li",$ ("ul.list")[0]).live("click",function(){ });
“受托方”就从默认的$(document)变成了$("ul.list ")[0],解决了“事件传播链”过长 - 为了解决无谓生成元素集合的问题,jQuery 1.4.2干脆直接引入了一个新方法
delegate()。
3.delegate()
$("ul.list ").delegate("li","click",function(){
});使用场景:
- 为DOM中的很多元素绑定相同事件;
- 为DOM中尚不存在的元素绑定事件;
优点:
- 直接将目标元素选择符("li")、事件("click")及处理程序与“受托方”$("ul.list ")绑定,
不额外收集元素、事件传播路径缩短、语义明确; - 支持在连缀的DOM遍历方法后面调用,
$("div ").find("#list").delegate...支持精确控制
用事件委托时,如果注册到目标元素上的其他事件处理程序使用.stopPropagation()阻止了事件传播,那么事件委托就会失效。
4. on() off()
jQuery 1.7
其实是将以前的绑定事件方法作了统一
$('button').on('click',function(){});
$(div).on('click', 'button',function(){});如果指定selector,则为事件委托;否则,就是常规绑定。
//delegate源码
delegate: function( selector, types, data, fn ) {
return this.on( types, selector, data, fn );
}