最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to create collapse expand list in react - Stack Overflow

programmeradmin1浏览0评论

I try to create a collapse and expand side menu in React (v 16.5) with the following criteria -

On page load first item (Circulars) will be in expanded view. Any of one item can expand at a time, like, if user clicks on the second item (Specifications), the first item will collapse. I also want some CSS animation during collapse/expand transaction , like smoothly down/up the body section of each item and change the arrow icons. My approach is to add/remove a CSS class on each item (sidebar-nav-menu-item) dynamically, like -

sidebar-nav-menu-item item-active

So, when a item was in expanded view it should be like above class and remove item-active when its in collapse view. By default, the body divs (sidebar-nav-menu-item-body) should be hidden through CSS when the item in a collapse mode.

import React, { Component } from 'react';

className SidebarNavs extends React.Component{
    constructor(props) {
       super(props);
    }

    render() {
        return(
            <div className="sidebar-nav">
                <div className="sidebar-nav-menu">

                    <div className="sidebar-nav-menu-item" data-id="circulars">
                        <div className="sidebar-nav-menu-item-head" onClick={this.handleExpandCollaps}>
                            <div className="sidebar-nav-menu-item-head-title">Circulars</div>
                            <div className="sidebar-nav-menu-item-head-help">
                                <button type="button" className="btn-help" onClick={this.moreInfoClick}>View more info</button>
                            </div>
                            <div className="sidebar-nav-menu-item-head-icon">
                                <i className="fa fa-caret-down" aria-hidden="true"></i>
                            </div>
                        </div>
                        <div className="sidebar-nav-menu-item-body">BODY CONTENT HERE</div>
                    </div>

                    <div className="sidebar-nav-menu-item" data-id="specifications">
                        <div className="sidebar-nav-menu-item-head" onClick={this.handleExpandCollaps}>
                            <div className="sidebar-nav-menu-item-head-title">Specifications</div>
                            <div className="sidebar-nav-menu-item-head-help">
                                <button type="button" className="btn-help" onClick={this.moreInfoClick}>View more info</button>
                            </div>
                            <div className="sidebar-nav-menu-item-head-icon">
                                <i className="fa fa-caret-down" aria-hidden="true"></i>
                            </div>
                        </div>
                        <div className="sidebar-nav-menu-item-body">BODY CONTENT HERE</div>
                    </div>


                    <div className="sidebar-nav-menu-item"  data-id="wo">
                        <div className="sidebar-nav-menu-item-head" onClick={this.handleExpandCollaps}>
                            <div className="sidebar-nav-menu-item-head-title">Work Orders</div>
                            <div className="sidebar-nav-menu-item-head-help">
                                <button type="button" className="btn-help" onClick={this.moreInfoClick}>View more info</button>
                            </div>
                            <div className="sidebar-nav-menu-item-head-icon">
                                <i className="fa fa-caret-down" aria-hidden="true"></i>
                            </div>
                        </div>
                        <div className="sidebar-nav-menu-item-body">BODY CONTENT HERE</div>
                    </div>

                </div>
            </div>
        )
    }
}

export default SidebarNavs;

CSS:

.sidebar-nav-menu-item{
   display:block;
}
.sidebar-nav-menu-item-body{
   display:none;
}
.sidebar-nav-menu-item.item-active .sidebar-nav-menu-item-body{
   display:block;
}

I try to create a collapse and expand side menu in React (v 16.5) with the following criteria -

On page load first item (Circulars) will be in expanded view. Any of one item can expand at a time, like, if user clicks on the second item (Specifications), the first item will collapse. I also want some CSS animation during collapse/expand transaction , like smoothly down/up the body section of each item and change the arrow icons. My approach is to add/remove a CSS class on each item (sidebar-nav-menu-item) dynamically, like -

sidebar-nav-menu-item item-active

So, when a item was in expanded view it should be like above class and remove item-active when its in collapse view. By default, the body divs (sidebar-nav-menu-item-body) should be hidden through CSS when the item in a collapse mode.

import React, { Component } from 'react';

