简体   繁体   English

有没有办法只在父元素上执行点击事件?

[英]Is there a way to perform a click event only on a parent element?

In my app I would like to be able to click an item (background color, text, etc), have a modal pop up with a color picker, then change the color of the item.在我的应用程序中,我希望能够单击一个项目(背景颜色、文本等),弹出一个带有颜色选择器的模式,然后更改项目的颜色。

The issue I'm having is that I made an onClick handler for the parent element to update a background color, but it's also activating when anything within the parent element is clicked.我遇到的问题是我为父元素创建了一个onClick处理程序来更新背景颜色,但是当单击父元素中的任何内容时它也会激活。

I have an example in Codesandbox and you can see that whether you click the background or the buttons, the color picker comes up when I only want it activated for the background.我在Codesandbox中有一个示例,您可以看到,无论您单击背景还是按钮,当我只想为背景激活颜色选择器时,它都会出现。

If anyone is familiar with Chakra-ui, this is my code:如果有人熟悉 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, 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>
  );
};

Is there a way to show the color picker only when the background is clicked?有没有办法仅在单击背景时显示颜色选择器?

The app is also deployed on Netlify if you want to see the real example or all of the code on GitHub .如果您想查看GitHub上的真实示例或所有代码,该应用程序也部署在Netlify上。

The event object has a target property, which holds the exact element that the user interacted with to trigger the event.事件 object 有一个target属性,它包含用户与之交互以触发事件的确切元素。 So, you can just check if the target element is the parent element to know if they interacted with the parent directly or one of their children.因此,您可以只检查目标元素是否是父元素,以了解它们是直接与父元素交互还是与它们的子元素之一交互。

Here's one way of doing it:这是一种方法:

if (e.target.classList.contains('navbar') && !showColorPicker) {
  setShowColorPicker((showColorPicker) => !showColorPicker);
}

A more robust way of doing it would be to store the parent in a React ref , and make sure that e.target is exactly the same as that ref.一种更强大的方法是将父对象存储在React ref中,并确保 e.target 与该 ref 完全相同。 (This is one of the places where it's ok to use a ref). (这是可以使用 ref 的地方之一)。

Here's a complete example that uses a ref.这是一个使用 ref 的完整示例。 (in won't run in StackOverflow, because I didn't properly load up the libraries, but it'll work). (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)} /> )} </> ); }

Whats happening is called "Event Bubbling" and it is the intended behavior (you can read more about it here ).发生的事情称为“事件冒泡”,这是预期的行为(您可以在此处阅读更多信息)。 Eventually, you'll find that it is very useful.最终,您会发现它非常有用。

If you want to only handle events that are triggered from the same element where the handler is attached, you can do something like this:如果您只想处理从附加处理程序的同一元素触发的事件,您可以执行以下操作:

 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>

The event object provided gives you some default preventions that you can use.提供的事件 object 为您提供了一些可以使用的默认预防措施。

Example:例子:

const handleClick = (event) => {
 event.stopPropagation();
}

Should be added on clickable elements that are part of the parent and that should not trigger the event.应添加到属于父级且不应触发事件的可点击元素上。 This will prevent your event to be propagated to the parent element.这将阻止您的事件传播到父元素。

I've forked your codesanbox and added my solution in. https://codesandbox.io/s/infallible-hellman-66sln?file=/src/App.js我已经分叉了你的 codesanbox 并添加了我的解决方案。https://codesandbox.io/s/infallible-hellman-66sln?file=/src/App.js

I think the above solutions are also correct and everything will depend on the situation.我认为上述解决方案也是正确的,一切都将视情况而定。

More info here: https://developer.mozilla.org/en-US/docs/Web/API/Event/stopPropagation更多信息: https://developer.mozilla.org/en-US/docs/Web/API/Event/stopPropagation

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM