Andrew Andrew - 3 months ago 31
React JSX Question

React - I can't stop propagation of a label click within a table

In the below example, I have a simple

<table>
with a checkbox inside it. I have click events on the td, tr and checkbox. I want to be able to click the checkbox and stop the bubbling to the td and tr. A simple "event.stopPropagation()" works great.

The problem is that if I want to connect a
<label>
to the checkbox using "htmlFor", the events won't stop bubbling when the label is clicked (even though it still works as expected with the checkbox itself is clicked). And even more strangely, the bubbling seems to happen in a weird order (as in Checkbox click is received last!).

Here's the code:

var Hello = React.createClass({
func1(e){
console.log('tr was clicked')
},
func2(e){
console.log('td was clicked')
},
func3(e){
e.stopPropagation();
console.log('Checkbox was clicked')
},
render: function() {
return <table>
<tbody>
<tr onClick={this.func1}>
<td onClick={this.func2}>
<input id="thing" type="checkbox" onClick={this.func3} />
<label htmlFor="thing"> label for checkbox</label>
</td>
</tr>
</tbody>
</table>;
}
});

ReactDOM.render(
<Hello name="World" />,
document.getElementById('container')
);


...And here's the Fiddle:
https://jsfiddle.net/69z2wepo/52785/
(View the console for the click events)

Answer

The label doesn't have a click handler of it's own, and can't stop propagation, so when you click the label normal event bubbling takes place. This means that all the parent's event handlers are invoked in the correct order. In addition, because of the htmlFor, the checkbox click handler is also triggered, but not as part of the event bubbling.

To solve the problem, add a separate click handler to the label that only includes .stopPropgation() (demo):

var Hello = React.createClass({
  func1(e){
    console.log('tr was clicked')
  },
  func2(e){
    console.log('td was clicked')
  },
  func3(e){
    e.stopPropagation();
    console.log('Checkbox was clicked')
  },
  stopLabelPropagation(e) {
    e.stopPropagation();
  },
  render: function() {
    return <table>
        <tbody>
        <tr onClick={this.func1}>
          <td onClick={this.func2}>
            <input id="thing" type="checkbox" onClick={this.func3} />                                     
            <label htmlFor="thing" onClick={ this.stopLabelPropagation }>label for checkbox</label>
          </td>
        </tr>
      </tbody>
    </table>;
  }
});

ReactDOM.render(
  <Hello name="World" />,
  document.getElementById('container')
);