react popover实现
使用 React Portals 实现 Popover
React Portals 提供了一种将子节点渲染到父组件 DOM 层次结构之外的 DOM 节点的方式,适合实现弹出层。
import React, { useState, useRef } from 'react';
import ReactDOM from 'react-dom';
function Popover({ content, children }) {
const [isOpen, setIsOpen] = useState(false);
const triggerRef = useRef(null);
const handleClick = () => setIsOpen(!isOpen);
return (
<>
<span ref={triggerRef} onClick={handleClick}>
{children}
</span>
{isOpen &&
ReactDOM.createPortal(
<div className="popover">
<div className="popover-content">{content}</div>
</div>,
document.body
)}
</>
);
}
使用 CSS 定位 Popover
通过获取触发元素的位置信息,可以精确控制 Popover 的显示位置。
function Popover({ content, children }) {
const [isOpen, setIsOpen] = useState(false);
const [position, setPosition] = useState({ top: 0, left: 0 });
const triggerRef = useRef(null);
const handleClick = () => {
if (triggerRef.current) {
const rect = triggerRef.current.getBoundingClientRect();
setPosition({
top: rect.bottom + window.scrollY,
left: rect.left + window.scrollX
});
}
setIsOpen(!isOpen);
};
return (
<>
<span ref={triggerRef} onClick={handleClick}>
{children}
</span>
{isOpen && (
<div
className="popover"
style={{
position: 'absolute',
top: `${position.top}px`,
left: `${position.left}px`
}}
>
{content}
</div>
)}
</>
);
}
添加关闭行为和动画效果
实现点击外部关闭和简单的淡入淡出动画效果。
function Popover({ content, children }) {
const [isOpen, setIsOpen] = useState(false);
const popoverRef = useRef(null);
useEffect(() => {
const handleClickOutside = (event) => {
if (popoverRef.current && !popoverRef.current.contains(event.target)) {
setIsOpen(false);
}
};
document.addEventListener('mousedown', handleClickOutside);
return () => document.removeEventListener('mousedown', handleClickOutside);
}, []);
return (
<div className="popover-container">
<span onClick={() => setIsOpen(true)}>{children}</span>
<div
ref={popoverRef}
className={`popover ${isOpen ? 'fade-in' : 'fade-out'}`}
>
{content}
</div>
</div>
);
}
使用第三方库实现
React 社区有许多成熟的 Popover 组件库可以直接使用。
import { Popover } from 'react-tiny-popover';
function Example() {
const [isOpen, setIsOpen] = useState(false);
return (
<Popover
isOpen={isOpen}
positions={['top', 'bottom', 'left', 'right']}
content={<div>Popover Content</div>}
>
<button onClick={() => setIsOpen(!isOpen)}>
Toggle Popover
</button>
</Popover>
);
}
样式基础
基本的 Popover 样式可以这样定义:
.popover {
position: absolute;
background: white;
border: 1px solid #ddd;
border-radius: 4px;
padding: 10px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
z-index: 1000;
}
.fade-in {
opacity: 1;
transition: opacity 0.2s ease-in;
}
.fade-out {
opacity: 0;
transition: opacity 0.2s ease-out;
}






