You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React可展开侧边菜单动画问题:独立控制与图标切换修复

React可折叠侧边菜单修复方案

问题根源

  1. 全局状态联动:用单个open变量控制所有菜单,点击任意一个会触发所有菜单同步展开/收起
  2. 图标定位错误:.nav_icons用绝对定位,但父元素.sidebarMenu未设置position: relative,所有图标会相对于页面定位,堆在同一位置
  3. 无平滑动画:display: none/block不支持CSS过渡,无法实现平滑展开/收起效果

修复后的React代码

import { useState } from 'react';
// 请确保已正确导入图标组件和资源
import Home from './icons/Home';
import GroupAdd from './icons/GroupAdd';
import PostAdd from './icons/PostAdd';
import collapse_icon from './icons/collapse.png';
import expand_icon from './icons/expand.png';

function Sidebar() {
  // 用对象存储每个菜单的独立展开状态
  const [menuStates, setMenuStates] = useState({
    userCreation: true,
    mainReport: true
  });

  // 通用切换函数,仅修改对应菜单的状态
  const toggleMenu = (menuKey) => {
    setMenuStates(prev => ({
      ...prev,
      [menuKey]: !prev[menuKey]
    }));
  };

  return (
    <div className="sidebar">
      <div className="sidebarWrapper">
        <div className="sidebarMenu">
          {/* 统一标题和图标点击区域 */}
          <div className="sidebarTitleContainer" onClick={() => toggleMenu('userCreation')}>
            <h3 className="sidebarTitle">User Creation</h3>
            <img
              src={menuStates.userCreation ? collapse_icon : expand_icon}
              alt="Expand"
              className="nav_icons"
            />
          </div>

          <ul
            className={`sidebarList ${menuStates.userCreation ? 'show_menu' : 'hide_menu'}`}
          >
            <li className="sidebarListItem active">
              <Home className="sidebarIcon" />
              <span className="titleText">Home</span>
            </li>
            <li className="sidebarListItem">
              <GroupAdd className="sidebarIcon" />
              <span className="titleText"> New users</span>
            </li>
            <li className="sidebarListItem">
              <PostAdd className="sidebarIcon" />
              <span className="titleText">Home</span>
            </li>
          </ul>
        </div>
        <div className="sidebarMenu">
          <div className="sidebarTitleContainer" onClick={() => toggleMenu('mainReport')}>
            <h3 className="sidebarTitle">Main Report</h3>
            <img
              src={menuStates.mainReport ? collapse_icon : expand_icon}
              alt="Expand"
              className="nav_icons"
            />
          </div>

          <ul
            className={`sidebarList ${menuStates.mainReport ? 'show_menu' : 'hide_menu'}`}
          >
            <li className="sidebarListItem active">
              <Home className="sidebarIcon" />
              <span className="titleText">Sales Report</span>
            </li>
            <li className="sidebarListItem">
              <GroupAdd className="sidebarIcon" />
              <span className="titleText">Orders Report</span>
            </li>
            <li className="sidebarListItem">
              <PostAdd className="sidebarIcon" />
              <span className="titleText">Stock Report</span>
            </li>
          </ul>
        </div>
      </div>
    </div>
  );
}

export default Sidebar;

修复后的CSS代码

.sidebar {
  flex: 1;
  height: calc(100vh - 50px);
  background-color: rgb(251, 251, 255);
  position: sticky;
  top: 50px;
  overflow-y: scroll;
}

.sidebarWrapper {
  padding: 10px;
  color: #555;
}

.sidebarMenu {
  margin-bottom: 10px;
  cursor: pointer;
  /* 关键:添加相对定位,让图标相对于当前菜单容器定位 */
  position: relative;
}

/* 新增标题容器,统一点击区域 */
.sidebarTitleContainer {
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
}

.sidebarTitle {
  font-size: 15px;
  color: white;
  background-color: #646464;
  padding: 4px;
  border-top-left-radius: 8px;
  border-top-right-radius: 8px;
  width: 100%;
  text-align: center;
  margin: 0;
}

.titleText {
  color: #990000;
  font-size: 14px;
}

.sidebarList {
  list-style: none;
  padding: 5px;
  margin: 0;
  /* 用max-height实现平滑过渡,设置足够大的默认值 */
  max-height: 500px;
  opacity: 1;
  transition: max-height 0.3s ease, opacity 0.3s ease;
  overflow: hidden;
}

.sidebarListItem {
  padding: 5px;
  cursor: pointer;
  display: flex;
  align-items: center;
  border-radius: 10px;
}

.sidebarListItem.active,
.sidebarListItem:hover {
  background-color: rgb(240, 240, 255);
}

.sidebarIcon {
  margin-right: 5px;
  font-size: 20px !important;
}

/* 保留原媒体查询逻辑 */
@media only screen and (max-width: 500px) {
  .sidebarListItem {
    display: grid;
  }
  .titleText {
    font-size: 11px;
  }
  .sidebarIcon {
    margin-right: 1px;
    font-size: 12px !important;
  }
  .sidebarTitle {
    font-size: 12px;
  }
}

@media only screen and (min-width: 501px) and (max-width: 700px) {
  .sidebarListItem {
    display: grid;
  }
  .titleText {
    font-size: 13px;
  }
  .sidebarIcon {
    margin-right: 1px;
    font-size: 17px !important;
  }
  .sidebarTitle {
    font-size: 12px;
  }
}

@media only screen and (min-width: 701px) and (max-width: 992px) {
  .sidebarListItem {
    display: grid;
  }
  .titleText {
    font-size: 13px;
  }
  .sidebarIcon {
    margin-right: 1px;
    font-size: 19px !important;
  }
  .sidebarTitle {
    font-size: 14px;
  }
}

.nav_icons {
  width: 15px;
  height: 15px;
  background-color: #fafafa;
  filter: invert(76%) sepia(30%) saturate(3461%) hue-rotate(321deg) brightness(98%) contrast(91%);
  position: absolute;
  top: 50%;
  right: 15px;
  /* 垂直居中图标 */
  transform: translateY(-50%);
}

/* 替换display为max-height+opacity实现平滑过渡 */
.hide_menu {
  max-height: 0;
  opacity: 0;
  padding: 0 5px;
}

.show_menu {
  max-height: 500px;
  opacity: 1;
  padding: 5px;
}

关键修改说明

  1. 状态独立管理:改用对象存储每个菜单的展开状态,每个菜单仅触发自身的切换逻辑
  2. 图标定位修复:给.sidebarMenu添加position: relative,并调整图标为垂直居中,避免重叠
  3. 平滑动画实现:用max-height和opacity替代display属性,支持CSS过渡动画
  4. 点击区域优化:新增.sidebarTitleContainer,让标题和图标区域均可触发菜单切换,提升交互体验

内容的提问来源于stack exchange,提问作者Denis Morgan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 02:50:27