React onClick get li clicked [Solved]

React onClick get li clicked [Solved]
0.0 0

#1

I have a React component which looks like this:

class Answers extends React.Component {
    constructor(props) {
        super(props);
    }
    
    handleCheck(e) {
        alert(e.target);
    }
    
    render() {
        return (
            <div id="answers">
                <ul>
                    <li onClick={this.handleCheck}><span>A</span> <p>{this.props.answers[0]}</p></li>
                    <li onClick={this.handleCheck}><span>B</span> <p>{this.props.answers[1]}</p></li>
                    <li onClick={this.handleCheck}><span>C</span> <p>{this.props.answers[2]}</p></li>
                    <li onClick={this.handleCheck}><span>D</span> <p>{this.props.answers[3]}</p></li>
                </ul>
            </div>
        );
    }
}

As you can see, I have an onClick event on each li items.
All I want is to get the corresponding li when the user click that li.
The problem is that e.target, will get sometimes the <span> and sometimes the <p> tags… (which are the li’s children tags)
I don’t understand why is this happening. Can someone enlighten me? :slight_smile:

Thank you!

You can find and download the repo on github.


#2

I’ve solved it by adding for each li’s a data-id attribute and binding the handleCheck method to this.

<li onClick={this.handleCheck.bind(this)} data-id="1"><span>A</span> <p>{this.props.answers[0]}</p></li>
<li onClick={this.handleCheck.bind(this)} data-id="2"><span>B</span> <p>{this.props.answers[1]}</p></li>
<li onClick={this.handleCheck.bind(this)} data-id="3"><span>C</span> <p>{this.props.answers[2]}</p></li>
<li onClick={this.handleCheck.bind(this)} data-id="4"><span>D</span> <p>{this.props.answers[3]}</p></li>

And then in handleCheck I’ll get the element with:

handleCheck(e) {
   e.currentTarget.dataset.id 
}

#3

Can you put ‘this.handleCheck = this.handleCheck.bind(this)’ in your constructor to trim things down a bit?


#4

You are right. :slight_smile: thank you!


#5

I was having a similar issue, I was able to solve it by using e.currentTarget.dataset.id instead of e.target.dataset.id. If I used e.target the onClick of the parent would still fire, but the data-id value would be undefined, which makes sense because the children elements do not have data-id set. But by using e.currentTarget instead, even clicking on a child element would still capture the correct data-id value.

This code is not actually tested because I’m short on time but it is linted.
Styles removed for readability except to show the parent / child boundaries.
Clicking on either the parent or the child should carry the data-id value into the setRejectionID onClick method.

Note that the setRejectionID method only needs to be bound once.

import React from 'react';
import { connect } from 'react-redux';

class Rejection extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      rejectionID: null,
      rejections: [{
        id: 0,
        name: "First Rejection"
      },
      {
        id: 1,
        name: "Second Rejection"
      }]
    };

    this.setRejectionID = this.setRejectionID.bind(this);
  }

  setRejectionID(e) {
    let rejectionID = this.state.rejectionID;
    if (e.currentTarget.dataset.id == 0 || e.currentTarget.dataset.id) {
      rejectionID = e.currentTarget.dataset.id;
    }
    this.setState({ rejectionID });
  }

  render() {
    let rejections = this.state.rejections;
    let rejectionID = this.state.rejectionID;
    let content;

    // Display rejection if a rejection ID has been selected
    if (!isNaN(rejectionID) || rejectionID == 0) { // If value is == 0 or is a number

      // Get our rejection object
      let rejection = rejections[rejectionID];

      // Display the page to show the selected data
      content = (<div><p>You have selected Rejection {rejection.name} which has ID {rejection.id}</p></div>);

    } else {  // If rejectionID is not set
      if (rejections.length) {

        let rejections = rejections
          .map((rejection, i) => {
            return (<li style={{ border: "1px solid #000000", width: "600px" }} key={i} data-id={rejection.id} onClick={this.setRejectionID}>
              <span style={{ border: "1px solid #ff0000", width: "300px", margin: "20px" }}>Rejection for {rejection.name}</span>
            </li>);
          });

        content = (<div>
          <div>Please choose one:</div>
          <div>
            <ul>
              {rejections}
            </ul>
          </div>
        </div>);

      } else {
        content = (<div>There are no Rejections to choose from.</div>);
      }
    }
    return (<div>
      {content}
    </div>);

  }
}

export default Rejection;