NealVDV
NealVDV

Reputation: 2532

if clicked on another element remove class with react

Currently upgrading from a jquery / php webapp to react. All is going well and I understand the concepts behind react etc. The only issue I can't find a workaround is how to dynamically add / delete a class based on where the user clicks. I need this because I have a few dropdowns which trigger when the user click it and need to be hidden if they click somewhere else.

From: Give a class of "selected" when clicked on another <a> then remove the class if it's the same <a>

I've taken this example as it is simple, Jquery solution to the problem:

var h2 = $("h2 a");

h2.on("click", function() {
  if ($(this).is(".selected")) {
    $(this).removeClass("selected");
  } else {
    h2.removeClass("selected")
      .filter(this).addClass("selected")
  }
});

How to mimic the same functionality in react (and / or redux)?

Image to further clarify enter image description here

Upvotes: 2

Views: 12851

Answers (2)

FurkanO
FurkanO

Reputation: 7308

The thing is, you want to toggle some data on click event and change the classname of a html element accordingly.

Your data that 'selected' class based on could come from anywhere, from parent or component state. You would do something like this :

<div className={ myData ? 'selected' : '' } ></div>

But there is a better way to display classname changes with a library called classnames. The same thing is accomplished as :

<div className={ classNames({ 'selected' : myData }) } ></div>

Until now, we have seen how to display changes on render function. Second thing you need is to listen to click events and fire the function that will eventually toggle the data that controls the 'selected' classname, in my example 'myData'.

Here is a working example, there might be various ways to accomplish this. But I strongly recommend using classnames library to toggle classnames.

The workaround about removing class when another element ( apart from li elements we observe ) clicked could be solved by a click event listener.

For instance : 

import React, { Component }     from 'react'
import classNames               from 'classnames'

class DropDown extends Component {
  constructor(props){
    super(props)
    this.state = {
      activeSelected : ''
    }
    this.handleClick = this.handleClick.bind(this)
  }
  componentDidMount(){
    global.document.addEventListener( 'click', this.handleClick, false )
  }
  componentWillUnmount(){
    global.document.removeEventListener( 'click', this.handleClick, false )
  }
  handleClick(event){
    if( event.target.className.includes('not-changing-css-class') && 
        this.state.activeSelected !== '' 
    ) this.setState( { activeSelected : '' } )
  }
  render(){
    let { activeSelected } = this.state
    return (
      <ul>
        <li 
          className={ classNames({
            'not-changing-css-class' : true,
            'selected' : activeSelected === 'item1'
          }) } 
          onClick={ event => this.setState({ activeSelected : activeSelected === 'item1' ? '' : 'item1' }) }
        >
          Item 1
        </li>
        <li 
          className={ classNames({
            'not-changing-css-class' : true,
            'selected' : activeSelected === 'item2'
          }) } 
          onClick={ event => this.setState({ activeSelected : activeSelected === 'item2' ? '' : 'item2' }) }
        >
          Item 2
        </li>
        <li 
          className={ classNames({
            'not-changing-css-class' : true,
            'selected' : activeSelected === 'item3'
          }) } 
          onClick={ event => this.setState({ activeSelected : activeSelected === 'item3' ? '' : 'item3' }) }
        >
          Item 3
        </li>
      </ul>
    )
  }
}

Upvotes: 3

harun
harun

Reputation: 1919

You can hold the selected element index (or ID if you use IDs) in the component state. You can use redux store if you think this state will be relevant to any other component in your app, but starting with state is simpler.

Once you have this state. You can check within your render() function whether a link is selected or not, by comparing with the component state. You would also update the selectedIndex whent the links are clicked.

A simple example to render the links could be as follows. Note that you can extract parts of these to be functions instead of using expressions in JSX.

```

allLinks.map( (link, index) => 
  <a href={link.target} className={this.state.selectedIndex === index ? 'selected' : null}/>
)

```

Upvotes: 0

Related Questions