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

问题描述

在我的应用中,我希望能够点击一个项目(背景颜色、文本等),弹出一个带有颜色选择器的模式,然后更改项目的颜色。

我遇到的问题是,我为父元素创建了一个 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

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...