Skip to content

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的主要用途

  1. 动态内容更新:可以在不重新加载整个页面的情况下,修改网页的内容。
  2. 创建新的内容:可以动态地创建新的HTML元素,并添加到DOM树中。
  3. 导航DOM树:可以遍历DOM树,以访问和操作任何元素。
  4. 事件处理:可以添加事件监听器到DOM元素上,以响应用户的操作(如点击、键盘输入等)。

document对象

在JavaScript中,document对象是一个非常重要的全局对象,它代表了整个HTML文档。这个对象提供了大量用于操作文档内容、结构和样式的属性和方法。由于它是全局的,你不需要显式地创建它,只需要在脚本中直接使用它即可。

document对象的主要用途

  1. 访问和操作DOM:通过document对象,你可以访问和操作DOM(文档对象模型)中的任何元素。这包括读取和修改元素的属性、样式和内容,以及添加、删除或移动元素。
  2. 处理事件document对象也用于为整个文档或文档中的特定元素添加事件监听器。这样,你就可以在用户与页面交互时执行JavaScript代码,比如点击按钮、提交表单或滚动页面等。
  3. 创建新的DOM元素:使用document.createElement()方法,你可以创建新的HTML元素,并将它们添加到DOM树中。
  4. 加载和获取资源:虽然不是document对象的主要职责,但它也提供了加载和获取外部资源(如图片、样式表或脚本)的能力,尽管这些功能通常由<img>, <link>, 和<script>等HTML元素以及XMLHttpRequestfetch API等更现代的技术来处理。

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>

DOM结构

节点关系

节点树中的节点彼此拥有层级关系。

我们常用父(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元素

  1. document.getElementById通过id获取指定节点

由于id不可以重复, 所以找到了就会返回一个DOM元素节点, 找不到就返回null

javascript
document.getElementById('box')
  1. document.getElementsByClassName通过class名称获取

由于class可以重复, 所以该方法会返回一个类数组

javascript
document.getElementsByClassName('class1')
  1. document.getElementsByTagName通过标签名称获取

由于标签名称可以重复, 所以该方法会返回一个类数组

javascript
document.getElementsByTagName('div')
  1. document.querySelector通过指定选择器找到的第一个元素
javascript
document.querySelector('ul>li')
  1. document.querySelectorAll通过指定选择器找到的所有元素
javascript
document.querySelectorAll('ul>li')
  1. 获取指定元素所有的子节点childrenchildNodes
javascript
const box = document.getElementById('box');
console.log(box.childNodes);
console.log(box.children);

childrenchildNodes区别:children返回所有元素节点,childNodes返回所有类型节点

  1. 获取指定元素中的第一个子节点firstChildfirstElementChild
javascript
const box = document.getElementById('box')
console.log(box.firstChild);
console.log(box.firstElementChild);

firstChildfirstElementChild区别:firstChild返回第一个节点,firstElementChild返回第一个元素节点

  1. 获取指定元素中的最后一个子节点lastChildlastElementChild
javascript
const box = document.getElementById('box')
console.log(box.lastChild);
console.log(box.lastElementChild);
  1. 通过子元素获取父元素/父节点parentElementparentNode
javascript
const box = document.getElementById('box')  
console.log(box.parentElement);
console.log(box.parentNode);

parentNodeparentElement 的主要区别在元素是 html 时才表现出来

  console.log(document.documentElement.parentElement); // null
  console.log(document.documentElement.parentNode); // document
  1. 获取相邻上一个节点previousSiblingpreviousElementSibling
javascript
const box = document.getElementById('box') 
console.log(box.previousElementSibling);
console.log(box.previousSibling);

previousSibling获取的是节点,previousElementSibling获取的是元素节点

  1. 获取相邻下一个节点,nextSiblingnextElementSibling
javascript
const box = document.getElementById('box') 
console.log(box.nextSibling);
console.log(box.nextElementSibling);

nextSibling获取的是节点,nextElementSibling获取的是元素节点

新增DOM元素

  1. 创建元素节点document.createElement
javascript
let div = document.createElement("div") // 创建一个div元素
  1. 添加节点appendChild

appendChild方法会将指定的元素添加到最后

javascript
const box = document.getElementById('box') 
let div = document.createElement("div") // 创建一个div元素
box.appendChild(div)
  1. 插入节点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>

元素属性操作

  1. 获取元素属性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>
  1. 修改/设置元素属性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>
  1. 删除元素属性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>

元素内容操作

innerTexttextContentinnerHTML

  1. 获取元素内容
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>
  1. 设置元素内容
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元素的类名时,classNameclassList是两个常用的属性,但它们各自有不同的用途和特性。

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):检查元素是否包含指定的类名,返回truefalse
  • 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事件通常涉及以下几个步骤:

  1. 指定事件类型:确定你想要监听的事件类型,如clickmouseoverkeydown等。
  2. 选择事件目标:选择你想要监听事件的DOM元素。这通常通过document.getElementById()document.querySelector()等方法完成。
  3. 注册事件监听器:在选定的元素上注册一个事件监听器,当事件发生时执行特定的函数(称为事件处理程序或事件监听函数)。

监听点击事件

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>

