✊不积跬步,无以至千里;不积小流,无以成江海
今天又遇见了一个新问题,感觉是可以深挖一下的。写一篇笔记记录一下。
问题描述
在实现能够通过单击召唤/隐藏,以及3s自动隐藏的时候,发现当对上方文件栏进行操作的时候也会触发单击召唤/隐藏。在已经将这个div作为ref添加到event事件里面的时候,依旧不能够解决这个bug。
解决之后期待的效果如下:
遇见这个问题的原因
这是因为在 React 组件中,直接为包含子组件的 div 引用并不会自动包含子组件的 DOM 元素。也就是说,即便 声明在 videoTitleRef 所引用的 div 内部,React 并不保证该子组件的 DOM 元素会包含在父组件的引用中。
要解决这个问题最终采用的方案:
事件传播停止:
在 DropDown4image 组件内部阻止事件传播,即通过 stopPropagation 方法来防止点击事件冒泡到父组件。
原理解释
在 JavaScript 中,事件传播包括三个阶段:
- 捕获阶段(Capturing Phase):事件从最外层元素向目标元素传播,检查是否有监听器捕获该事件。
- 目标阶段(Target Phase):事件到达目标元素并触发目标元素的事件监听器。
- 冒泡阶段(Bubbling Phase):事件从目标元素冒泡向最外层元素,检查是否有监听器捕获该事件。
默认情况下,事件会经历从捕获到冒泡的全部阶段。也就是说,当你在某个元素上触发一个事件(例如点击事件),这个事件将会先从最外层进入到该元素(捕获阶段),然后在该元素本身触发(目标阶段),最后再从该元素向最外层传播(冒泡阶段)。因此,在父组件添加的事件监听器也会被激活。
stopPropagation 方法的作用是在事件传播时立即阻止事件从目标元素冒泡到父元素,从而避免父元素的事件处理函数被触发。具体来说,它能防止事件在冒泡阶段传递给父级元素的事件监听器。
使用 stopPropagation 的实际应用
假设我们不使用 stopPropagation:
- 用户点击
<DropDown4image />元素。 - 触发
<DropDown4image />内的点击事件处理函数。 - 由于事件会冒泡,事件会继续传播到其父元素(
<div ref={videoTitleRef}>)。 - 最终传播到
document,触发handleClickOutside函数。
使用 stopPropagation 后:
- 用户点击
<DropDown4image />元素。 - 触发
<DropDown4image />内的点击事件处理函数。 - 由于
stopPropagation方法被调用,事件不会冒泡到父元素。 - 因此,
handleClickOutside函数中的外部点击检测将不会被触发。
其他可行的办法:
在DropDown4imag组件内部封装一个ref:
并同时为 DropDown4image 组件增加 forwardRef,让它可以接受来自父组件的 ref。