1

I have multiple elements with same className and i want if some element (with className history-node) is clicked it should get className active along with current className.

But i am having an issue, there are childs of that element and if child elements gets clicked they also get class Active.

Here is the code:

<div className="history-node-container" key={index}>
  <div className="history-node" onClick={(e) => {this.handleHistoryClick(e.target)}}>
    <span className="history-title">{heading.action}</span>
    <span className="history-date">{moment(heading.added_at).format("MMMM Do, YYYY")}</span>
  </div>
</div>

handleHistoryClick function

handleHistoryClick(target){
  $('.history-node').removeClass('active'); //removing active class from all elements
  target.className = 'history-node active';
}

I want to run function when user click on element with className history-node

But if user clicks on history-title, ClickHandler gives class active to history-title element.

EXPECTED BEHAVIOUR: if history-node is clicked only history-node should get class active.

2 Answers 2

5

One tip and how to solve your problem (In two ways)

Tip: Not usually the best idea to mix React with jQuery. React came in as a major paradigm shift in how we interact with the DOM. Try to read a little bit more about React, how it works, why is it so different from simply adding/removing elements in the DOM with jQ.

Some references to help you with that:

How to go from jQuery to React.js?

Thinking in React for jQuery Programmers


Now, back to your question

You should use currentTarget.

As the .history-title and .history-date elements are wrapped within .history-node, any click on them will trigger it's parent's event, since .history-node body is .history-title + .history-date. That's the correct behavior. When you trigger an event in JS, the event object receives two parameters: target, which is the event triggering the event and currentTarget, the element that is actually listening for the event.

Now for the code:

with JQ

Component:

<div className="history-node-container" key={index}>
  <div className="history-node" onClick={handleHistoryClick}>
    <span className="history-title">{heading.action}</span>
    <span className="history-date">{moment(heading.added_at).format("MMMM Do, YYYY")}</span>
  </div>
</div>

Click:

handleHistoryClick(event){
  $('.history-node').removeClass('active')
  event.currentTarget.classList.add('active')
}

The React way (Pure React, no modules)

Component:

class HistoryNode extends Component {
    constructor(props) {
        super(props)
        this.state = { isActive: false }
        this.handleClick = this.handleClick.bind(this)
    }

    handleClick(e) {
      let state = this.state
      this.setState({isActive: !state.isActive})
    }

    render() {
      return(
        <div className="history-node-container">
          <div className={`history-node ${this.state.isActive ? 'active' : ''}`} onClick={this.handleClick}>
            <span className="history-title">{heading.action}</span>
            <span className="history-date">
               {moment(heading.added_at).format("MMMM Do, YYYY")}</span>
          </div>
        </div>
      )
    }
}

Notice how you don't need to manipulate the DOM at any moment at the React solution. You just attach your event to that particular component, define how it's state change should reflect on the UI and let React do the rest.

Hope it helps ;)

Reference for target vs currentTarget

Sign up to request clarification or add additional context in comments.

2 Comments

Great explanation though. Thanks a lot for your time and effort. I got it working.
Two comments: * In the line <div className={history-node ${this.state.isActive ? 'active' : ''}} onClick={handleHistoryClick}>, it should be onClick={this.handleClick} (I submitted an edit) * If you declare handleClick as handleClick = () => {... then you won't need this.handleClick = this.handleClick.bind(this) in the constructor
1

I think the event propagates to child components.

Have you tried this ?

<div className="history-node-container" key={index}>
  <div className="history-node" onClick={handleHistoryClick}>
    <span className="history-title">{heading.action}</span>
    <span className="history-date">{moment(heading.added_at).format("MMMM Do, YYYY")}</span>
  </div>
</div>

HandleClick function

handleHistoryClick(event){
  event.stopPropagation();
  $('.history-node').removeClass('active');
  event.target.className = 'history-node active';
}

EDIT : You could make it simpler though (and without jQuery) using your component state. But without knowing how you wrote your component I cannot give you a snippet illustrating it. Be careful too as you interact directly with the DOM, this implies a performance loss. Using the React state allows you to avoid such thing!

1 Comment

Could you edit your original post with the entire component ?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.