admin管理员组

文章数量:1405508

I am Using GatsbyJs and am trying to acplish a active class toggle. I have a simple Component posed of a anchor tag with a span inside. I want to change some css by toggling a active class on the anchor when it is clicked. Here is my code so far, I am also using styled-ponents. I tried some vanilla js in the broken code section below, that obviously didn't work hence broken code.

Thanks in advance

styles

const Menu = styled.a`
  position: absolute;
  cursor: pointer; padding: 10px 35px 16px 0px;
  span, span:before, span:after{
    cursor: pointer;
    border-radius: 1px;
    height: 5px;
    width: 35px;
    background: #000000;
    position: absolute;
    display: block;
    content: '';
    transition: all 200ms ease-in-out;
  }
  span:before{
    top: -10px; 
  }
  span:after{
    bottom: -10px; 
  }
  .active span{
    background-color: transparent;
  }
  .active span:before, .active span:after{
    top:0;
  }
  .active span:before{
    transform: rotate(45deg);
  }
  .active span:after{
    transform: rotate(-45deg);
  }
`

ponent

const TemplateWrapper = ({ children }) => (
  <div>
    <Wrapper>
      <Menu id="nav-toggle" className="menu"><span></span></Menu>
      {children()}
    </Wrapper>       
  </div>
)

TemplateWrapper.propTypes = {
  children: PropTypes.func,
}

export default TemplateWrapper

broken code

document.querySelector( "#nav-toggle" )
  .addEventListener( "click", function() {
    this.classList.toggle( "active" );
  });

This is the html that is rendered

<a class="menu sc-bwzfXH SqHLW" id="nav-toggle"><span></span></a>

I am Using GatsbyJs and am trying to acplish a active class toggle. I have a simple Component posed of a anchor tag with a span inside. I want to change some css by toggling a active class on the anchor when it is clicked. Here is my code so far, I am also using styled-ponents. I tried some vanilla js in the broken code section below, that obviously didn't work hence broken code.

Thanks in advance

styles

const Menu = styled.a`
  position: absolute;
  cursor: pointer; padding: 10px 35px 16px 0px;
  span, span:before, span:after{
    cursor: pointer;
    border-radius: 1px;
    height: 5px;
    width: 35px;
    background: #000000;
    position: absolute;
    display: block;
    content: '';
    transition: all 200ms ease-in-out;
  }
  span:before{
    top: -10px; 
  }
  span:after{
    bottom: -10px; 
  }
  .active span{
    background-color: transparent;
  }
  .active span:before, .active span:after{
    top:0;
  }
  .active span:before{
    transform: rotate(45deg);
  }
  .active span:after{
    transform: rotate(-45deg);
  }
`

ponent

const TemplateWrapper = ({ children }) => (
  <div>
    <Wrapper>
      <Menu id="nav-toggle" className="menu"><span></span></Menu>
      {children()}
    </Wrapper>       
  </div>
)

TemplateWrapper.propTypes = {
  children: PropTypes.func,
}

export default TemplateWrapper

broken code

document.querySelector( "#nav-toggle" )
  .addEventListener( "click", function() {
    this.classList.toggle( "active" );
  });

This is the html that is rendered

<a class="menu sc-bwzfXH SqHLW" id="nav-toggle"><span></span></a>
Share Improve this question edited Apr 5, 2018 at 1:14 Anders Kitson asked Apr 4, 2018 at 22:01 Anders KitsonAnders Kitson 1,5456 gold badges44 silver badges115 bronze badges 5
  • 1 Can you post the react code where #nav-toggle gets rendered ? – Tareq Commented Apr 4, 2018 at 22:04
  • Ok I added what is rendered above. Also I fixed something in the menu ponent it was missing the nav-toggle id. However the broken code section doesn't even allow the app to run. If I delete it the app runs and everythingrenders fine. – Anders Kitson Commented Apr 4, 2018 at 22:10
  • I see the problem, you need to add "&" before ".active span" in your styled ponent. like &.active span ... – m0rjjj Commented Apr 4, 2018 at 22:12
  • And what's the error that you get? – m0rjjj Commented Apr 4, 2018 at 22:13
  • @m0rjjj the site pletely crashes when I include the broken just, so the css part is working however I haven't tested add the active class manually so you might be partly right. – Anders Kitson Commented Apr 4, 2018 at 22:21
Add a ment  | 

2 Answers 2

Reset to default 2

Okay, you can take a look here REACT - toggle class onclick . There are several answers that show the way how you can achieve that, but things get messier when you want to use more than one class.

I like this package to handle the cases where you deal with styling with multiple classes based on the props or state: https://github./JedWatson/classnames.

I simplified the styles for the sake of clarity of this answer and assumed that the menu is a global class, you want to apply as well.

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import styled from 'styled-ponents';

const Wrapper = styled.div``;

const Menu = styled.a`
  span {
    color: green;
  }
  &.active span {
    color: blue;
  }
`;

class TemplateWrapper extends Component {
  state = {
    isActive: false
  };

  handleClick = () => {
    this.setState(state => ({ isActive: !state.isActive }));
  };

  render() {
    const menuClass = classNames({
      menu: true,
      active: this.state.isActive
    });

    return (
      <Wrapper>
        <Menu id="nav-toggle" className={menuClass} onClick={this.handleClick}>
          <span>Test</span>
        </Menu>
        {this.props.children}
      </Wrapper>
    );
  }
}

TemplateWrapper.propTypes = {
  children: PropTypes.node
};

TemplateWrapper.defaultProps = {
  children: null
};

export default TemplateWrapper;

Notice the ampersand (&) before .active. As stated in the docs, styled ponents supports all of CSS plus nesting.

Ampersands (&) get replaced by our generated, unique classname for that styled ponent, making it easy to have plex logic.

However, you can achieve the same effect without classnames by fully utilizing styled ponents functionality. Take a look at this section of the documentation.

A few changes needed:

const Menu = styled.a`
  span {
    color: ${props => (props.active ? 'blue' : 'green')};
  }
`;

The render method would look like that:

render() {
  return (
    <Wrapper>
      <Menu id="nav-toggle" className="menu" active={this.state.isActive} onClick={this.handleClick}>
        <span>Test</span>
      </Menu>
      {this.props.children}
    </Wrapper>
  );
}

You can use ponent state to mange this. Code will look like:

Updated: Moved logic to Menu ponent as it makes more sense to place it there. Below working example

class Menu extends React.Component {

  constructor(props) {
    super(props);
    
    this.menuClick = this.menuClick.bind(this);
    this.state = {
      menuClass: '',
    }
  }
  
  menuClick(e) {
    const menuClass = this.state.menuClass === '' ? 'active' : '';
    this.setState({ menuClass });
  }
  
  render() {
    const {children, id} = this.props;
    const menuClassName = `menu sc-bwzfXH SqHLW nav-toggle ${this.state.menuClass}`;
    
    return (<a className={menuClassName} id={id} onClick={this.menuClick}>{children}</a>);

  }
}

ReactDOM.render(<Menu><span>Test link</span></Menu>,  document.getElementById('menu'))
.menu {
  font-weight: bolder;
  color: blue;
 }
 
.active{
  color:red;
}
<script src="https://cdnjs.cloudflare./ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare./ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="menu" />

本文标签: javascriptReact toggle active class on click of anchor using Gatsby JsStack Overflow