Appearance
DOM和DOM事件
内容概述
这一讲ECMAScript的内容先暂时告一段落,开始讲DOM API和DOM 事件
教学目的
- 理解DOM是什么
- 理解document对象,掌握其常用属性和方法
- 理解html文档结构,节点和元素节点
- 掌握获取DOM元素、新增DOM元素、删除DOM元素、元素属性操作、元素内容操作、元素类名操作和元素样式操作
- 理解事件,掌握编写事件处理程序
- 了解事件类型和事件对象
- 理解事件流,掌握addEventListener使用、阻止时间冒泡和事件委托
具体内容
- DOM:document对象、DOM节点、获取DOM元素、新增DOM元素、删除DOM元素、元素属性操作、元素内容操作、元素类名操作和元素样式操作
- DOM事件:事件处理、事件类型、事件对象
- 事件流:捕获阶段和冒泡阶段、阻止事件冒泡、事件委托
DOM
**DOM(Document Object Model,文档对象模型)**是一个与语言无关的接口,它允许程序和脚本动态地访问和更新文档的内容、结构和样式。在Web开发中,DOM通常指的是HTML或XML文档的编程接口。通过这个接口,JavaScript可以获取(访问)和操作网页上的内容、结构和样式。
DOM将文档看作是一个由节点(Node)构成的层次结构,这些节点包括不同类型的对象,如元素节点(Element Node,如<div>、<p>等HTML标签)、属性节点(Attribute Node)、文本节点(Text Node,包含文本内容的节点)等。每个节点都是DOM中的一个对象,可以通过JavaScript进行访问和操作。
DOM的主要用途
- 动态内容更新:可以在不重新加载整个页面的情况下,修改网页的内容。
- 创建新的内容:可以动态地创建新的HTML元素,并添加到DOM树中。
- 导航DOM树:可以遍历DOM树,以访问和操作任何元素。
- 事件处理:可以添加事件监听器到DOM元素上,以响应用户的操作(如点击、键盘输入等)。
document对象
在JavaScript中,document对象是一个非常重要的全局对象,它代表了整个HTML文档。这个对象提供了大量用于操作文档内容、结构和样式的属性和方法。由于它是全局的,你不需要显式地创建它,只需要在脚本中直接使用它即可。
document对象的主要用途
- 访问和操作DOM:通过
document对象,你可以访问和操作DOM(文档对象模型)中的任何元素。这包括读取和修改元素的属性、样式和内容,以及添加、删除或移动元素。 - 处理事件:
document对象也用于为整个文档或文档中的特定元素添加事件监听器。这样,你就可以在用户与页面交互时执行JavaScript代码,比如点击按钮、提交表单或滚动页面等。 - 创建新的DOM元素:使用
document.createElement()方法,你可以创建新的HTML元素,并将它们添加到DOM树中。 - 加载和获取资源:虽然不是
document对象的主要职责,但它也提供了加载和获取外部资源(如图片、样式表或脚本)的能力,尽管这些功能通常由<img>,<link>, 和<script>等HTML元素以及XMLHttpRequest或fetchAPI等更现代的技术来处理。
document对象的常用属性和方法
- 属性:
documentElement:指向文档的根元素(通常是<html>元素)。body:指向文档的<body>元素。head:指向文档的<head>元素。title:获取或设置文档的标题(<title>元素的内容)。URL:获取文档的URL。
- 方法:
getElementById(id):通过元素的ID获取元素。getElementsByClassName(classNames):通过元素的类名获取一个元素集合。getElementsByTagName(tagName):通过元素的标签名获取一个元素集合。querySelector(selector):通过CSS选择器获取文档中与指定选择器或选择器组匹配的第一个Element。querySelectorAll(selector):通过CSS选择器获取文档中与指定选择器或选择器组匹配的所有Element的静态(非实时)NodeList。createElement(tagName):创建新的元素。createTextNode(text):创建包含指定文本的新文本节点。addEventListener(event, function, useCapture):为指定元素添加事件监听器。
DOM节点
根据 W3C 的 HTML DOM 标准,HTML 文档中的所有内容都是节点:
- 整个文档是一个文档节点
- 每个 HTML 元素是元素节点
- HTML 元素内的文本是文本节点
- 每个 HTML 属性是属性节点
- 注释是注释节点
HTML DOM 将 HTML 文档视作树结构。这种结构被称为节点树:
html
<html>
<head>
<title>My Title</title>
</head>
<body>
<a href="www.baidu.com">My Link</a>
<h1>My header</h1>
</body>
</html>
节点关系
节点树中的节点彼此拥有层级关系。
我们常用父(parent)、**子(child)和同胞(sibling)**等术语来描述这些关系。父节点拥有子节点。同级的子节点被称为同胞。
- 在节点树中,顶端节点被称为根(root)。
- 每个节点都有父节点、除了根(它没有父节点)。
- 一个节点可拥有任意数量的子节点。
- 同胞是拥有相同父节点的节点。
html
<html>
<head>
<meta charset="utf-8">
<title>DOM 教程</title>
</head>
<body>
<h1>DOM 课程1</h1>
<p>Hello world!</p>
</body>
</html>从上面的 HTML 中:
- html 节点没有父节点;它是根节点
- head 和body的父节点是html节点
- 文本节点 "Hello world!" 的父节点是p节点
并且:
html节点拥有两个子节点:head和body
head节点拥有两个子节点:meta与title节点
title节点也拥有一个子节点:文本节点 "DOM 教程"
h1和p节点是同胞节点,同时也是body的子节点
并且:
head元素是html元素的首个子节点
body元素是html元素的最后一个子节点
h1元素是body元素的首个子节点
p元素是body元素的最后一个子节点
节点属性
- nodeType
nodeType 属性返回节点的类型。nodeType 是只读的。
比较重要的节点类型有:
| 元素类型 | NodeType |
|---|---|
| 元素 | 1 |
| 属性 | 2 |
| 文本 | 3 |
| 注释 | 8 |
| 文档 | 9 |
nodeName
nodeName 属性规定节点的名称。
- nodeName 是只读的
- 元素节点的 nodeName 与标签名相同
- 属性节点的 nodeName 与属性名相同
- 文本节点的 nodeName 始终是 #text
- 文档节点的 nodeName 始终是 #document
nodeValue
nodeValue 属性规定节点的值。
- 元素节点的 nodeValue 是 undefined 或 null
- 文本节点的 nodeValue 是文本本身
- 属性节点的 nodeValue 是属性值
获取DOM元素
- document.getElementById通过id获取指定节点
由于id不可以重复, 所以找到了就会返回一个DOM元素节点, 找不到就返回null
javascript
document.getElementById('box')- document.getElementsByClassName通过class名称获取
由于class可以重复, 所以该方法会返回一个类数组
javascript
document.getElementsByClassName('class1')- document.getElementsByTagName通过标签名称获取
由于标签名称可以重复, 所以该方法会返回一个类数组
javascript
document.getElementsByTagName('div')- document.querySelector通过指定选择器找到的第一个元素
javascript
document.querySelector('ul>li')- document.querySelectorAll通过指定选择器找到的所有元素
javascript
document.querySelectorAll('ul>li')- 获取指定元素所有的子节点children和childNodes
javascript
const box = document.getElementById('box');
console.log(box.childNodes);
console.log(box.children);children和childNodes区别:children返回所有元素节点,childNodes返回所有类型节点
- 获取指定元素中的第一个子节点firstChild和firstElementChild
javascript
const box = document.getElementById('box')
console.log(box.firstChild);
console.log(box.firstElementChild);firstChild和firstElementChild区别:firstChild返回第一个节点,firstElementChild返回第一个元素节点
- 获取指定元素中的最后一个子节点lastChild和lastElementChild
javascript
const box = document.getElementById('box')
console.log(box.lastChild);
console.log(box.lastElementChild);- 通过子元素获取父元素/父节点parentElement和parentNode
javascript
const box = document.getElementById('box')
console.log(box.parentElement);
console.log(box.parentNode);parentNode 与 parentElement 的主要区别在元素是 html 时才表现出来
console.log(document.documentElement.parentElement); // null
console.log(document.documentElement.parentNode); // document- 获取相邻上一个节点previousSibling和previousElementSibling
javascript
const box = document.getElementById('box')
console.log(box.previousElementSibling);
console.log(box.previousSibling);previousSibling获取的是节点,previousElementSibling获取的是元素节点
- 获取相邻下一个节点,nextSibling和nextElementSibling
javascript
const box = document.getElementById('box')
console.log(box.nextSibling);
console.log(box.nextElementSibling);nextSibling获取的是节点,nextElementSibling获取的是元素节点
新增DOM元素
- 创建元素节点document.createElement
javascript
let div = document.createElement("div") // 创建一个div元素- 添加节点appendChild
appendChild方法会将指定的元素添加到最后
javascript
const box = document.getElementById('box')
let div = document.createElement("div") // 创建一个div元素
box.appendChild(div)- 插入节点insertBefore
指定一个节点,插入到它之前
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<ul id="box">
<li id="child1">111</li>
</ul>
<script>
const box = document.getElementById('box')
const child1 = document.getElementById('child1')
const li = document.createElement('li')
li.innerText = '000'
box.insertBefore(li, child1)
</script>
</body>
</html>删除DOM元素
removeChild
通过父元素删除指定子元素
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<ul id="box">
<li id="child1">111</li>
<li id="child2">222</li>
</ul>
<script>
const box = document.getElementById('box')
const child1 = document.getElementById('child1')
box.removeChild(child1)
</script>
</body>
</html>复制DOM元素
cloneNode
cloneNode方法默认不会克隆子节点, 如果想克隆子节点需要传递一个true
javascript
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="box">
<p>这是一个段落</p>
</div>
<script>
const box = document.getElementById('box')
const p = document.querySelector('div>p')
const p2 = p.cloneNode(true)
box.append(p2)
</script>
</body>
</html>元素属性操作
- 获取元素属性getAttribute
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="box" class="class1">
</div>
<script>
const box = document.getElementById('box')
console.log(box.className) // class在JavaScript里是关键字
console.log(box.id)
console.log(box.getAttribute('class'));
console.log(box.getAttribute('id'));
</script>
</body>
</html>- 修改/设置元素属性setAttribute
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="box" class="class1">
dffdsfds
</div>
<script>
const box = document.getElementById('box')
box.setAttribute('title', 'box')
box.title = 'box111'
</script>
</body>
</html>- 删除元素属性removeAttribute
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="box" class="class1" title="box">
dffdsfds
</div>
<script>
const box = document.getElementById('box')
box.removeAttribute('title')
</script>
</body>
</html>元素内容操作
innerText、textContent和innerHTML
- 获取元素内容
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="box" class="class1" title="box">
<p>这是一个段落</p>
<p>这是一个段落</p>
</div>
<script>
const box = document.getElementById('box')
console.log(box.innerText);
console.log(box.textContent);
console.log(box.innerHTML);
</script>
</body>
</html>- 设置元素内容
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="box" class="class1" title="box">
<p>这是一个段落</p>
<p>这是一个段落</p>
</div>
<script>
const box = document.getElementById('box')
box.innerText = '<span>span的内容</span>'
box.textContent = '<span>span的内容</span>'
box.innerHTML = '<span>span的内容</span>'
</script>
</body>
</html>元素类名操作
在JavaScript中,处理HTML元素的类名时,className和classList是两个常用的属性,但它们各自有不同的用途和特性。
className
className属性是一个字符串,表示元素的类名。如果你想要获取或设置元素的类名,可以直接使用这个属性。但是,当处理多个类名时,需要手动处理字符串,这可能会变得有点复杂。
获取类名
javascript
const box = document.getElementById("box");
const className = box.className; // 获取元素的类名字符串设置类名
当设置className时,它会覆盖元素上现有的所有类名。
javascript
box.className = "newClassName"; // 设置新的类名,会覆盖之前的类名classList
classList属性是一个只读属性,但它返回的是一个实时的DOMTokenList集合,表示元素的类属性。与className不同,classList提供了更多操作类名的方法,如添加、删除、切换类等,这使得处理多个类名变得更加方便。
方法
add(class1, class2, ...):向元素添加一个或多个类名,如果已存在则忽略。remove(class1, class2, ...):从元素中删除一个或多个类名,如果不存在则忽略。toggle(class, [force]):切换元素的类名,如果指定类名存在则删除它,否则添加它。如果提供了第二个参数,并且为true,则添加类名;如果为false,则删除类名。contains(class):检查元素是否包含指定的类名,返回true或false。item(index):按集合中的索引返回类名。replace(oldClass, newClass):将元素的一个类名替换为另一个类名。
示例
javascript
const box = document.getElementById("box");
// 添加类名
box.classList.add("newClass");
// 删除类名
box.classList.remove("oldClass");
// 切换类名
box.classList.toggle("toggleClass");
// 检查是否包含类名
if (box.classList.contains("someClass")) {
console.log("Element has 'someClass' class.");
}
// 替换类名
box.classList.replace("oldClass", "newClass");元素样式操作
在JavaScript中,设置和获取元素的样式可以通过直接操作元素的style属性来实现。这个属性是一个对象,包含了元素的所有内联样式(即在元素的style属性中直接定义的样式)。
获取元素样式
要获取元素的某个样式属性的值,可以直接访问style对象的相应属性。但是,请注意,这种方式只能获取到通过内联样式(即直接在元素的style属性中定义的样式)设置的样式值。如果样式是通过CSS类或外部样式表设置的,那么直接访问style对象可能无法获取到这些值。
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
</style>
</head>
<body>
<div id="box" style="color: red">
<p>这是一个段落</p>
<p>这是一个段落</p>
</div>
<script>
const box = document.getElementById("box");
const color = box.style.color; // 获取内联样式的颜色值
console.log(color);
</script>
</body>
</html>如果需要获取通过CSS类或外部样式表设置的样式值,可以使用window.getComputedStyle()方法。这个方法接受一个元素作为参数,并返回一个对象,该对象包含了元素的所有最终样式值(包括从CSS类和外部样式表继承的样式)。
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
#box {
color: red;
}
</style>
</head>
<body>
<div id="box">
<p>这是一个段落</p>
<p>这是一个段落</p>
</div>
<script>
const box = document.getElementById("box");
const computedStyle = window.getComputedStyle(box);
const color = computedStyle.color; // 获取元素的最终颜色值
console.log(color);
</script>
</body>
</html>设置元素样式
要设置元素的样式,可以直接修改style对象的属性。这会将新的样式值以内联样式的方式应用到元素上。
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
</style>
</head>
<body>
<div id="box">
fdsfsdf
</div>
<script>
const box = document.getElementById("box");
box.style.color = "red"; // 将元素的文字颜色设置为红色
box.style.fontSize = "16px"; // 将元素的字体大小设置为16像素
</script>
</body>
</html>注意,在设置样式时,CSS属性名通常使用驼峰命名法(例如,backgroundColor而不是background-color)。
DOM事件
在JavaScript中,DOM(文档对象模型)事件是用户或浏览器自身执行的某些动作时触发的。这些事件允许我们编写响应这些动作的代码。DOM事件可以是用户触发的(如点击、键盘输入等),也可以是浏览器触发的(如页面加载完成、窗口大小改变等)。
事件处理
处理DOM事件通常涉及以下几个步骤:
- 指定事件类型:确定你想要监听的事件类型,如
click、mouseover、keydown等。 - 选择事件目标:选择你想要监听事件的DOM元素。这通常通过
document.getElementById()、document.querySelector()等方法完成。 - 注册事件监听器:在选定的元素上注册一个事件监听器,当事件发生时执行特定的函数(称为事件处理程序或事件监听函数)。
监听点击事件
html
<button id="myButton">点击我</button>
<script>
// 获取按钮元素
const button = document.getElementById("myButton");
// 注册点击事件监听器
button.addEventListener("click", function() {
alert("按钮被点击了!");
});
</script>监听页面加载完成
html
<script>
// 当文档加载完成时执行
document.addEventListener("DOMContentLoaded", function() {
console.log("页面加载完成!");
});
</script>事件类型
- 点击事件(Click Events)
click: 当用户点击某个元素时触发。dblclick: 当用户双击某个元素时触发。
- 鼠标事件(Mouse Events)
mousedown: 当鼠标按钮被按下时触发。mouseup: 当鼠标按钮被释放时触发。mousemove: 当鼠标指针在元素内部移动时触发。mouseover: 当鼠标指针移动到元素上方时触发。mouseout: 当鼠标指针移出元素时触发。mouseenter: 当鼠标指针进入元素时触发,不冒泡。mouseleave: 当鼠标指针离开元素时触发,不冒泡。
- 键盘事件(Keyboard Events)
keydown: 当用户按下键盘上的任意键时触发。keypress: 当用户按下键盘上的字符键时触发(注意:某些浏览器已弃用此事件)。keyup: 当用户释放键盘上的键时触发。
- 表单事件(Form Events)
submit: 当表单提交时触发。change: 当表单元素的值发生变化且失去焦点时触发(对于<select>、<input>等)。input: 当<input>、<select>或<textarea>元素的值发生变化时触发。
- UI事件(UI Events)
scroll: 当元素滚动条滚动时触发。resize: 当窗口或框架被调整大小时触发。
- 焦点事件(Focus Events)
focus: 当元素获得焦点时触发。blur: 当元素失去焦点时触发。
- 拖放事件(Drag and Drop Events)
dragstart: 当拖拽操作开始时触发。drag: 当元素或选中的文本被拖动时触发。dragenter: 当被拖动的元素或文本进入目标元素时触发。dragover: 当被拖动的元素或文本在目标元素上方移动时触发。dragleave: 当被拖动的元素或文本离开目标元素时触发。drop: 当被拖动的元素或文本在目标元素上释放时触发。dragend: 当拖拽操作结束时触发。
- 触摸事件(Touch Events)
touchstart: 当用户的手指触摸屏幕时触发。touchmove: 当用户的手指在屏幕上移动时触发。touchend: 当用户的手指从屏幕上移开时触发。touchcancel: 当系统取消触摸事件时触发(如电话来电)。
- 其他事件
load: 当页面或资源加载完成时触发。unload: 当用户离开页面时触发(注意:现代浏览器对unload事件的限制较多)。error: 当资源加载失败时触发(如图片加载失败)。
事件对象
在JavaScript中,DOM事件对象是浏览器在事件触发时自动创建的一个对象,它封装了事件相关的各种信息,如事件的类型、触发事件的元素、鼠标的位置等。这个对象作为事件处理函数的参数传递给开发者,以便在函数中获取这些信息并据此执行相应的操作。
事件对象的获取
在事件处理函数中,事件对象通常是作为第一个参数传递的,可以使用任何合法的变量名来接收它,但通常习惯使用event、e或evt等名称。例如:
javascript
element.onclick = function(event) {
// 在这里可以使用 event 对象
};
element.addEventListener('click', function(e) {
// 或者使用 e 作为事件对象的别名
});事件对象的常用属性和方法
不同的事件类型可能会包含不同的信息,但事件对象通常包含以下一些共有的属性和方法:
常用属性
- type:事件的类型,如
'click'、'mouseover'等。 - target:触发事件的元素。与
event.currentTarget不同,target是实际触发事件的元素,而currentTarget是绑定事件处理函数的元素。 - currentTarget:绑定事件处理函数的元素。在事件捕获或冒泡阶段,这个属性总是指向添加事件监听器的那个元素。
- bubbles:一个布尔值,表示事件是否冒泡。对于冒泡事件,这个属性为
true;对于不冒泡的事件(如focus、blur等),这个属性为false。 - cancelable:一个布尔值,表示事件是否可以取消。如果事件可以取消,则这个属性为
true;否则为false。 - eventPhase:表示事件目前所处的事件流阶段。
0表示事件没有发生,1表示捕获阶段,2表示目标阶段,3表示冒泡阶段。 - timeStamp:事件触发的时间戳,表示从1970年1月1日至今的毫秒数。
- clientX/clientY:鼠标事件触发时,鼠标指针相对于浏览器窗口视口(viewport)左上角的水平/垂直坐标。
常用方法
- preventDefault():取消事件的默认行为。如果事件的
cancelable属性为true,则可以使用这个方法。 - stopPropagation():阻止事件的进一步传播(即阻止事件冒泡或捕获)。
- stopImmediatePropagation():不仅阻止事件的进一步传播,而且阻止同一事件的其他监听函数被调用。
事件流
事件发生时会在元素节点之间按照特定的顺序传播,这个传播过程即DOM事件流。
DOM事件流分为三个阶段,分别为:
捕获阶段:事件从Document节点自上而下向目标节点传播的阶段;
目标阶段:真正的目标节点正在处理事件的阶段;
冒泡阶段:事件从目标节点自上而下向Document节点传播的阶段。

addEventListener
javascript
element.addEventListener(event, function, useCapture);- element: 要添加监听器的DOM元素。
- event: 要监听的事件名称,如
"click","mouseover","keydown"等(不包括"on"前缀)。 - function: 当事件发生时,要调用的函数。
- useCapture (可选): 一个布尔值,指定事件是否在捕获或冒泡阶段执行。默认为
false,即在冒泡阶段执行。
注意事项
- 事件监听器可以添加多个:同一个元素可以为同一个事件添加多个监听器,这些监听器会按照它们被添加的顺序依次触发。
- 移除事件监听器:如果需要,可以使用
removeEventListener方法来移除之前添加的事件监听器。注意,移除时需要提供与添加时完全相同的函数引用。 - 内存泄漏:如果添加事件监听器的元素被删除或不再需要,但监听器函数仍然保留在内存中,这可能导致内存泄漏。确保在适当的时候移除不再需要的事件监听器。
捕获阶段
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
.father {
overflow: hidden;
width: 300px;
height: 300px;
margin: 100px auto;
background-color: pink;
text-align: center;
}
.son {
width: 200px;
height: 200px;
margin: 50px;
background-color: purple;
line-height: 200px;
color: #fff;
}
</style>
</head>
<body>
<div class="father">
<div class="son">son盒子</div>
</div>
<script>
const son = document.querySelector('.son');
son.addEventListener('click', function() {
console.log('son');
}, true);
const father = document.querySelector('.father');
father.addEventListener('click', function() {
console.log('father');
}, true);
document.addEventListener('click', function() {
console.log('document');
}, true);
</script>
</body>
</html>控制台输出结果是 document、father、son,可以看出捕获阶段 事件是从Document节点自上而下向目标节点传播的。
冒泡阶段
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
.father {
overflow: hidden;
width: 300px;
height: 300px;
margin: 100px auto;
background-color: pink;
text-align: center;
}
.son {
width: 200px;
height: 200px;
margin: 50px;
background-color: purple;
line-height: 200px;
color: #fff;
}
</style>
</head>
<body>
<div class="father">
<div class="son">son盒子</div>
</div>
<script>
const son = document.querySelector('.son');
son.addEventListener('click', function() {
console.log('son');
}, false);
const father = document.querySelector('.father');
father.addEventListener('click', function() {
console.log('father');
}, false);
document.addEventListener('click', function() {
console.log('document');
}, false);
</script>
</body>
</html>控制台输出结果为:son、father、document,可以看出冒泡阶段 事件是从目标节点自上而下向Document节点传播的。
注意:
onclick和attachEvent(ie)只能得到冒泡阶段
addEventListener(type, listener[, useCapture]) 第三个参数如果是true,表示在事件捕获阶段调用事件处理程序;如果是false(不写默认是false),表示在事件冒泡阶段调用事件处理程序
实际开发中,我们很少使用事件捕获,我们更关注事件冒泡
有些事件是没有冒泡的,比如onblur、onfocus、onmouseenter、onmouseleave
事件的冒泡有时会带来麻烦,不过是可以被阻止的,方法是:stopPropagation()
阻止事件冒泡
阻止事件冒泡是一种常见需求,特别是当你处理嵌套元素上的事件时。事件冒泡(Event Bubbling)是当事件在一个元素上被触发时,这个事件会逐级向上传播至其所有父元素,直到它被文档对象(document)捕获的过程。
stopPropagation()
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
.father {
overflow: hidden;
width: 300px;
height: 300px;
margin: 100px auto;
background-color: pink;
text-align: center;
}
.son {
width: 200px;
height: 200px;
margin: 50px;
background-color: purple;
line-height: 200px;
color: #fff;
}
</style>
</head>
<body>
<div class="father">
<div class="son">son盒子</div>
</div>
<script>
const son = document.querySelector('.son');
const father = document.querySelector('.father');
son.onclick = function (event) {
console.log('son');
event.stopPropagation()
}
father.onclick = function () {
console.log('father');
}
document.onclick = function () {
console.log('document');
}
</script>
</body>
</html>事件委托
JavaScript 中的事件委托(Event Delegation)是一种有效的事件处理策略,它利用事件冒泡(Event Bubbling)机制来减少事件处理器(event handler)的数量,特别适用于动态内容或者大量相似元素的场景。
基本原理
在事件委托中,我们不直接为目标元素(如列表中的每个项目)添加事件监听器,而是将事件监听器添加到它们的父元素或祖先元素上。当事件发生时,由于事件冒泡机制,事件会从最具体的元素(即事件目标)开始,然后逐级向上传播到DOM树中。通过检查事件对象(event)的target属性,我们可以确定实际触发事件的元素,并据此执行相应的操作。
优点
- 性能提升:由于减少了事件监听器的数量,特别是当处理大量相似元素时,性能会得到显著提升。
- 动态内容管理:对于动态添加到DOM中的元素,无需额外添加事件监听器,因为父元素上的事件监听器已经涵盖了这些新元素。
示例
假设我们有一个列表(<ul>),里面包含多个列表项(<li>),我们想要在每个列表项被点击时执行一些操作。
html
<ul id="myList">
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<!-- 列表项可能会动态添加 -->
</ul>使用事件委托的JavaScript代码如下:
javascript
document.getElementById('myList').addEventListener('click', function(event) {
// 检查事件目标是否为列表项
if (event.target && event.target.nodeName === 'LI') {
// 执行针对列表项的操作
console.log('Clicked:', event.target.textContent);
}
});在这个例子中,我们只在<ul>元素上添加了一个事件监听器,而不是在每个<li>元素上分别添加。当点击任何一个<li>元素时,事件会冒泡到<ul>元素,我们检查事件的target属性来确定是哪个<li>元素被点击,并据此执行相应的操作。
作业
在第三讲作业的基础上,增加添加文章和删除文章功能。

初始状态下可以显示几篇文章也可以不显示
上面表单样式和删除按钮样式随意,旨在完成基本功能
图片链接可以写死