0

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

我遇到的问题是我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, 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>
  );
};

有没有办法仅在单击背景时显示颜色选择器?

如果您想查看GitHub 上的真实示例或所有代码,该应用程序也部署在Netlify上。

4

3 回答 3

2

事件对象有一个target属性,该属性包含用户与之交互以触发事件的确切元素。因此,您可以只检查目标元素是否是父元素,以了解它们是直接与父元素交互还是与它们的子元素之一交互。

这是一种方法:

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

一种更强大的方法是将父对象存储在React ref中,并确保 e.target 与该 ref 完全相同。(这是可以使用 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)}
        />
      )}
    </>
  );
}

于 2021-06-22T23:27:11.173 回答
2

发生的事情称为“事件冒泡”,这是预期的行为(您可以在此处阅读更多信息)。最终,您会发现它非常有用。

如果您只想处理从附加处理程序的同一元素触发的事件,您可以执行以下操作:

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>

于 2021-06-22T23:33:16.397 回答
1

提供的事件对象为您提供了一些可以使用的默认预防措施。

例子:

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

于 2021-06-22T23:37:21.693 回答