className SidebarNavs extends React.Component{
    constructor(props) {
       super(props);
    }

    render() {
        return(
            <div className="sidebar-nav">
                <div className="sidebar-nav-menu">

                    <div className="sidebar-nav-menu-item" data-id="circulars">
                        <div className="sidebar-nav-menu-item-head" onClick={this.handleExpandCollaps}>
                            <div className="sidebar-nav-menu-item-head-title">Circulars</div>
                            <div className="sidebar-nav-menu-item-head-help">
                                <button type="button" className="btn-help" onClick={this.moreInfoClick}>View more info</button>
                            </div>
                            <div className="sidebar-nav-menu-item-head-icon">
                                <i className="fa fa-caret-down" aria-hidden="true"></i>
                            </div>
                        </div>
                        <div className="sidebar-nav-menu-item-body">BODY CONTENT HERE</div>
                    </div>

                    <div className="sidebar-nav-menu-item" data-id="specifications">
                        <div className="sidebar-nav-menu-item-head" onClick={this.handleExpandCollaps}>
                            <div className="sidebar-nav-menu-item-head-title">Specifications</div>
                            <div className="sidebar-nav-menu-item-head-help">
                                <button type="button" className="btn-help" onClick={this.moreInfoClick}>View more info</button>
                            </div>
                            <div className="sidebar-nav-menu-item-head-icon">
                                <i className="fa fa-caret-down" aria-hidden="true"></i>
                            </div>
                        </div>
                        <div className="sidebar-nav-menu-item-body">BODY CONTENT HERE</div>
                    </div>


                    <div className="sidebar-nav-menu-item"  data-id="wo">
                        <div className="sidebar-nav-menu-item-head" onClick={this.handleExpandCollaps}>
                            <div className="sidebar-nav-menu-item-head-title">Work Orders</div>
                            <div className="sidebar-nav-menu-item-head-help">
                                <button type="button" className="btn-help" onClick={this.moreInfoClick}>View more info</button>
                            </div>
                            <div className="sidebar-nav-menu-item-head-icon">
                                <i className="fa fa-caret-down" aria-hidden="true"></i>
                            </div>
                        </div>
                        <div className="sidebar-nav-menu-item-body">BODY CONTENT HERE</div>
                    </div>

                </div>
            </div>
        )
    }
}

export default SidebarNavs;

CSS:

.sidebar-nav-menu-item{
   display:block;
}
.sidebar-nav-menu-item-body{
   display:none;
}
.sidebar-nav-menu-item.item-active .sidebar-nav-menu-item-body{
   display:block;
}
Share Improve this question edited Sep 22, 2019 at 10:05 ravibagul91 20.8k6 gold badges38 silver badges61 bronze badges asked Sep 21, 2019 at 13:15 Daniel SmithDaniel Smith 1,7444 gold badges30 silver badges64 bronze badges 2
  • That would be great as I need the body part of each should show smoothly and the icon on header, it should be up-arrrow when its expanded. – Daniel Smith Commented Sep 22, 2019 at 3:39
  • fa-caret-down should be fa-caret-up – Daniel Smith Commented Sep 22, 2019 at 3:40
Add a ment  | 

2 Answers 2

Reset to default 1

You should use a state variable to show your collapsiable item active / in-active.

I modified your code a bit to fit it into your requirements.

class App extends Component {
  constructor() {
    super();
    this.state = {
        activeCollapse: 'circulars'
    };
  }

  handleExpandCollaps = (name) => {
    if (this.state.activeCollapse === name) {
        this.setState({ activeCollapse: '' })
    } else {
        this.setState({ activeCollapse: name })
    }
  }

