事件流
事件流描述的是从页面中接收事件的顺序。
标准DOM事件流存在三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段。
事件捕获:当触发事件时,浏览器会从根节点开始由外到内进行事件传播,即点击了子元素,如果父元素也绑定了对应事件的话,会先触发父元素绑定的事件。
事件冒泡:与事件捕获恰恰相反,事件冒泡的顺序是由内到外进行事件传播,直到根节点。
无论是事件捕获还是事件冒泡,它们都有一个共同的行为,就是事件传播。
注意:IE低版本不支持事件捕获,所以就少了一个事件捕获阶段,其他浏览器则同时存在。
事件委托:由于事件冒泡的存在,可以把原本想要绑定到子元素上的事件绑定到父元素上,满足条件时触发事件。事件委托用到了event
对象的target
(触发事件的某个具体对象)属性和currentTarget
(绑定事件的对象)属性来区分父元素和子元素。
如何绑定事件
方法一:直接在标签中添加事件属性,事件前面要加on
。
缺点:HTML与JS耦合,不利于维护。还有就是可能存在元素已经渲染但事件代码还未加载,这种情况下触发事件会报错。
<!-- 直接在onclick定义代码 -->
<div onclick="alert('hello')">click Me</div>
<!-- 触发一个函数 -->
<div onclick="alertHello()">click Me</div>
<script>
function alertHello() {
alert('hello');
}
</script>
方法二:是将一个函数赋值给DOM节点的事件属性,事件前面要加on
。
特点:事件处理程序是在元素的作用域中运行,程序中的 this
引用当前元素。
<div id="div">click me</div>
<script>
var div = document.getElementById('div');
div.onclick = function () {
alert('hello');
}
//可以通过下面这行代码删除事件
//div.onclick = null;
</script>
方法三:addEventListener()
接受三个参数:要处理的事件名(不用加on
)、事件处理函数和一个布尔值(可选,默认值为false
,表示事件在冒泡阶段执行,true
表示事件在捕获阶段执行)。
缺点:IE低版本浏览器不支持。
特点:为同一事件定义多个事件处理程序,触发事件时会以添加它们的顺序执行。事件处理程序是在元素的作用域中运行,程序中的 this
引用当前元素。
<div id="div">click me</div>
<script>
var div = document.getElementById('div');
function alertHello() {
alert('hello');
}
div.addEventListener('click', alertHello, false);
// 可以通过下面这行代码删除事件
// div.removeEventListener('click', alertHello, false);
// 如果绑定的事件函数是匿名函数,如
// div.addEventListener('click', function(){}, false);
// 则无法删除事件
</script>
方法四:attachEvent()
接受两个参数:事件名(要加on
)与事件处理函数。由于 IE8 及更早版本只支持事件冒泡,所以通过添加的事件处理程序都会被添加到冒泡阶段。
缺点:只能在IE8及更早版本使用。
特点:事件处理程序会在全局作用域中运行,因此 this
等于 window
。为同一事件定义多个事件处理程序,触发事件时不会以添加它们的顺序执行,而是以相反的顺序被触发。
<div id="div">click me</div>
<script>
var div = document.getElementById('div');
function alertHello() {
alert('hello');
}
div.attachEvent("onclick", alertHello);
// 可以通过下面这行代码删除事件
// div.detachEvent('click', alertHello, false);
// 如果绑定的事件函数是匿名函数,如
// div.attachEvent('click', function(){}, false);
// 则无法删除事件
</script>
阻止事件冒泡
方法一(推荐):event.stopPropagation();
缺点:IE浏览器无法使用。
<div id="parent">
<div id="child">click me</div>
</div>
<script>
var parent = document.getElementById('parent'),
child = document.getElementById('child');
parent.addEventListener('click', function() {
alert('parent');
})
child.addEventListener('click', function(e) {
alert('child');
e.stopPropagation();
})
</script>
方法二:event.cancelBubble = true;
缺点:只能在IE浏览器使用。
<div id="parent">
<div id="child">click me</div>
</div>
<script>
var parent = document.getElementById('parent'),
child = document.getElementById('child');
parent.attachEvent('onclick', function() {
alert('parent');
})
child.attachEvent('onclick', function() {
alert('child');
window.event.cancelBubble = true;
})
</script>
方法三:event.stopImmediatePropagation()
特点:阻止事件冒泡并且阻止相同事件的其他侦听器被调用。
<div id="parent">
<div id="child">click me</div>
</div>
<script>
var parent = document.getElementById('parent'),
child = document.getElementById('child');
parent.addEventListener('click', function () {
alert('parent');
})
child.addEventListener('click', function (e) {
alert('child');
e.stopImmediatePropagation();
})
child.addEventListener('click', function (e) {
alert('hello'); //不会执行
})
</script>
取消默认事件
方法一(推荐):event.preventDefault()
缺点:IE8及以下版本浏览器不支持。
<a href="https://www.baidu.com" onclick="alert('hello');event.preventDefault();">click me</a>
方法二:event.returnValue = false;
缺点:非标准,兼容性差。
<a href="https://www.baidu.com" onclick="alert('hello');event.returnValue=false;">click me</a>
方法三:return false;
缺点:在addEventListener
注册的事件处理函数中无法阻止事件默认行为。
<a href="https://www.baidu.com" onclick="alert('hello');return false;">click me</a>
事件对象
在触发 DOM 上的某个事件时,会产生一个事件对象 event
,这个对象中包含着所有与事件有关的信息。这个 event
对象会传入到事件处理函数中。
dom.onclick = function (e) {
alert(e.type)
}
直接定义在元素上的事件中的变量 event
中保存着 event
对象。
<button onclick="alert(event.type)">click me</button>
属性 | 类型 | 读写 | 描述 |
---|---|---|---|
target | Element | 只读 | 事件的目标 |
currentTarget | Element | 只读 | 事件处理函数所在的那个元素 |
stopPropagation() | Function | 只读 | 取消事件的进一步捕获或冒泡。如果bubbles 为true ,则可以使用这个方法 |
stopImmediatePropagation() | Function | 只读 | 取消事件的进一步捕获或冒泡,同时阻止任何相同事件处理程序被调用 |
bubbles | Boolean | 只读 | 表明事件是否冒泡 |
preventDefault() | Function | 只读 | 取消事件的默认行为。如果cancelable 是true ,则可以使用这个方法 |
cancelable | Boolean | 只读 | 表明是否可以取消事件的默认行为 |
defaultPrevented | Boolean | 只读 | 为 true 表示已经调用了 preventDefault() |
type | String | 只读 | 被触发的事件的类型 |
eventPhase | Integer | 只读 | 调用事件处理程序的阶段:1 表示捕获阶段,2 表示“处于目标”,3 表示冒泡阶段 |
trusted | Boolean | 只读 | 为true 表示事件是浏览器生成的。为false 表示事件是由开发人员通过 JavaScript 创建的 |
detail | Integer | 只读 | 与事件相关的细节信息 |
view | AbstractView | 只读 | 与事件关联的抽象视图。等同于发生事件的window 对象 |
在事件处理程序内部,对象 this
始终等于 currentTarget
的值,而 target
则只包含事件的实际目标。
<div id="parent">
<div id="child">
clickme
</div>
</div>
<script>
let parent = document.getElementById("parent"),
child = document.getElementById("child");
parent.addEventListener('click', function(e){
console.log(this === parent); //true
console.log(e.target === child); //true
console.log(e.currentTarget === parent); //true
});
</script>
IE事件对象
直接定义DOM对象的事件属性,event
对象会作为 window
对象的一个属性存在。
dom.onclick = function(){
console.log(window.event.type);
}
如果事件处理程序是使用 attachEvent()
添加的,那么就会有一个 event
对象作为参数被传入事件处理程序函数中。
dom.attachEvent("onclick", function (event) {
alert(event.type);
});
直接定义在元素上的事件中的变量 event
中保存着 event
对象。
<button onclick="alert(event.type)">click me</button>
属性 | 类型 | 读写 | 描述 |
---|---|---|---|
cancelBubble | Boolean | 读/写 | 默认值为false ,但将其设置为true 就可以取消事件冒泡 |
returnValue | Boolean | 读/写 | 默认值为true ,但将其设置为false 就可以取消事件的默认行为 |
srcElement | Element | 只读 | 事件的目标 |
type | String | 只读 | 被触发的事件的类型 |
因为事件处理程序的作用域是根据指定它的方式来确定的,所以不能认为 this
会始终等于事件目标。故而,最好还是使用 event.srcElement
比较保险。例如:
<div id="parent">
clickme
</div>
<script>
var div = document.getElementById("parent");
div.onclick = function () {
alert(window.event.srcElement === this); //true
};
div.attachEvent("onclick", function (event) {
alert(event.srcElement === this); //false
alert(this === window); //true
});
</script>
由于 IE 不支持事件捕获,因而只能取消事件冒泡;但 stopPropagatioin()
可以同时取消事件捕获和冒泡。