js是一门事件驱动的编程语言,事件通常有多种,比如说网络事件,IO事件,定时事件,点击事件等等。 在这里先只关注下前端浏览器中的事件。
什么事件
如同字面意思,事件就代表了一件事或一个动作,浏览器中的事件有哪些
window事件 | form事件 | keyboard事件 | mouse事件 | media事件 |
---|---|---|---|---|
afterprint | onblur | onkeydown | onclick | onabort |
beforeprint | onchange | onkeypress | ondbclick | oncanplay |
onload | oncontextmenu | onkeyup | ondrag | oncanplaythrough |
onunload | onformchange | ongragend | ondurationchange | |
onbeforeunload | onforminput | ondragenter | onemptied | |
onerror | onforcus | ondragleave | onended | |
onhaschange | oninput | ondragover | onerror | |
onmessage | oninvalid | ondragstart | onloadeddata | |
onoffline | onreset | ondrop | onloadedmetadata | |
ononline | onselect | onmousedown | onloadstart | |
onpagehide | onsubmit | onmousemove | onpause | |
onpageshow | onmouseout | onplay | ||
onpopstate | onmouseover | onplaying | ||
onredo | onmouseover | onprogress | ||
onresize | onmoseup | onratechange | ||
onstorage | onmosewhell | onreadystatechange | ||
onundo | onscroll | onseeked | ||
onunload | onseeking | |||
onstalled | ||||
onsuspend | ||||
ontimeupdate | ||||
onvolumechange | ||||
onwaiting |
事件触发过程
从上面各方各面的事件上我们可以看出浏览器冲加载到退出那一刻,无时不刻不在触发事件,那么事件触发是怎样的一个流程呢?
当dom节点发生了某些操作时,就会有一个事件发射出去,这个事件从window发出,不断经过下级节点知道目标节点,这个阶段称为捕获阶段。所有经过的节点都会触发这个事件,捕获阶段的任务就是建立事件传递线路,以便后面冒泡阶段顺着该线路返回至window。监听该阶段的事件,可以通过把addEventListener第三个参数设为true
node.addEventListener('click', function() {}, true);
当事件传递到目标节点哪里,最终会在目标节点上触发这个事件,这个就是目标阶段(事件触发的目标总是最最底层的节点:如<p>hello <em>world</em></p>,目标节点可以是p也可以是em)
当事件到达目标节点之后,会沿捕获阶段的路线原路返回,这个阶段称为冒泡阶段,同样沿途所有的节点都会再次触发该事件。
监听父节点
由事件的触发机制我们可以看到,沿途所有的父节点都会被触发两次该事件,一次是捕获阶段,一次是冒泡阶段。
node.addEventListener('click', functioon() {console.log('hello')}, true) ; //捕获阶段
node.addEventListener('click', function() {console.log('world')}, false); //冒泡阶段
所以我们可以通过冒泡机制来监听父节点的事件,实现监听子节点,尤其是当子节点非常多的时候
注:由于IE浏览器不支持在捕获阶段监听事件,所有通常只用冒泡阶段来监听事件。
冒泡的烦恼
事件的冒泡机制,虽然很好,但有些场合并不使用。在比较复杂的应用中,监听比较复杂,我们可能只希望监听发生具体事件的节点,这个时候就要阻止冒泡
阻止冒泡可以使用事件对象的stopPropagation方法
node.addEventListener('click', function(e) {
//operations
e.stopPropagation();
}, false);
事件对象
在阻止冒泡时我们使用到了事件对象,那么事件对象是什么? 当一个事件被触发的时候,会创建一个事件对象,这里面包含了一些有用的属性和方法,事件对象会作为回调函数的的第一个参数(如同node回调第一个参数永远是error一样)
<body>
<div class="one">
<div class="two">
<div class="three">
</div>
</div>
</div>
</body>
node.addEventListener('click', function(e) {
console.log('事件对象', e)
}, false);
事件对象的属性和方法等(方法大部分上是对属性的get方法)
属性 | 描述 | 方法 | 描述 |
---|---|---|---|
altKey | addEventListener | 添加事件绑定 | |
bubbles | 是否在冒泡阶段触发 | removeEventListener | 删除事件绑定 |
button | stopPropagation | 停止冒泡 | |
buttons | preventDefault | 禁止浏览器默认行为 | |
cancelBubble | dispatchEvent | 手动触发事件(var event=new Event('click'); element.dispatchEvent(event) ) | |
cancelable | 是否可以调用proventDefault来禁止 | ||
clientX | |||
clientY | |||
composed | |||
ctrlKey | |||
currentTarget | |||
defaultPrevented | 是否禁止了默认行为 | ||
detail | |||
eventPhase | 当前事件触发在什么阶段。none:0;捕获:1;目标:2;冒泡:3 | ||
fromElement | |||
isTrusted | 浏览器触发(用户真实操作触发),还是 JavaScript 代码触发的 | ||
layerX | |||
layerY | |||
metaKey | |||
movementX | |||
movementY | |||
offsetX | |||
offsetY | |||
pageX | 页面的坐标 | ||
pageY | 页面的坐标 | ||
path | 事件路径 | ||
relatedTarget | |||
returnValue | |||
screenX | |||
screenY | |||
shiftKey | |||
sourceCapabilities | |||
srcElement | ie6-8 的触发事件的 dom 元素,非标准 | ||
target | 触发的目标节点 | ||
timeStamp | 返回事件发生时的时间戳 | ||
toElement | |||
type | 事件类型 | ||
view | |||
which | |||
x | |||
y |
事件代理
传统的事件绑定存在两个不足的地方:1)需要绑定很多个eventhandler 2)事件无法绑定后加入的节点。 事件的代理或委托就是事件触发过程中冒泡机制和事件对象的应用,j简单的结构如下
document.getElementById("parent-list").addEventListener("click",function(e) {
// e.target是被点击的元素!
// 如果被点击的是li元素
if(e.target && e.target.nodeName == "li") {
// 找到目标,输出ID!
console.log("find");
}
});