  moreInfoClick = (e) => {
    e.stopPropagation();
    console.log("clicked");
  }
  render() {
    return (
      <div>
        <div className="sidebar-nav">
          <div className="sidebar-nav-menu">

            <div className={`sidebar-nav-menu-item ${this.state.activeCollapse === "circulars" ? 'item-active' : ''}`} onClick={() => this.handleExpandCollaps("circulars")} data-id="circulars" >
              <div className="sidebar-nav-menu-item-head">
                <span className="sidebar-nav-menu-item-head-title">Circulars</span>
                <span className="sidebar-nav-menu-item-head-help">
                  <button type="button" className="btn-help" onClick={this.moreInfoClick}>View more info</button>
                </span>
              </div>
              <div className="sidebar-nav-menu-item-body">BODY CONTENT HERE</div>
            </div>

            <div className={`sidebar-nav-menu-item ${this.state.activeCollapse === "specifications" ? 'item-active' : ''}`} onClick={() => this.handleExpandCollaps("specifications")} data-id="specifications">
              <div className="sidebar-nav-menu-item-head">
                <span className="sidebar-nav-menu-item-head-title">Specifications</span>
                <span className="sidebar-nav-menu-item-head-help">
                  <button type="button" className="btn-help" onClick={this.moreInfoClick}>View more info</button>
                </span>
              </div>
              <div className="sidebar-nav-menu-item-body">BODY CONTENT HERE</div>
            </div>


            <div className={`sidebar-nav-menu-item ${this.state.activeCollapse === "wo" ? 'item-active' : ''}`} onClick={() => this.handleExpandCollaps("wo")} data-id="wo">
              <div className="sidebar-nav-menu-item-head">
                <span className="sidebar-nav-menu-item-head-title">Work Orders</span>
                <span className="sidebar-nav-menu-item-head-help">
                  <button type="button" className="btn-help" onClick={this.moreInfoClick}>View more info</button>
                </span>
              </div>
              <div className="sidebar-nav-menu-item-body">BODY CONTENT HERE</div>
            </div>

          </div>
        </div>
      </div>
    );
  }
}

Note: I have used CSS for font-awesome icons. Hope you have added font-awesome

Demo

To do that I would use React.useState, since its a small state to control and to animate I would use CSS:

The ponent would look like this:

function SidebarNavs() {
  const [activeItem, setActiveItem] = React.useState(1);

  return (
    <div className="sidebar-nav">
      <div className="sidebar-nav-menu">
        <SidebarItem
          title="Circulars"
          setActiveItem={setActiveItem}
          index={1}
          activeItem={activeItem}
        >
          Sidebar Content Here
        </SidebarItem>

        <SidebarItem
          title="Specifications"
          setActiveItem={setActiveItem}
          index={2}
          activeItem={activeItem}
        >
          Sidebar Content Here
        </SidebarItem>

        <SidebarItem
          title="Specifications"
          setActiveItem={setActiveItem}
          index={3}
          activeItem={activeItem}
        >
          Work Orders
        </SidebarItem>
      </div>
    </div>
  );
}

function SidebarItem({ title, children, setActiveItem, activeItem, index }) {
  const expanded = activeItem === index;
  const cls = "sidebar-nav-menu-item " + (expanded ? "item-active" : "");
  return (
    <div className={cls}>
      <div className="sidebar-nav-menu-item-head">
        <div className="sidebar-nav-menu-item-head-title">{title}</div>
        <div className="sidebar-nav-menu-item-head-help">
          <button
            type="button"
            className="btn-help"
            onClick={() => setActiveItem(index)}
          >
            View more info
          </button>
        </div>
        <div className="sidebar-nav-menu-item-head-icon">
          <i className="fa fa-caret-down" aria-hidden="true" />
        </div>
      </div>
      <div className="sidebar-nav-menu-item-body">{children}</div>
    </div>
  );
}

The CSS would look like this:

.sidebar-nav-menu-item {
  border: 1px solid #CCC;
  margin-bottom: 20px;
}
.sidebar-nav-menu-item .sidebar-nav-menu-item-body {
  overflow: hidden;
  max-height: 0;
  transition: all linear 0.5s;
}

.sidebar-nav-menu-item.item-active .sidebar-nav-menu-item-body {
  max-height: 100px;
  transition: all linear 0.5s 0.3s;
}

发布评论

评论列表(0)

  1. 暂无评论