Search code examples
javascriptevent-handlingevent-bubblingevent-capturing

How to prevent events from acting in nested divs when using React Hooks


I have nested div elements. Outside there should be events to move and to click. I want to enable a nested element to act as a button and to prevent the event from outside to act. In the example when we click on the red part, both events are happening. How can I stop the event from the green part when we click on the red part? stopPropagation() did not do the job. I am using React and my structure is more complex than in the example. But I wanted to reduce for reasons of clarity.

I would be happy if someone can help.

#bigCircle {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 180px;
  height: 180px;
  border-radius: 50%;
  background-color: green;
}

#smallCircle {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background-color: red;
}
<!DOCTYPE html>
<html lang="en">

<head>
</head>

<body>
  <div id="bigCircle">
    <div id="smallCircle"></div>
  </div>
  
  <script>
  document.getElementById("bigCircle").addEventListener("click",myBigCircle);
  
    document.getElementById("smallCircle").addEventListener("click",mySmallCircle);
  
  function myBigCircle(){
  console.log("Hello");}
  
    function mySmallCircle(){
  console.log("Bye");}
  </script>
</body>

</html>

I think it is related to React Hooks. Here you see the example with stopPropagation not working:

import React, { useState } from 'react';

function Test() {
    let [test, setTest] = useState(false);


    return (
        <div id="wrapper"
            style={styleBigCircle}
            onMouseUp={() => {
                setTest(false);
                console.log("Hello", test)
            }
            }>

            <div id="center"
                style={styleSmallCircle}
                onClick={(e) => {
                    e.stopPropagation();
                    setTest(!test);
                    console.log("Bye", test)
                }}
            >

            </div>

        </div>

    );
}

const styleBigCircle = {
    display: "flex",
    justifyContent: "center",
    alignItems: "center",
    width: "180px",
    height: "180px",
    borderRadius: "50%",
    backgroundColor: "green"
}

const styleSmallCircle = {
    width: "40px",
    height: "40px",
    borderRadius: "50%",
    backgroundColor: "red"
}

export default Test;

I would be happy if someone knows how to fix this.


Solution

  • You are dealing with different events, so to prevent the child element from trigger the parent you would need to add the same event and stopPropagation() on that one instead:

    <div
        id="center"
        style={styleSmallCircle}
        onMouseUp={e => {
          e.stopPropagation();
        }}
        onClick={e => {
          setTest(!test);
          console.log("Bye");
        }}
    />
    

    Here is a fully working example:

    const Test = () => {
      let [test, setTest] = React.useState(false);
    
      return (
        <div
          id="wrapper"
          style={styleBigCircle}
          onMouseUp={() => {
            console.log("Hello");
          }}
        >
          <div
            id="center"
            style={styleSmallCircle}
            onMouseUp={e => {
              e.stopPropagation();
            }}
            onClick={e => {
              console.log("Bye");
            }}
          />
        </div>
      );
    };
    
    const styleBigCircle = {
      display: "flex",
      justifyContent: "center",
      alignItems: "center",
      width: "180px",
      height: "180px",
      borderRadius: "50%",
      backgroundColor: "green"
    };
    
    const styleSmallCircle = {
      width: "40px",
      height: "40px",
      borderRadius: "50%",
      backgroundColor: "red"
    };
    
    const rootElement = document.getElementById("root");
    ReactDOM.render(<Test />, rootElement);
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.12.0/umd/react-dom.production.min.js"></script>
    
    
    <div id="root"></div>