早期React多级折叠动效选项菜单思考与实现

折叠菜单或者叫多级选项菜单,用流行UI框架下的相关实现改改就好了,为什么要手写?因为手痒 啊,有个简单方法特别想印证一下效果。前面的选项框就不做了,就是你点击了用一个数组去收集id之类的数据。我主要实现一下效果。

1.首先模拟的数据结构:

js 复制代码
let [list, setList] = useState([{
    title: "父标题111",
    child: [{
      title: '子标题1111'
    }, {
      title: '子标题1112',
      child: [
        { title: '子标题11121' },
        {
          title: '子标题11122',
          child: [
            { title: "子标题111221" },
            { title: "子标题111222" },
            { title: "子标题111223" },
            { title: "子标题111224" },
            { title: "子标题111225" },
            { title: "子标题111226" },
          ]
        }
      ]
    }, {
      title: '子标题1113'
    }]
  },
  {
    title: "父标题222",
    child: [{
      title: '子标题2221'
    }]
  },
  { title: "父标题333" }
  ])

2.控制child显示隐藏可不是用display/visibility属性就行了,有动画效果要用transition和控制高度。用到的样式,一些想法都在代码注释里:

css 复制代码
*{
    margin: 0;
    padding: 0;
}
.uuu{  
    transition:0.5s;
    height:0;  //一开始子项的ul列表要隐藏
    overflow: hidden;
}
ul{
    padding-left:20px; 
}

3.视图起始

js 复制代码
function App() {
...//上面的模拟数据
 return (
    <List list={list} />
  )
}

4.List定义,设计上肯定是组件递归。

js 复制代码
function List(props) {
  return <ul className={[props.iso ? 'uuu' : '', 'out'].join(' ')} >
    {props.list.map((t, i) => {
      return <>
        <li key={i} >
          <div onClick={(e) => { t.child && an(e, t.child.length) }}>{t.title}</div>
          {t.child && <List list={t.child} iso={true}></List>}
        </li>
      </>
    })
    }
  </ul>
}

5.想法:点击title的div如果是显示子菜单,除了下一层ul要显示高度,它的所有上层都要增加这个高度,标识它可控外层的条件是增加了'out'这个class,父级没有这个class表示不是菜单本身不需要增加高度。

增加的iso={true}是什么,为什么视图起始没有?这个是控制子菜单一开始隐藏的,而最外层不需要隐藏。

6.核心代码。首先"展开"效果,上面有说,不仅本身高度展开,所有上层都要加这个高度:

js 复制代码
let lout = (e, l) => {
  if (e.className.indexOf('out') > -1) {
    e.style.height = e.clientHeight + l + 'px'
    lout(e.parentNode.parentNode, l)  
  } //点击的是div,它的上级是li,再上级才是ul。
}

7."折叠"效果,不仅折叠本身,它的所有子级都要折叠起来:

js 复制代码
let lin = (e) => {
  e.style.height = 0
  e.childNodes.forEach(_e => {
    if (_e.childNodes.length > 1) {
      lin(_e.childNodes[1])
    }
  });
}

为什么要判断>1并且childNodes取第二项?因为有第一项div层。

8.控制逻辑。

js 复制代码
let nHeight = 21

let an = (e, l) => {
  let dom = e.currentTarget.nextSibling
  let flag = dom.clientHeight == 0 ? false : true
  if (flag) {
    lout(dom, dom.clientHeight * (-1))
    lin(dom)
  } else {
    lout(dom, l * nHeight)
  }
}

如果当前菜单为折叠态,那就展开它和它的所有上层菜单;如果当前菜单是展开态,那不仅要折叠它和它的所有子级菜单,同时要把所有上层菜单也减少这个高度,也就是为什么要*(-1)。

相关推荐
进阶的鱼1 小时前
关于微前端框架wujie的一次企业级应用实践demo?
前端·vue.js·react.js
凯心1 小时前
React 中没有 v-model,如何优雅地处理表单输入
前端·vue.js·react.js
WYiQIU17 小时前
11月面了7.8家前端岗,兄弟们12月我先躺为敬...
前端·vue.js·react.js·面试·前端框架·飞书
Bigger20 小时前
🎨 用一次就爱上的图标定制体验:CustomIcons 实战
前端·react.js·icon
weixin79893765432...1 天前
React + Fastify + DeepSeek 实现一个简单的对话式 AI 应用
人工智能·react.js·fastify
用户600071819101 天前
【翻译】使用 React 19 操作构建可复用组件
react.js
禁止摆烂_才浅1 天前
Taro 小程序页面返回传参完整示例
react.js·微信小程序·taro
还是大剑师兰特1 天前
React面试题及详细答案150道(51-60)
react.js·react面试题·大剑师
10share1 天前
React组件间通信
react.js
是一碗螺丝粉1 天前
React Native 运行时深度解析
前端·react native·react.js