首页 > 解决方案 > Parent click handler is always executing first

问题描述

I have a simple list:

<li onClick={handleRowClick}>
 <input type="checkbox" onClick={handleCheckboxClick} />
 ...
</li>

When I click in any place on the li (except its checkbox child), handleRowClick executes.

When I click on the checkbox, it always executes in this order:

row
row
checkbox

Here's what I've tried

  1. using onClickCapture on each: checkbox, li, and both
  2. calling e.stopPropagation() on each: checkbox, li, and both
  3. calling e.nativeEvent.stopPropagation() on each: checkbox, li, and both
  4. calling e.nativeEvent.stopImmediatePropagation() on each: checkbox, li, and both
  5. calling e.nativeEvent.preventDefault() on each: checkbox, li, and both

With the code above, at some point the order became

row
checkbox

But the row is always executed BEFORE the checkbox, so I can't see how make them not overlap.

Also worth mentioning that both handlers have logic, that need to happen, so making the row never execute any code is not an option.

This seems like a trivial thing, like I should know the answer to this. What am I missing?

Edit

Here's the minimal reproducible example: https://codesandbox.io/s/shy-shape-u1me4?file=/src/App.js

In the example above I stripped a lot of things like CSS classes. While doing that, I realised that what I'm actually clicking is a label for the checkbox, and the checkbox is "hidden" in my app.

I solved the problem by moving the event handler to the label instead of the checkbox, and running:

  1. e.stopPropagation() to prevent propagation to the parent li.
  2. e.preventDefault() to prevent the label to trigger the checkboxes o'clock, which would also cause the event to propagate to the li above.

标签: javascriptreactjs

解决方案


The problem seem to be that my UI library hides the actual input, and the visible area where I was clicking was actually the label. These 2 elements are siblings related by the id and htmlFor attributes respectively.

As I was attaching the event handler on the input, but the label was the one actually being clicked, the propagation was not properly being stopped.

I solved the problem by moving the event handler to the label instead of the checkbox, and running:

  1. e.stopPropagation() to prevent propagation to the parent li.
  2. e.preventDefault() to prevent the label to trigger the checkboxes o'clock, which would also cause the event to propagate to the li above.
<li onClick={handleRowClick}>
 <input id="c-1" type="checkbox" />
 <label htmlFor="c-1" onClick={handleCheckboxClick}>Foo</label>
 ...
</li>


const handleCheckboxClick = () => {
  e.stopPropagation()
  e.preventDefault()
}

推荐阅读