概要:本文主要记录js事件模型相关的知识,主要讲解事件冒泡事件捕获,以及阻止冒泡,事件相关的方法。还有因为事件冒泡引发的鼠标事件之间的区别
js事件模型发展历史
原始事件模型(DOM0级的事件模型)
兼容所有浏览器,不存在事件流概念。事件发生立马处理。特点就是:没有办法为同一个节点绑定多个事件,后绑定的事件会覆盖前面的事件。事件发生的阶段依然是在冒泡的阶段发生的。
- 实现方式1:直接在dom元素上绑定事件 如:onclick=""
- 实现方式2: 通过js的方式绑定事件 dom.onclick=function
标准事件模型(dom2级事件模型)
标准事件模型是W3C组织制定的标准事件模型,现代浏览器(IE6~8之外)都支持,该模型将事件分为三个阶段: 在标准事件模型中(DOM2级事件中),当事件发生在节点时,目标元素的事件处理函数就被触发,而且目标的每个祖先节点也有机会处理那个事件。
-
捕获阶段:当某个事件触发时,事件会从window对象,自上而下传播,直至事件发生的目标元素,默认在这个过程中相应的事件监听函数不会触发。
-
目标阶段:当事件传播到目标元素之后,执行目标元素上,该事件的监听函数,如果没有就不执行。
-
冒泡阶段:事件再从目标元素开始逐层向上传播, 如果途中,有该事件的监听函数就执行。 所有事件都有捕获阶段,但是只有部分事件才有冒泡阶段。具体详情见: www.w3.org/TR/DOM-Leve…
标准事件模型对应的事件绑定方法
element.addEventListener(event, function, useCapture)
useCapture:
- true - 事件句柄在捕获阶段执行(即在事件捕获阶段调用处理函数)
- false- 默认值。事件句柄在冒泡阶段执行(即表示在事件冒泡的阶段调用事件处理函数)
阻止事件冒泡:
- 使用方法event.stopPropagation()
$("#div1").mousedown(function(e){
var e=event||window.event;
event.stopPropagation();
});
- 加上条件判断event.target==event.currentTarget,让触发事件的元素等于绑定事件的元素,也可以阻止事件冒泡;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="btn1">
btn1
<div id="btn2">
btn2
<div id="btn3">btn3</div>
</div>
</div>
</body>
<script>
var btn1 = document.getElementById("btn1");
var btn2 = document.getElementById("btn2");
var btn3 = document.getElementById("btn3");
btn1.onclick = function() {
if(event.target == event.currentTarget){
console.log(1)
}
}
btn2.onclick = function() {
if(event.target == event.currentTarget){
console.log(2)
}
}
btn3.onclick = function() {
if(event.target == event.currentTarget){
console.log(3)
}
}
</script>
</html>
<style>
#btn1, #btn2, #btn3 {
width: 500px;
height: 300px;
background-color: blue;
}
#btn2 {
width: 300px;
background-color: brown;
}
#btn3 {
width: 200px;
background-color: aquamarine;
}
</style>
阻止默认事件
event.preventDefault( )
或者
return false
IE事件模型
IE的事件机制没有捕获阶段,事件流是非标准的,只有目标阶段和冒泡阶段。
<button id="btn">点我</button>
<script type="text/javascript">
var target = document.getElementById("btn");
target.attachEvent('onclick',function(){
alert("我是button");
alert(this) // window
});
</script>
与之对应的也有事件的移除函数 :detachEvent()
阻止事件冒泡的方法:e = window.event;e.cancelBubble = true;
阻止默认事件发生:e = window.event; e.returnValue =false;
IE模型与标准事件模型的区别
- 由于IE不支持事件捕获,所以在注册函数中只有两个参数,类型和处理函数;
- 标准事件模型中,注册函数时,事件类型前不加on,IE中要加on;
- attachEvent注册的函数作为全局调用函数,this引用window对象;
// this指向的是当前的btn1
btn1.addEventListener('click', function(){console.log(this)})
- 标准事件模型和IE事件模型都允许对同一元素,针对同一事件类型注册多个处理函数。但在标准事件模型中若注册同一函数,与之同名的函数都会被忽略,以第一个为准;在IE中,同一函数可以被注册多次,即发生次数等于注册次数。
由事件模型引发的概念
事件冒泡和事件捕获分别由微软和网景公司提出,这两个概念都是为了解决页面中事件流(事件发生顺序)的问题。
事件冒泡
微软提出了名为事件冒泡(event bubbling)的事件流。
事件冒泡可以形象地比喻为把一颗石头投入水中,泡泡会一直从水底冒出水面。也就是说,事件会从最内层的元素开始发生,一直向上传播,直到document对象。
事件捕获
网景提出另一种事件流名为事件捕获(event capturing)。与事件冒泡相反,事件会从最外层开始发生,直到最具体的元素。
事件代理
把原本绑定在子元素的事件委托给父元素,让父元素担任监听的任务。
// 点击父元素,监听父元素事件,当前点击的元素是不是包含了传递过来子元素的标签,包含的话就触发事件执行相关的函数。
// 通用的事件函数绑定 元素节点 绑定事件的类型 需要代理的元素 事件的处理
function bindEvent(elem, type, selector, fn) {
if (!fn) {
fn = selector;
selector = null;
}
elem.addEventListener(type, e => {
const target = e.target; // 当前点击的元素
if (selector) {
// 当前点击元素是不是包含了需要代理的元素标签
// 代理绑定
if(target.matches(selector)) {
fn.call(target, e);
}
} else {
// 触发自己
fn.call(target, e);
}
});
}
事件代理发生的时机
对于事件代理来说,在事件捕获或者事件冒泡阶段处理,并没有明显的优劣之分,但是由于事件冒泡的事件流模型,被所有主流的浏览器兼容,从兼容性角度来说还是建议大家使用事件冒泡模型
鼠标四个事件的区别 mouseover/mouseenter mouseleave/mouseout
- mouseenter/mouseover 鼠标进入事件
- mouseleave/mouseout 鼠标离开事件
- mouseover mouseleave 会发生冒泡,当进入或者离开子元素也会触发事件
- mouseenter mouseleave 只触发当前元素上的事件 如下例子可以帮助理解原理
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div class="inline">
<div class="box out" onmouseout="out()">
<span>out</span>
</div>
<div class="box leave" onmouseleave="leave()">
<span>leave</span>
</div>
</div>
<div class="inline">
<div class="box over" onmouseover="over()">
<span>over</span>
</div>
<div class="box enter" onmouseenter="enter()">
<span>enter</span>
</div>
</div>
</body>
</html>
<script>
// mouseover事件
function over() {
console.log('触发了mouseover事件!');
}
// mouseenter事件
function enter() {
console.log('触发了mouseenter事件!');
}
// mouseout事件
function out() {
console.log('触发了mouseout事件!');
}
// mouseleave事件
function leave() {
console.log('触发了mouseleave事件!');
}
</script>
<!-- mounseenter只有在事件绑定的元素上触发,mouseover在子元素上也会触发 -->
<!-- mouuseleave只在元素上触发,mouseout在子元素上也会触发 -->
<style>
.box {
width: 200px;
height: 200px;
margin-bottom: 10px;
background-color: aquamarine;
}
.over span,.enter span, .out span, .leave span {
width: 50px;
height: 50px;
background-color: brown;
}
.enter span, .leave span {
background-color: blue;
}
.inline {
display: inline-block;
}
</style>