简体   繁体   中英

How to toggle class on mousedown/mouseup in react component?

I'm trying to toggle a class in a react component where if a user has his mouse down on a button, then it'll add a class to that button and when the user has his mouseup, then it reverts back and removes the class from the button.

Following the explanations and examples in the official ReactJS docs here , I managed to put together the following snippet but it's not working and keeps throwing syntax errors on the console:

import React from 'react';

export class Buttons extends React.Component{

    toggleClass() {
        if this.classList.has('btnDown') {
            this.classList.remove('btnDown');
        } else {
            this.classList.add('btnDown');
        }
    }


    render(){
        return (
            <div className="btn-wrap">
                <button onMouseDown={(e) => this.toggleClass(e)} onMouseUp={(e) => this.toggleClass(e)} value = '1' >1</button>
                <button onMouseDown={(e) => this.toggleClass(e)} onMouseUp={(e) => this.toggleClass(e)} value = '2' >2</button>
                <button onMouseDown={(e) => this.toggleClass(e)} onMouseUp={(e) => this.toggleClass(e)} value = '3' >3</button>
                <button onMouseDown={(e) => this.toggleClass(e)} onMouseUp={(e) => this.toggleClass(e)} value = '4' >4</button>
                <button onMouseDown={(e) => this.toggleClass(e)} onMouseUp={(e) => this.toggleClass(e)} value = '5' >5</button>
                <button onMouseDown={(e) => this.toggleClass(e)} onMouseUp={(e) => this.toggleClass(e)} value = '6' >6</button>
            </div>
        )
    }
}

UPDATE:

I rewrote the component to use react's state using @TomaszMularczyk's answer below as a reference but now all the buttons are toggled instead of just the button clicked as can be seen here: https://jsfiddle.net/69z2wepo/183164/

You can make your own Button component

class Button extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      isDown: false
    };
  }

  toggleClass() {
    this.setState(prevState => ({ isDown: !prevState.isDown }));
  }  

  render() {
    const className = `${this.state.isDown ? "btnDown" : ""}`;
    return <button className={className} onMouseDown={e => this.toggleClass(e)} value={this.props.value} {...this.props}>{this.props.children}</button>
  }
}

And then use those instead of the html button -

<div className="btn-wrap">
    <Button value = '1'>1</Button>
    <Button value = '2'>2</Button>
    <Button value = '3'>3</Button>
    <Button value = '+' onClick={e => console.log('+ clicked')}>3</Button>
    <Button value = '-' onClick={e => console.log('- clicked')}>3</Button>
</div>

this will not be an HTML element, but React Object instance.

I would try more reactive way to achieve this and used state :

class Button extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      isDown: false
    };
  }

  toggleClass() {
    this.setState(prevState => ({ isDown: !prevState.isDown }));
  }

  render() {
    return (
      <button
        className={this.state.isDown ? "btnDown" : ""}
        onMouseDown={e => this.toggleClass(e)}
        onMouseUp={e => this.toggleClass(e)}
        {...this.props}
      />
    );
  }
}

export class Buttons extends React.Component {
  render() {
    return (
      <div className="btn-wrap">
        <Button value="1">1</Button>
        <Button value="2">2</Button>
        ....
      </div>
    );
  }
}

Instead of using this inside toggleClass , use the event object :

class Buttons extends React.Component {
    toggleClass(e) {
      if(e.target.classList.contains('btnDown')) {
        e.target.classList.remove('btnDown');
      } else {
        e.target.classList.add('btnDown');
      }
    }

    render() {
      return (
        <div className="btn-wrap">
          <button onMouseDown={(e) => this.toggleClass(e)} onMouseUp={(e) => this.toggleClass(e)} value = '1' >1</button>
          <button onMouseDown={(e) => this.toggleClass(e)} onMouseUp={(e) => this.toggleClass(e)} value = '2' >2</button>
          <button onMouseDown={(e) => this.toggleClass(e)} onMouseUp={(e) => this.toggleClass(e)} value = '3' >3</button>
        </div>
      )
    }
}

The css was taken from @Tomasz Mularczyk's answer.

jsfiddle

I rewrote your code a little bit, it should work properly now:

class Buttons extends React.Component {

     constructor(props) {
         super(props);
         this.state = {
         isDown: null
      };

  }

  pressButton = (i) => {
    this.setState({ isDown: i });
  };
  releaseButton = () => {
    this.setState({ isDown: null });
  };

  render() {
    const { isDown } = this.state;
    const buttons = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0, '+', '-'];

    return (
      <div className="btn-wrap">
        {buttons.map((button, i) => (
          <button
            className={`${isDown === i ? 'btnDown' : ''}`}
            onMouseDown={this.pressButton.bind(undefined, i)}
            onMouseUp={this.releaseButton}
            value={button}
          >
            {button}
          </button>
        ))}
      </div>
    )
  }
}

ReactDOM.render(
  <Buttons />,
  document.getElementById('container')
);`

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM