Search code examples
javascripthtmlreactjsdomdom-events

Make a button inside an element clickable while the element itself is also clickable


I have a simple sidebar, that has a bunch of list items in it, and a button right next to the list item, like so:

enter image description here

I attached a click handler to the <li> element like in the code below:

<li className="note" onClick={()=> props.selectNote(props.note)} role="button">
    <button className="delete-note" onClick={() => console.log('Fired')}>Delete dis</button>
    <span className="updated-at">2hr</span>
    <div className="note-content">
        <h4 className="note-title">
            {title}
        </h4>
        <p className="note-preview">
            {notePreview.substr(0, 80)}...
        </p>
    </div>
</li>

But as expected, when I click the button next to it, the actual li gets clicked and not the button inside it. I think this is some sort of issue with how the event bubbles, and how it's a bad practice to attach onClick handlers to non-interactive elements (My ESLint says that).

What I want instead:

  1. When the list item gets clicked, the attached onClick event fire.
  2. When the button gets clicked, fire the onClick event on the button, and not the <li>.

Solution

  • Hack incoming!

    I solved this by adding a name attribute to the elements that I didn't want to trigger the main click event:

    handleClick = (e) => {
        if(e.target.name === 'deleteButton') {
            e.preventDefault();
            e.stopPropagation();
        }else {
            this.props.selectNote(this.props.note)
        }
    }
    
    
    <li className="note" onClick={this.handleClick} role="button">
        <button className="delete-note" name="deleteButton" onClick={() => console.log('Fired')}>Delete dis</button>
        <span className="updated-at">2hr</span>
        <div className="note-content">
            <h4 className="note-title">
                {title}
            </h4>
            <p className="note-preview">
                {notePreview.substr(0, 80)}...
            </p>
        </div>
    </li>
    

    You need to check which element triggered the event, and prevent it if it was the button.