一、事件流
:从页面中接收事件的顺序
JS和HTML之间的交互是通过事件实现的
1. IE: 事件冒泡流
具体的元素逐级向上传播到不具体的节点
所有现代浏览器都支持事件冒泡
如:
Click me
PS: 每个载入浏览器的 HTML 文档都会成为 Document 对象。
Document 对象使我们可以从脚本中对 HTML 页面中的所有元素进行访问。
Document 对象是 Window 对象的一部分,可通过 window.document 属性对其进行访问。
2. Netscape Communicator: 事件捕获流
document----
--很少使用事件捕获
3.DOM事件流
DOM2级:
事件捕获阶段 | 处于目标阶段 | 事件冒泡阶段
document----
实际目标(div元素)在捕获阶段不会接收到事件
二、事件处理程序
1.HTML事件处理程序
- 事件处理程序中的代码在执行是有权访问全局作用域中的任何代码
“HTML标签的on-属性” - 优点:
- 简单
具有跨浏览器的优势 - 缺点:
同一个事件只能定义一个监听函数,也就是说,如果定义两次onclick属性,后一次定义会覆盖前一次。
时差问题: 用户可能会HTML元素一出现就在在页面上触发相应的时间,但当时的事件处理程序可能还没有执行条件。因此,会封装其在一个try-catch块中。
扩展事件处理程序的作用域链在不同浏览器中会导致不同结果。
HTML和JS代码紧密耦合(若要更换,要改两个地方)
2.DOM0级事件处理程序
每个元素都有自己的事件处理程序属性
var btn = document.getElementById("myBtn");
btn.onclick = function(){
alert("clicked");
alert("this.id"); // "myBtn"
}
若这些代码在页面中位于按钮后面,有可能在一段时间内怎么单击都无反应。
事件处理程序在元素的作用域中运行,this引用当前元素。
删除事件处理程序
btn.click = null;
3.DOM2级事件处理程序
指定: addEventListener()
删除:removeEventListener()
三个参数: 处理的事件名、作为事件处理程序的函数、布尔值(true-捕获,false-冒泡阶段调用事件处理程序-大多数)
var btn=document.getElementById("myBtn");
btn.addEventListener("click", function(){
alert(this.id);
}, false);
btn.addEventListener("click", function(){
alert("hello world");
}, false);
//myBtn
//hello world
移除时间是要参数相同。匿名函数无法移除。
- 如果为同一个事件多次添加同一个监听函数,该函数只会执行一次,多余的添加将自动被去除(不必使用removeEventListener方法手动去除)。
function hello() {
console.log('Hello world');
}
document.addEventListener('click', hello, false);
document.addEventListener('click', hello, false);
//执行上面代码,点击文档只会输出一行Hello world。
- 如果希望向监听函数传递参数,可以用匿名函数包装一下监听函数。
function print(x) {
console.log(x);
}
var el = document.getElementById('div1');
el.addEventListener('click', function () { print('Hello'); }, false);
//上面代码通过匿名函数,向监听函数print传递了一个参数。
4.IE事件处理程序
添加: attachEvent()
移除: detachEvent()
参数:第一个参数要加on,onclick
在 IE中使用attachEvent与使用DOM0级方法主要区别:
事件处理程序的作用域
attachEvent: 全局作用域, this===window
DOM0级:所属元素的作用域
var btn=document.getElementById("myBtn");
btn.attachEvent("click", function(){
alert(this.id);
}, false);
btn.attachEventr("click", function(){
alert("hello world");
}, false);
//hello world
//myBtn
反序触发
detachEvent: 相同参数,匿名函数不能移除
5.跨浏览器的事件处理程序
var EventUtil = {
addHandler: function(element, type, handler){
if (element.addEventListener){
element.addEventListener(type, handler, false);
} else if (element.attachEvent){
element.attachEvent("on" + type, handler);
} else {
element["on" + type] = handler;
}
},
removeHandler: function(element, type, handler){
if (element.removeEventListener){
element.removeEventListener(type, handler, false);
} else if (element.detachEvent){
element.detachEvent("on" + type, handler);
} else {
element["on" + type] = null;
}
}
}
//DOM2/IE/DOM0(现代浏览器应该不会到DOM0这一步)
三、事件对象 event
1.DOM中的事件对象
兼容DOM的浏览器会将一个event对象传入到事件处理程序中
bubbles
:是否冒泡
cancelable
:是否可取消默认行为
currentTarget
:正在处理时间的元素
detail
: 与事件相关的细节信息
eventPhase
:1捕获,2处于目标,3冒泡
preventDefault()
:取消事件默认行为(cancelable为true)
stopPropagation()
:取消事件进一步捕获或冒泡(bubbles为true)
target
:事件的目标
type
:被触发事件的类型
- 直接将事件处理程序指定给目标元素,click事件的目标是按钮
var btn = document.getElementById("myBtn");
btn.onclick = function(event){
alert(event.currentTarget === this);//true
alert(event.target === this);//true
};
currentTarget===target===this
若位于按钮的父节点,值不同
document.body.onclick = function(event){
alert(event.currentTarget === document.body); //true
alert(this === document.body); //true
alert(event.target === document.getElementById("myBtn")); //true
};
- 通过一个函数处理多个事件,用type属性
var btn = document.getElementById("myBtn");
var handler = function(event){
switch(event.type){
case "click":
alert("Clicked");
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;
2.IE中的事件对象
event对象作为window对象的一个属性存在
cancelBubble
:默认false,true时可取消事件冒泡(相当于stopPropagation())
returnValue
:false时取消默认行为(preventDefault())
srcElement
:事件目标(target)
type
:被触发事件的类型
3.跨浏览器的时间对象
var EventUtil = {
addHandler: function(element, type, handler){
if (element.addEventListener){
element.addEventListener(type, handler, false);
} else if (element.attachEvent){
element.attachEvent("on" + type, handler);
} else {
element["on" + type] = handler;
}
},
getEvent: function(event){
return event ? event : window.event;
},
getTarget: function(event){
return event.target || event.srcElement;
},
getWheelDelta: function(event){
if (event.wheelDelta){
return (client.engine.opera && client.engine.opera < 9.5 ? -event.wheelDelta : event.wheelDelta);
} else {
return -event.detail * 40;
}
},
preventDefault: function(event){
if (event.preventDefault){
event.preventDefault();
} else {
event.returnValue = false;
}
},
removeHandler: function(element, type, handler){
if (element.removeEventListener){
element.removeEventListener(type, handler, false);
} else if (element.detachEvent){
element.detachEvent("on" + type, handler);
} else {
element["on" + type] = null;
}
},
stopPropagation: function(event){
if (event.stopPropagation){
event.stopPropagation();
} else {
event.cancelBubble = true;
}
}
};
4.内存与性能
添加到页面上的事件处理程序数量直接关系到整个页面的整体运行性能:
每个函数都是对象,会占用内存,性能差
事先指定的所有事件处理程序而导致的DOM访问次数,会延迟整个页面的交互就绪事件
- 事件委托
利用事件冒泡,指定一件,管理一类事件
如:给ul里每个li用click
- Go somewhere
- Do something
- Say hi
var list = document.getElementById("myLinks");
EventUtil.addHandler(list, "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":
location.href = "http://www.wrox.com";
break;
case "sayHi":
alert("hi");
break;
}
});
})();
使用事件委托,只需在DOM树中尽量最高的层次上添加一个事件处理程序
- 移除事件处理程序
移除过时不用的事件处理程序,提高性能
removeChild(), replaceChild()方法,若带有事件处理程序的元素被innerHTML删除了,则原来可能无妨被当做垃圾回收。
只能手工移除: btn.onclick = null;
事件处理程序中,删除按钮也能阻止事件冒泡
如事先指定会用innerHTML替换页面中一部分,则可不直接将事件处理程序添加到该部分元素中,而通过指定给较高层元素,同样能处理该区域中的事件。
- 空事件处理程序另一种情况:卸载页面时,卸载前没清除干净,滞留在内存中。
只要通过onload事件处理程序添加的东西,最后要通过onunload事件处理程序将它们移除。
5.事件类型
- UI事件
load事件
unload
resize
scroll - 焦点事件
blur
DOMFocusIn
DOMFocusOut
focus
focusin
focusout - 鼠标与滚轮事件
click
dbclick
mousedown
mouseenter
mouseleave
mousemove
mouseout
mouseover
mouseup
客户区坐标位置
页面坐标位置
屏幕坐标位置
修改键
相关元素
鼠标按钮
更多时间信息(detail属性)
鼠标滚轮事件
触摸设备 - 键盘与文本事件
keydown
keypress
keyup
键码
字符编码
DOM3级变化
textInput事件
设备中的键盘事件 - 复合事件
compositionstart
compositionupdate
compositionend - 变动事件
DomSubtreeModified
DomNodeInserted
DomNodeRemoved
删除节点
插入节点 - HTML5事件
contextmenu事件
beforeunload事件
DOMContentLoaded事件
readystatechange事件
pageshow和pagehide事件
haschange事件 - 设备事件
orientationchange
MozOrientation
deviceorientation
devicemotion - 触摸与手势事件