Momo
Momo

Reputation: 464

Why does mouseDown event not propagate in React?

I have a simple React component with two squares which are in the same absolute position. The blue square covers the red square.

function App() {
  return (
    <div className="App">
      <div>
        <div
          style={{
            position: "absolute",
            backgroundColor: "red",
            width: 100,
            height: 100
          }}
          onMouseDown={() => console.log("Red!")}
        />
        <div
          style={{
            position: "absolute",
            backgroundColor: "blue",
            width: 100,
            height: 100
          }}
          onMouseDown={() => console.log("Blue!")}
        />
      </div>
    </div>
  );
}

ReactDOM.render(<App />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>

When attaching event listeners to the react components, only the component on top catches the event and it does not reach the red square. Why is that the case? Is this default behavior? I thought event propagation is only stopped using event.stopPropagation()? When using vanilla javascript, both squares catch the event though.

function clickRed() {
  console.log("Red!");
}

function clickBlue() {
  console.log("Blue!");
}
<div id="root">
    <div style="position: absolute; background-color: red; min-width: 100px; min-height: 100px;" onmousedown="clickRed();" />
    <div style="position: absolute; background-color: blue; min-width: 100px; min-height: 100px;" onmousedown="clickBlue();"/>
</div>

Upvotes: 0

Views: 1556

Answers (1)

T.J. Crowder
T.J. Crowder

Reputation: 1073968

This isn't a React thing, it's how events work in the DOM. The issue is that events propagate through the DOM tree, from innermost children to outermost ancestors of those children. But your elements are siblings, they don't have a parent/child relationship. So whichever one is above the other in the z-order will be the one that receives the event (which then propagates to that element's parent, not its siblings).

When using vanilla javascript, both squares catch the event though.

Not with the DOM structure you've defined in your question. Here it is with straight HTML and inline event handlers:

<div class="App">
  <div>
    <div
      style="
        position: absolute;
        background-color: red;
        width: 100px;
        height: 100px;
      "
      onmousedown='console.log("Red!")'
    ></div>
    <div
      style="
        position: absolute;
        background-color: blue;
        width: 100px;
        height: 100px;
      "
      onmousedown='console.log("Blue!")'
    ></div>
  </div>
</div>

Or with JavaScript using the DOM directly:

const app = document.createElement("div");
app.innerHTML = `
<div>
  <div
    style="
      position: absolute;
      background-color: red;
      width: 100px;
      height: 100px;
    "
    class="red"
      ></div>
      <div
    style="
      position: absolute;
      background-color: blue;
      width: 100px;
      height: 100px;
    "
    class="blue"
  ></div>
</div>
`;
app.querySelector(".red").addEventListener("mousedown", () => console.log("Red!"));
app.querySelector(".blue").addEventListener("mousedown", () => console.log("Blue!"));
document.getElementById("root").appendChild(app);
<div id="root"></div>


Re your edit adding this example:

function clickRed() {
  console.log("Red!");
}

function clickBlue() {
  console.log("Blue!");
}
<div id="root">
    <div style="position: absolute; background-color: red; min-width: 100px; min-height: 100px;" onmousedown="clickRed();" />
    <div style="position: absolute; background-color: blue; min-width: 100px; min-height: 100px;" onmousedown="clickBlue();"/>
</div>

The HTML there is incorrect. In HTML, <div/> is exactly the same thing as <div> — it's just a start tag. As a result, your div elements are nested (blue is inside red). The HTML equivalent to JSX <div/> is <div></div>:

function clickRed() {
  console.log("Red!");
}

function clickBlue() {
  console.log("Blue!");
}
<div id="root">
    <!-- Scroll right in the stack snippet to see the difference at the end ⇒ ⇒                                             ⇩⇩⇩⇩⇩⇩⇩ -->
    <div style="position: absolute; background-color: red; min-width: 100px; min-height: 100px;" onmousedown="clickRed();"  ></div>
    <div style="position: absolute; background-color: blue; min-width: 100px; min-height: 100px;" onmousedown="clickBlue();"></div>
</div>

Upvotes: 3

Related Questions