2026/3/28 16:56:58
网站建设
项目流程
linux 做网站用哪个版本,建设网站费用计入什么科目,抖音网络营销推广方式,动画网站建设捕获、冒泡和事件委托
一、DOM和DOM树
DOM 概念#xff1a;DOM#xff08;Document Object Model —— 文档对象模型#xff09;是一种将 HTML、XML 文档结构转化为树状对象的编程接口。它把文档里的元素、文本、属性等都变成可操作的 “节点对象”#xff0c;让编程语言…捕获、冒泡和事件委托一、DOM和DOM树DOM概念DOMDocument Object Model —— 文档对象模型是一种将 HTML、XML 文档结构转化为树状对象的编程接口。它把文档里的元素、文本、属性等都变成可操作的 “节点对象”让编程语言比如 JavaScript能方便地访问、修改文档的内容、结构和样式是实现网页动态交互的基础作用开发网页内容特效和实现用户交互DOM树概念DOM 树是 DOM文档对象模型对 HTML 或 XML 文档结构的一种树状可视化表示。它将文档中的所有内容元素、文本、属性、注释等抽象为 “节点”并按照它们在文档中的层级关系组织成类似树的结构DOM 树的核心特点根节点整个树的起点是document对象代表整个文档其直接子节点通常是 HTML 文档的html元素层级关系每个节点有明确的父子、兄弟关系。例如html是head和body的父节点而head和body互为兄弟节点节点类型树中包含多种节点如元素节点div、文本节点 “Hello”、属性节点classbox等共同构成文档的完整结构作用DOM树直观的体现了标签与标签之间的关系总结DOM为我们提供了一套专门用来操作网页内容的接口这时候他是一种抽象的模型概念看不见摸不着。它本质是一套规范和接口比如 “如何获取元素” “如何修改文本” 的规则定义了操作文档的方式但本身不是一个可见的实体更像是一种 “协议” 或 “逻辑框架”DOM树将这个模型概念给具体化可视化了变成我们能看见画出来的一种树状结构。是对 DOM 模型的直观呈现通过树状结构把文档的层级关系父子、兄弟节点画出来让抽象的 “节点关系” 变成了能看懂的图形帮助理解 DOM 的结构逻辑一张概览图二、事件流为什么要先介绍DOM和DOM树DOM、DOM 树跟事件流有很紧密的关联简单说DOM 是基础接口DOM 树是结构载体事件流是基于 DOM 树的事件传播规则。什么是事件流事件完整执行过程中的流动路径用一个比喻理解把 DOM 树看作一棵 “网页树”树上的每个枝桠、每片叶子对应 DOM 节点如按钮、文本都是可能被 “触碰”触发事件如点击的对象当我们触碰一片叶子比如点击一个按钮这个 “触碰信号” 不会直接就到达被触碰的目标叶子上也不会到达之后一直就停留再目标叶子上 —— 它会先从树根document出发沿着树干、树枝向下 “找”一路 “找” 到被触碰的叶子上等到达叶子目标节点后信号又会沿着原来的树枝、树干向上 “返”一路回到树根。这个 “从根向下找、再从目标向上返” 的完整信号传播路径和规则就是事件流事件流有三个阶段事件捕获目标阶段事件到达实际触发事件的目标节点比如被点击的按钮触发该节点上绑定的事件处理函数的阶段事件冒泡通过上面的比喻简单来说捕获阶段是从父到子冒泡阶段是从子到父一个流程图事件捕获概念事件从DOM树的根节点document向下逐层寻找目标节点的过程一个由上到下的过程。如果这个从上到下的过程中遇到的节点有绑定与你的触发动作我点击了一下相同的事件监听有一个点击的事件监听就会先执行该节点上对应的事件处理函数等所有上层节点的事件执行完才到目标节点本身一个比喻理解把DOM 树当成一棵大树我们要触碰的叶子比如点击按钮即目标节点是最终要找的 “目的地”。事件捕获就像 “信号从树根出发沿着树干、大枝桠、小枝桠一步步向下找到那片被触碰的叶子”—— 沿途每经过一个节点树干、大枝桠、小枝桠如果这个节点绑定了 “捕获阶段” 的事件监听就会先触发这个监听函数再继续往下走直到抵达目标叶子注意事件捕获需要写对应代码才能看到效果代码DOM元素.addEventListener(事件类型事件处理函数是否使用捕获机制)代码参数说明addEventListener第三个参数传入true代表是捕获阶段触发较少使用若传入false代表冒泡阶段触发默认就是false若是用 L0 级事件监听则只有冒泡阶段没有捕获!--HTML标签内直接绑定--button onclickalert(点击了)按钮/button// JS中通过on事件名绑定constbtndocument.querySelector(button);btn.onclickfunction(){alert(L0级点击事件触发);};一个事件捕获的代码案例style#grandpa{margin:100px auto;width:300px;height:300px;background-color:cadetblue;}#father{width:200px;height:200px;background-color:darkseagreen;}#son{width:100px;height:100px;background-color:lightblue;}/style!--三层嵌套爷爷 → 爸爸 → 儿子--div idgrandpa爷爷div idfather爸爸div idson点击我儿子/div/div/divscript// 给三层都绑定捕获阶段的点击事件第三个参数为truegrandpa.addEventListener(click,(){alert(爷爷 - 捕获阶段);},true);father.addEventListener(click,(){alert(爸爸 - 捕获阶段);},true);son.addEventListener(click,(){alert(儿子 - 捕获阶段);},true);/script事件冒泡概念事件从触发的的目标节点开始向上逐层 “扩散” 回到DOM树的根节点的过程由下向上的过程。这个从下到上扩散回根节点的过程中沿途如果有节点绑定了与你触发目标节点的触发动作相同的监听事件就会向上依次触发这些监听函数知道回到根节点一个比喻理解假设你点击了一片叶子目标节点比如一个按钮事件冒泡就像 “叶子被点击的信号沿着树枝、树干一路向上传到树根”—— 沿途向上经过的每个节点比如按钮的父 div、父 body、甚至 html如果绑定了 “冒泡阶段” 的点击的事件监听就会依次触发这些监听函数直到信号传到最顶端的document简单理解当一个元素触发事件后会依次向上触发所有父级的同名事件注意事件冒泡在大多数常用事件如click、input、mouseover里面都是是默认存在的L1 级、L2 级事件监听第三个参数是 false或者默认都是冒泡但是也有少数事件如focus、blur、scroll没有冒泡特性一个事件冒泡的代码案例grandpa.addEventListener(click,(){alert(爷爷 - 冒泡阶段);});father.addEventListener(click,(){alert(爸爸 - 冒泡阶段);});son.addEventListener(click,(){alert(儿子 - 冒泡阶段);});一个小小的问题// 以上面的案例里面的其中一个事件监听为例son.addEventListener(click,(){alert(儿子 - 捕获阶段);});现在上面这个案例里面第三个参数为默认false即现在这个事件监听它监听的是事件冒泡的过程那么问题来了这个事件流里面事件捕获阶段还存在吗答案是存在只是此时绑定的事件处理函数不会在捕获阶段被触发只会在目标阶段和冒泡阶段响应触发为什么呢事件流的三个阶段捕获 → 目标 → 冒泡是DOM 规范规定的固定流程只要事件被触发这三个阶段就一定会完整执行与事件绑定方式是否监听捕获阶段无关addEventListener的第三个参数useCapture的作用是决定当前事件处理函数在哪个阶段响应当useCapture为true时函数在捕获阶段触发从根到目标的过程中当useCapture为false或省略时函数在冒泡阶段触发到达目标时或从目标向根传播时。目标阶段不论useCapture参数是true还是false都会触发一个关于触发顺序的例子grandpa.addEventListener(click,(){alert(爷爷);},true);father.addEventListener(click,(){alert(爸爸);});son.addEventListener(click,(){alert(儿子);},true);控制事件行为的两种方法理解了捕获和冒泡的原理后为了避免“意外触发”或实现复杂的事件逻辑我们将介绍一个阻止事件传播的方法。以及另一个阻止默认行为方法因为经常会与阻止事件传播组合搭配使用所以我们放在这里一并介绍阻止事件传播stopPropagation()event.stopPropagation()方法可以阻止事件继续向下或向上传播无论是在捕获阶段还是冒泡阶段调用都能中断后续的传播过程两个小问题ev.stopPropagation()会阻止目标元素事件监听的执行吗不会前面的定义说过只是会中断捕获和冒泡的后续传播具体效果看下面例子的效果ev.stopPropagation()会影响该元素其他事件的后续传播吗也不会每次事件触发都是一个独立的事件对象ev调用stopPropagation()仅作用于当前这个事件实例不会影响未来触发的其他事件即使是同一类型。具体效果看下面例子的效果一个具体例子// 思考// 以上三个事件监听如果都改为默认的冒泡阶段监听时ev.stopPropagation()这句话在第二个事件监听里面出现和第三个事件监听里面出现结果有什么不同grandpa.addEventListener(click,(){alert(爷爷);},true);father.addEventListener(click,(ev){ev.stopPropagation()alert(爸爸);},true);son.addEventListener(click,(ev){// ev.stopPropagation();alert(儿子);},true);grandpa.addEventListener(mouseover,(){alert(爷爷鼠标移入事件出现);},true);father.addEventListener(mouseover,(ev){alert(爸爸鼠标移入事件出现);},true);son.addEventListener(mouseover,(ev){// ev.stopPropagation();alert(儿子鼠标移入事件出现);},true);阻止默认行为preventDefault()为什么需要很多事件都有浏览器预设的默认行为比如点击链接会跳转、提交按钮会提交表单。有时候我们不需要浏览器预设的默认行为我们想要自定义一些事件的默认行为就需要先阻止浏览器预设的默认行为再设置我们自己的。所以介绍的这个event.preventDefault()方法就可以阻止这些默认行为与阻止事件传播方法的区别不会影响事件的传播过程这是与stopPropagation()的核心区别作用对象不同一个具体例子bodya hrefhttps://example.comidlink点击我/aform idformaction/submitbutton typesubmit提交/button/formscript// 阻止链接跳转constlinkdocument.getElementById(link);document.querySelector(div).addEventListener(click,function(){console.log(父盒子被触发);})link.addEventListener(click,(e){// 取消默认跳转行为e.preventDefault();console.log(链接被点击但不跳转);});// 阻止表单提交constformdocument.getElementById(form);form.addEventListener(submit,(e){// 取消默认提交刷新e.preventDefault();console.log(表单提交被拦截将异步处理);});/script/bodystopPropagation()与preventDefault()核心区别总结特性stopPropagation()preventDefault()作用对象事件的传播过程捕获 / 冒泡事件的默认行为浏览器预设是否影响传播是中断传播链否传播正常进行是否影响默认行为否默认行为仍会执行是取消默认行为典型使用场景阻止父子元素事件相互干扰自定义元素行为如链接、表单三、事件委托在说事件委托之前有个问题在前面提到捕获和冒泡的比喻里面说的传递信号大家知道这个传递的信号在事件捕获和冒泡里面指的是什么吗本质是包含了 “触发节点信息” 在内的「事件对象event」—— 它装着整个事件的完整信息是事件传播时的 “信息载体”这个完整信息包括谁是触发者event.target就是被点的 li即 “触发节点信息”事件类型是什么event.type比如是“click”点击事件事件是在哪个阶段传播的event.eventPhase捕获 / 目标 / 冒泡点击时鼠标在哪个位置event.clientX/event.clientY甚至能让信号停止传播event.stopPropagation()、阻止默认行为event.preventDefault()事件委托也叫事件代理是一种利用事件冒泡特性实现的高效事件处理技巧 —— 简单说就是不给子节点单独绑定事件而是把事件绑定在它们的父节点上由父节点 “代理” 处理所有子节点的事件核心逻辑因为事件会冒泡子节点触发的事件最终会传到父节点上。父节点监听到了事件对像携带的事件类型与父节点绑定事件类型一致后就执行父节点对应的事件处理函数通过事件对象event的target属性判断是哪个子节点触发了事件再针对性处理一个例子style#list{margin:100px auto;width:200px;background-color:antiquewhite;}#list li{margin-bottom:10px;width:100%;background-color:lightpink;}/style!--父节点ul子节点多个li--ul idlistli列表项1/lili列表项2/lili列表项3/li/ulscript// 只给父节点ul绑定一次点击事件代理所有li的事件document.getElementById(list).addEventListener(click,function(ev){// e.target 就是触发事件的子节点被点击的li// 确保点击的是 li 节点if(ev.target.tagNameLI){alert(点击了${ev.target.textContent});}});/script