封闭开发了一段时间重感冒了,本来应该整理为几篇文章,康复后发现细节已淡忘,记录为几个不同的片段吧
问题场景(简要)
- 有一个仿 ant-design 的 Select:输入框有值时显示筛选下拉,输入为空时显示历史记录列表。
- 如果在 Input 的 onBlur 里关闭下拉,任何在下拉内的点击都会先触发 input 的 blur 导致下拉被隐藏,从而下拉内的点击无效(例如删除按钮或选择项无法响应)。
- 最初用的解决方案是:全局监听 document 的 mousedown(useClickAnywhere 钩子),点击不在下拉区域则关闭下拉。问题是:当页面嵌入子域 iframe 时,iframe 内的点击不会触发父页面的 document 事件,导致下拉一直保持(或不能正确关闭)。与子域协商后,他们通过 postMessage 在 iframe 内发出 click 通知,父域接收后关闭下拉,但每个子域都需配合,成本高。
解决办法
- 利用阻止默认行为:在下拉容器最外层添加 onMouseDown={(e) => e.preventDefault()},使得点击下拉内部时不会导致输入框失去焦点(即阻止触发 input 的 blur),从而下拉内部的按钮/项点击能够正常响应;点击下拉外部仍会触发 blur,关闭下拉。
- 示例:给按钮或下拉外层绑定 onMouseDown 并在处理函数里 e.preventDefault()。
e.preventDefault()
复盘发现下面写的我都看不懂 看上面的简要总结吧
背景:可模拟ant-design的select组件,在输入框有值显示下拉选择列表的基础上,当输入框为空的时候,显示10条之前的搜索历史记录列表
/**
* 自定义钩子:点击页面任何位置时触发回调函数
* @param onClickAway - 点击页面任何位置时需要执行的回调函数
* @param ref - React 引用对象,指向需要排除点击事件的 DOM 元素
*/
const useClickAnywhere = (onClickAway: () => void, ref: RefObject<HTMLElement>): void => {
useEffect(() => {
// 点击事件处理函数
const handleClickOutside = (event: MouseEvent): void => {
if (ref.current && !ref.current.contains(event.target as Node)) {
onClickAway();
}
};
// 绑定点击事件
document.addEventListener('mousedown', handleClickOutside);
// 组件卸载时移除事件监听
return (): void => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, [onClickAway, ref]);
};
export default useClickAnywhere;
可是子域嵌的Iframe,无法监听Iframe的点击事件, 导致子域弹窗一直尴尬的在那儿。和其中一位子域研发沟通后,他给我传了click事件,message监听到之后,处理弹窗消失。但是每个子域都需要这样做,每个子域研发不同,有点麻烦,e.preventDefault()出现了!!
e.preventDefault() 可以阻止某些特定的默认行为,但并不能阻止所有事件的触发。
e.preventDefault()可以用于阻止onPressEnter事件的默认行为,例如在表单中按下回车键时阻止表单提交。e.preventDefault()可以用于阻止onBlur事件的默认行为,例如在某些情况下阻止输入框失去焦点。
所以只要在列表的最外层元素上加上onMouseDown={(e: React.MouseEvent<HTMLDivElement>) => e.preventDefault()}这段代码,就可以实现点击弹窗区域,弹窗不消失,点击弹窗之外,触发onBlur,弹窗消失
举个例子,假设我们有一个输入框和一个按钮,当点击按钮时,阻止输入框失去焦点(阻止 onBlur 事件):
import React, { useRef } from 'react';
import { Input, Button } from 'antd';
const PreventBlurExample = () => {
const inputRef = useRef(null);
const handleMouseDown = (e) => {
e.preventDefault(); // 阻止按钮点击时输入框失去焦点
};
return (
<div>
<Input ref={inputRef} placeholder="Click the button to prevent blur" />
<Button onMouseDown={handleMouseDown}>Prevent Blur</Button>
</div>
);
};
export default PreventBlurExample;
在这个例子中,当点击按钮时,e.preventDefault() 阻止了 onMouseDown 事件的默认行为,从而阻止了输入框的 onBlur 事件。
pointer-events: none
背景:react项目,div包了一个svg元素,div上有个onClick点击事件,本地调试没有问题,pre环境发现点击svg的边缘可以触发onClick,点击svg没有反应。
一开始的解决方法是把svg改为了Img,后来大佬推荐了pointer-events: none;
当时时间紧迫且别人维护的代码又臭又长,没太研究具体原因
猜测可能的原因:
- 事件传播问题:SVG元素可能会阻止事件传播到其父元素(即div),尤其是当SVG内部有自己的一些事件处理逻辑时。即事件在SVG元素上被阻止或中断了。
- SVG默认行为:一些SVG元素可能会有自己的默认行为,阻止了事件的传播或者处理。
解释
CSS pointer-events 属性:
- 在CSS中,
pointer-events: none;的作用是禁用元素的鼠标事件。这意味着,当你把鼠标移动到该元素上时,它不会响应任何鼠标事件(如点击、悬停等)。 pointer-events: none;属性使得 SVG 元素不会拦截点击事件。这样,当你点击 SVG 元素时,事件会直接传递给它的父元素(即 div)。