冒泡
点击大一点的最顶层的盒子的时候,优先处理了那个小盒子,而父盒子里面那个小盒子的话,后面才去加载,这样做在处理hover的时候容易触发区域改变有问题,详情见我的项目友情链接那个地方
当一个事件发生在一个元素上,它会首先运行在该元素上的处理程序,然后运行其父元素上的处理程序,然后一直向上到其他祖先上的处理程序。
假设我们有 3 层嵌套 FORM > DIV > P
,它们各自拥有一个处理程序:
点击内部的 <p>
会首先运行 onclick
:
- 在该
<p>
上的。
- 然后是外部
<div>
上的。
- 然后是外部
<form>
上的。
- 以此类推,直到最后的
document
对象。
因此,如果我们点击 <p>
,那么我们将看到 3 个 alert:p
→ div
→ form
。
这个过程被称为“冒泡(bubbling)”,因为事件从内部元素“冒泡”到所有父级,就像在水里的气泡一样。
event.target
可能会等于 this
—— 当点击事件发生在 <form>
元素上时,就会发生这种情况。
具体例子,想要实现鼠标移动到父div时,显示更大的子div浮层卡片
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| import { useRef } from "react";
const LinkDetails = ({ post, getConfig, index }) => { const tooltipRef = useRef(null); const handleMouseOver = (e) => { e.stopPropagation(); if (tooltipRef.current) { tooltipRef.current.style.opacity = "1"; tooltipRef.current.style.visibility = "visible"; } }; const handleMouseOut = (e) => { if (tooltipRef.current) { tooltipRef.current.style.opacity = "0"; tooltipRef.current.style.visibility = "hidden"; } }; <div className="flex flex-col space-y-0.5 flex-grow w-3/4 hover-trigger" onMouseOver={(e) => handleMouseOver(e)} onMouseOut={(e) => handleMouseOut(e)} > <h2 className="text-sm font-semibold leading-5 truncate"> {post.title} </h2> <p className="text-current text-xs truncate ">{post.description}</p> <div ref={tooltipRef} className={classNames( "w-full p-2 bg-gray-700/[.9] text-white text-sm rounded-md", "absolute bottom-10 xl:left-[75px] left-[10px] z-[55]", "opacity-0 visibility-hidden transition-opacity duration-300" )} > {post.description} </div> </div>
|
捕获
事件处理的另一个阶段被称为“捕获(capturing)”。它很少被用在实际开发中,但有时是有用的。
DOM 事件标准描述了事件传播的 3 个阶段:
- 捕获阶段(Capturing phase)—— 事件(从 Window)向下走近元素。
- 目标阶段(Target phase)—— 事件到达目标元素。
- 冒泡阶段(Bubbling phase)—— 事件从元素上开始冒泡。
也就是说:点击 <td>
,事件首先通过祖先链向下到达元素(捕获阶段),然后到达目标(目标阶段),最后上升(冒泡阶段),在途中调用处理程序。
总结
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <style> body * { margin: 10px; border: 1px solid blue; } </style>
<form>FORM <div>DIV <p>P</p> </div> </form>
<script> for(let elem of document.querySelectorAll('*')) { elem.addEventListener("click", e => alert(`Capturing: ${elem.tagName}`), true); elem.addEventListener("click", e => alert(`Bubbling: ${elem.tagName}`)); } </script>
|
上面这段代码为文档中的 每个 元素都设置了点击处理程序,以查看哪些元素上的点击事件处理程序生效了。
如果你点击了 <p>
,那么顺序是:
HTML
→ BODY
→ FORM
→ DIV
(捕获阶段第一个监听器):
P
(目标阶段,触发两次,因为我们设置了两个监听器:捕获和冒泡)
DIV
→ FORM
→ BODY
→ HTML
(冒泡阶段,第二个监听器)。