问题描述
在我的应用中,我希望能够点击一个项目(背景颜色、文本等),弹出一个带有颜色选择器的模式,然后更改项目的颜色。
我遇到的问题是,我为父元素创建了一个 onClick
处理程序以更新背景颜色,但在单击父元素中的任何内容时它也会激活。
我在 Codesandbox 中有一个示例,您可以看到无论您单击背景还是按钮,当我只想为背景激活颜色选择器时,它都会出现。
如果有人熟悉 Chakra-ui,这是我的代码:
const Navbar = () => {
const [display,setdisplay] = useState('none');
const [color,setColor] = useState('#1A202C');
const [showColorPicker,setShowColorPicker] = useState(false);
const { isOpen,onopen,onClose } = usedisclosure();
/*
On click,showColorPicker becomes true and isOpen also becomes true
in order to display the modal with a color picker
*/
const handleModalClick = () => {
onopen();
if (!showColorPicker) {
setShowColorPicker((showColorPicker) => !showColorPicker);
}
};
return (
<div>
<Modal isOpen={isOpen} onClose={onClose}>
<Modaloverlay />
<ModalContent
bg='gray.600'
style={{ BoxShadow: '2px 2px 2px 1px rgba(0,0.2)' }}>
<ModalCloseButton color='gray.200' />
<ModalBody style={{ borderRadius: '10px' }}>
<Center>
{showColorPicker && (
<ChromePicker
color={color}
onChange={(updatedColor) => setColor(updatedColor.hex)}
/>
)}
</Center>
</ModalBody>
</ModalContent>
</Modal>
// Flex === a div with display flex
<Flex
bg={color}
color='gray.200'
style={{
textTransform: 'uppercase',fontWeight: 'bold',}}
onClick={handleModalClick}>
<Link p='5' _hover={{ color: 'cyan.400' }}>
<Text fontSize='xl'>Color Selector</Text>
</Link>
<Spacer />
<Flex
display={['none','none','flex','flex']}
fontSize='md'
align='center'>
<Link p='5' _hover={{ color: 'cyan.400' }}>
About
</Link>
<Link p='5' _hover={{ color: 'cyan.400' }}>
Portfolio
</Link>
<Link p='5' _hover={{ color: 'cyan.400' }}>
Contact
</Link>
</Flex>
</Flex>
...
</div>
);
};
有没有办法只在点击背景时显示颜色选择器?
如果您想在 Netlify 上查看真实示例或所有代码,该应用程序也部署在 GitHub 上。
解决方法
事件对象有一个 target
属性,它保存了用户与之交互以触发事件的确切元素。因此,您可以检查目标元素是否是父元素,以了解它们是直接与父元素交互还是与它们的子元素之一交互。
这是一种方法:
if (e.target.classList.contains('navbar') && !showColorPicker) {
setShowColorPicker((showColorPicker) => !showColorPicker);
}
一种更可靠的方法是将父级存储在 React ref 中,并确保 e.target 与 ref 完全相同。 (这是可以使用 ref 的地方之一)。
这是一个使用引用的完整示例。 (in 不会在 StackOverflow 中运行,因为我没有正确加载库,但它会工作)。
import "./styles.css";
import React,{ useState,useRef } from "react";
import { ChromePicker } from "react-color";
export default function App() {
const [display,setDisplay] = useState("none");
const [color,setColor] = useState("#1A202C");
const [showColorPicker,setShowColorPicker] = useState(false);
const navBarRef = useRef();
const handleModalClick = e => {
if (e.target === navBarRef.current && !showColorPicker) {
setShowColorPicker((showColorPicker) => !showColorPicker);
}
};
return (
<>
<div
className="navbar"
ref={navBarRef}
style={{ backgroundColor: `${color}`,color: "white" }}
onClick={handleModalClick}
>
<button style={{ padding: "10px 15px 10px 15px",margin: "20px" }}>
Left
</button>
<button style={{ padding: "10px 15px 10px 15px",margin: "20px" }}>
Right
</button>
</div>
{showColorPicker && (
<ChromePicker
color={color}
onChange={(updatedColor) => setColor(updatedColor.hex)}
/>
)}
</>
);
}
发生的事情称为“事件冒泡”,这是预期的行为(您可以阅读有关它的更多信息 here)。最终,您会发现它非常有用。
如果您只想处理从附加处理程序的同一元素触发的事件,您可以执行以下操作:
const parent = document.getElementById('parent');
const handler = (e) => {
if (e.target !== e.currentTarget) {
return;
}
console.log('PARENT CLICKED!');
};
parent.addEventListener('click',handler);
#parent {
background-color: #123ff0;
}
.box {
display: inline-block;
height: 50px;
width: 50px;
background-color: #000000;
}
p {
color: #ffffff;
background-color: #000000;
}
<div id='parent'>
<span class='box'></span>
<span class='box'></span>
<p>This is a paragraph.</p>
<span class='box'></span>
<span class='box'></span>
</div>
所提供的事件对象为您提供了一些可以使用的默认预防措施。
示例:
const handleClick = (event) => {
event.stopPropagation();
}
应该添加到属于父级且不应触发事件的可点击元素上。 这将阻止您的事件传播到父元素。
我已经分叉了你的 codesanbox 并添加了我的解决方案。 https://codesandbox.io/s/infallible-hellman-66sln?file=/src/App.js
我认为上述解决方案也是正确的,一切都要视情况而定。
更多信息在这里:https://developer.mozilla.org/en-US/docs/Web/API/Event/stopPropagation