Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React div container onClick conflicts with checkbox onChange

Tags:

reactjs

In the following React class I have a checkbox within a div container. I want a click on the container to toggle the checkbox. I also think I need to bind the onChange of the checkbox input itself, to handle things like when the user is tab/spacing to toggle the state of the checkbox (I also get a warning from React if I don't specify an onChange because this is a controlled component).

My problem is that both of the event handlers fire when I click the checkbox itself, so the state of the checkbox doesn't actually change. I have tried e.preventDefault and e.stopPropagation in handleCheckboxChange but neither seems to do anything.

export default class Item extends React.Component{
    constructor(){
        super();
        this.state={
            IsSelected: false
        }
    }
    render(){
        return (
            <div className="item">
                <div className="checkbox-container" onClick={this.handleContainerClick.bind(this)}>
                    <input type="checkbox" checked={this.state.IsSelected} onChange={this.handleCheckboxChange.bind(this)} />
                </div>                  
            </div>
        );
    }
    handleCheckboxChange(e){
        this.setState({
            IsSelected: !this.state.IsSelected
        });
    }
    handleContainerClick(){
        this.setState({
            IsSelected: !this.state.IsSelected
        });     }
}
like image 472
Sean Avatar asked Jan 06 '16 22:01

Sean


1 Answers

If you want the containing <div> to be in "control" of the checkbox's value, then don't put any handler on the checkbox itself. React will make sure the container will receive the childs click events.

var CheckboxInClickableBox = React.createClass({
  getInitialState: function () {
    return {
      checked: false
    };
  },
  render: function () {
    return (
      <div onClick={this.onClick}>
        <span>{"Checked: " + this.state.checked}</span>

        <br />

        <input type="checkbox" checked={this.state.checked} />
      </div>
    );
  },
  onClick: function () {
    this.setState({
      checked: !this.state.checked
    });
  }
});

Example on JSBin - clicking anywhere in the green box will trigger a toggle.

Also, HTML dictates that a <label> with a for="pie" attribute will pass click events to any <input id="pie">. This can appear to "break" your checkbox when using the method above - eg:

  render: function () {
    return (
      <div onClick={this.onClick}>
        <label htmlFor="pie">{"Checked: " + this.state.checked}</label>

        <br />

        <input id="pie" type="checkbox" checked={this.state.checked} />
      </div>
    );
  }

Clicking the <label> will behave as if there were 2 click events:

  • the first is the original click event bubbling from the <label> to the <div> and triggering our onClick handler
  • the second is the <label> "delegating" its click to the <input> element, due to the for attribute (htmlFor in React), which then bubbles up and hits our onClick handler

Example on JSBin - clicking anywhere in the box except the <label> will trigger a toggle. Clicking the <label> will trigger 2 toggles.

like image 169
Alex McMillan Avatar answered Oct 02 '22 13:10

Alex McMillan