事件类型

  1. 点击事件(Click Events)
    • click: 当用户点击某个元素时触发。
    • dblclick: 当用户双击某个元素时触发。
  2. 鼠标事件(Mouse Events)
    • mousedown: 当鼠标按钮被按下时触发。
    • mouseup: 当鼠标按钮被释放时触发。
    • mousemove: 当鼠标指针在元素内部移动时触发。
    • mouseover: 当鼠标指针移动到元素上方时触发。
    • mouseout: 当鼠标指针移出元素时触发。
    • mouseenter: 当鼠标指针进入元素时触发,不冒泡。
    • mouseleave: 当鼠标指针离开元素时触发,不冒泡。
  3. 键盘事件(Keyboard Events)
    • keydown: 当用户按下键盘上的任意键时触发。
    • keypress: 当用户按下键盘上的字符键时触发(注意:某些浏览器已弃用此事件)。
    • keyup: 当用户释放键盘上的键时触发。
  4. 表单事件(Form Events)
    • submit: 当表单提交时触发。
    • change: 当表单元素的值发生变化且失去焦点时触发(对于<select><input>等)。
    • input: 当<input><select><textarea>元素的值发生变化时触发。
  5. UI事件(UI Events)
    • scroll: 当元素滚动条滚动时触发。
    • resize: 当窗口或框架被调整大小时触发。
  6. 焦点事件(Focus Events)
    • focus: 当元素获得焦点时触发。
    • blur: 当元素失去焦点时触发。
  7. 拖放事件(Drag and Drop Events)
    • dragstart: 当拖拽操作开始时触发。
    • drag: 当元素或选中的文本被拖动时触发。
    • dragenter: 当被拖动的元素或文本进入目标元素时触发。
    • dragover: 当被拖动的元素或文本在目标元素上方移动时触发。
    • dragleave: 当被拖动的元素或文本离开目标元素时触发。
    • drop: 当被拖动的元素或文本在目标元素上释放时触发。
    • dragend: 当拖拽操作结束时触发。
  8. 触摸事件(Touch Events)
    • touchstart: 当用户的手指触摸屏幕时触发。
    • touchmove: 当用户的手指在屏幕上移动时触发。
    • touchend: 当用户的手指从屏幕上移开时触发。
    • touchcancel: 当系统取消触摸事件时触发(如电话来电)。
  9. 其他事件
    • load: 当页面或资源加载完成时触发。
    • unload: 当用户离开页面时触发(注意:现代浏览器对unload事件的限制较多)。
    • error: 当资源加载失败时触发(如图片加载失败)。

事件对象

在JavaScript中,DOM事件对象是浏览器在事件触发时自动创建的一个对象,它封装了事件相关的各种信息,如事件的类型、触发事件的元素、鼠标的位置等。这个对象作为事件处理函数的参数传递给开发者,以便在函数中获取这些信息并据此执行相应的操作。

事件对象的获取

在事件处理函数中,事件对象通常是作为第一个参数传递的,可以使用任何合法的变量名来接收它,但通常习惯使用eventeevt等名称。例如:

javascript
element.onclick = function(event) {  
    // 在这里可以使用 event 对象  
};  
  
element.addEventListener('click', function(e) {  
    // 或者使用 e 作为事件对象的别名  
});

事件对象的常用属性和方法

不同的事件类型可能会包含不同的信息,但事件对象通常包含以下一些共有的属性和方法:

常用属性

  • type:事件的类型,如'click''mouseover'等。
  • target:触发事件的元素。与event.currentTarget不同,target是实际触发事件的元素,而currentTarget是绑定事件处理函数的元素。
  • currentTarget:绑定事件处理函数的元素。在事件捕获或冒泡阶段,这个属性总是指向添加事件监听器的那个元素。
  • bubbles:一个布尔值,表示事件是否冒泡。对于冒泡事件,这个属性为true;对于不冒泡的事件(如focusblur等),这个属性为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,即在冒泡阶段执行。

注意事项

  1. 事件监听器可以添加多个:同一个元素可以为同一个事件添加多个监听器,这些监听器会按照它们被添加的顺序依次触发。
  2. 移除事件监听器:如果需要,可以使用removeEventListener方法来移除之前添加的事件监听器。注意,移除时需要提供与添加时完全相同的函数引用。
  3. 内存泄漏:如果添加事件监听器的元素被删除或不再需要,但监听器函数仍然保留在内存中,这可能导致内存泄漏。确保在适当的时候移除不再需要的事件监听器。

捕获阶段

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节点传播的。

注意:

  1. onclick和attachEvent(ie)只能得到冒泡阶段

  2. addEventListener(type, listener[, useCapture]) 第三个参数如果是true,表示在事件捕获阶段调用事件处理程序;如果是false(不写默认是false),表示在事件冒泡阶段调用事件处理程序

  3. 实际开发中,我们很少使用事件捕获,我们更关注事件冒泡

  4. 有些事件是没有冒泡的,比如onblur、onfocus、onmouseenter、onmouseleave

  5. 事件的冒泡有时会带来麻烦,不过是可以被阻止的,方法是: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属性,我们可以确定实际触发事件的元素,并据此执行相应的操作。

优点

  1. 性能提升:由于减少了事件监听器的数量,特别是当处理大量相似元素时,性能会得到显著提升。
  2. 动态内容管理:对于动态添加到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>元素被点击,并据此执行相应的操作。

作业

在第三讲作业的基础上,增加添加文章和删除文章功能。

第7讲作业

  • 初始状态下可以显示几篇文章也可以不显示

  • 上面表单样式和删除按钮样式随意,旨在完成基本功能

  • 图片链接可以写死