专业编程基础技术教程

网站首页 > 基础教程 正文

JavaScript事件模型

ccvgpt 2024-08-28 13:45:42 基础教程 32 ℃

事件模型是JS里十分重要的一个部分,这两天查询了一些相关资料,用自己的文字记录一下。本文不讨论IE。

事件模型

JavaScript事件模型

DOM事件模型

最简单且兼容所有浏览器的事件模型,有两种方式。默认发生在冒泡阶段。

  • HTML中直接绑定(不推荐)
<button id="button" onclick="click()">ClickMe</button>
  • JS指定属性值
var button = document.getElementById("button")
button.onclick = function() {
 //...
}

DOM 2级模型

属于W3C标准模型,先事件捕获,到达目标后再进行冒泡。兼容现代浏览器。

//DOM 2级事件第三个参数是一个布尔值(默认为false),true表示捕获阶段调用事件处理程序,false表示冒泡阶段调用事件处理程序。
var button = document.getElementById("button")
button.addEventListener('click', function() {
 //...
}, false)

DOM 0级和DOM 2级事件模型比较

  • 移除监听函数
button.onclick = null
button.removeEventListener('click', click, false)
//注意,addEventListener()添加的匿名函数无法移除
  • 同时绑定多个监听器
button.onclick = function() {
 console.log(1)
}
button.onclick = function() {
 console.log(2)
}
button.addEventListener('click', function() {
 console.log(3)
}, false)
button.addEventListener('click', function() {
 console.log(4)
}, false)
// 2
 3
 4

可见DOM0 级后绑定的函数会把前边的替换掉,而DOM 2级可以同时绑定多个监听器,因此推荐使用addEventListener方法监听事件。

  • 监听函数内部的this都指向触发事件的那个元素节点。
<button id="button" onclick="console.log(this.id)"></button>
var button = document.getElementById("button")
button.onclick = function() {
	console.log(this.id)
}
button.addEventListener('click', function() {
 console.log(this.id)
}, false)
//输出结果均为"button"

事件流

首先我们要先了解一下事件流。事件流描述的是在页面中接受的事件顺序。看下面的例子

<!-- HTML -->
 <div id="container">
 <button id="button"></button>
 </div>
//JS
 var container = document.getElementById("container")
 var button = document.getElementById("button")
 button.onclick = function() {alert('button!')}
 container.onclick = function() {alert('container!')}

当你点击button按钮时,会发生在button按钮上的单击事件,但同时,你也单击了container元素甚至是单击了整个页面。JS事件模型是一种观察者模式(又叫发布订阅者模式),当对应的事件被触发时,监听该事件的所有监听函数都会被调用。因此,如果你单击的多个元素都绑定了相同事件,他们执行的事件顺序是怎么样的呢?

事件流有两种:

事件冒泡:事件开始时由最具体的元素(文档中嵌套层次最深的那个节点)接受,然后逐渐向上传播到较不具体的那个节点。

<button> -> <#container> -> <body> -> <html> -> document

事件捕获:与事件冒泡相反。事件最开始由不太具体的节点最早接受事件, 而最具体的节点最后接受事件。

document -> <html> -> <body> -> <#container> -> <button>

在这个例子中,事件是通过DOM 0级事件(默认发生在冒泡阶段)添加的,所以弹出button!后弹出container!。

要让事件发生在捕获阶段也很简单

//DOM 2级事件第三个参数是一个布尔值(默认为false),true表示捕获阶段调用事件处理程序,false表示冒泡阶段调用事件处理程序。
button.addEventListener('click', function(){
 alert('button!')
}, true)
container.addEventListener('click', function(){
 alert('container!')
}, true)

事件对象

触发DOM上的事件后,会产生一个事件对象event,作为参数传给监听函数。所有的事件都是这个事件对象的示例。

  • 事件对象常用属性type 被触发的事件的类型target 事件的目标currentTarget 注册这个事件监听的对象事件对象常用方法preventDefault() 取消事件的默认行为stopPropagation() 阻止事件继续传播(冒泡和捕获),不包括在当前节点上其他的事件监听函数。stopImmediatePropagation() 阻止所有事件继续传播,包括在当前节点上其他的事件监听函数。

了解更多事件对象的属性和方法 事件对象

看到这里,尝试一下实现页面内点击弹窗外部关闭弹窗,点击弹窗内部不会关闭弹窗。

<body>
	<div id="pop-up-window"></div>
</body>
<script>
	 var body = document.querySelector('body')
 var popUpWindow = document.getElementById('pop-up-window')
 
 body.addEventListener('click', function(e) {
 popUpWindow.style.display = 'none'
 }, false)
 popUpWindow.addEventListener('click', function(e) {
 e.stopPropagation() //在弹窗内部点击时阻止事件传播,因此不会触发body的click事件
 }, false)
</script>

事件委托

借助事件冒泡和事件对象,可以实现事件委托(又叫事件代理)。

先思考如何给下面的按钮都绑定一个click事件,点击后输出按钮的id

 <div id="contiainer">
 <button id="button1">button1</button>
 <button id="button2">button2</button>
 <button id="button3">button3</button>
 <!-- ... -->
 <button id="button10">button10</button>
 </div>

或许你或你以前会这么写

 for (var i = 1; i <= 10; i++) {
 document.getElementById("button" + i).addEventListener('click', function(e) {
 console.log(e.target.id)
 }, false)
 }

每个函数都是对象,都会占用内存,现在创建了10个监听事件,影响了页面性能。我们利用事件委托(又叫事件代理)可以解决这个问题。事件委托借助事件冒泡和事件对象,只需要创建一个监听器,就可以管理一个类型的所有事件。只需要在DOM树尽量最高的层次创建一个监听。

 document.getElementById('container').addEventListener('click', function(e) {
 if (e.target.tagName.toLowerCase() === 'button') console.log(e.target.id)
 }, false)

Tags:

最近发表
标签列表