原生js生成无限级树形菜单

设计思路:

要生成菜单的源数据往往是一个树形数据结构(若不是也可以转换成树形结构),(那我们一起写博客吧)因为源数据结构和目标菜单结构都为树形结构,所以其实我们要做的就是数据结构的转译,即将js树形数据转换为 ul, li 拼接成的树形菜单。在这里我们通过树的深度优先遍历方式来完成这次转义操作。

 

结构(转义)映射关系说明:

迭代树形数据时,树形结构数据的每个同层级别的每条数据转换成一个 LI标签包裹的菜单项(添加class为menu-item标识),当遇到childrens项时,当前项数据用 LI 标签包裹(添加class为menu标识),同时在该LI标签后面添加UL标签作为下一级菜单包裹项,迭代childrens作为上述UL标签的子菜单项,对childrens子元素的操作同上述描述的操作。

简单总结就是树的每条数据都转换为LI标签内容,每个childrens都转换为UL标签。

 

以下我们来完成对上述描述代码实现

1,js树形数据

  1 var menuList = [
  2     {
  3         name: '音程比较',
  4         childrens: [
  5             {
  6                 name: '比较纯音程',
  7             },
  8             {
  9                 name: '比较不完全和协音程',
 10             },
 11             {
 12                 name: '比较不协和音程',
 13                 childrens: [
 14                     {
 15                         name: '大二度&小七度-上行',
 16                         intervalId: 112,
 17                         questionType: "bj",
 18                     },
 19                     {
 20                         name: '大二度&小七度-下行',
 21                         intervalId: 113,
 22                         questionType: "bj",
 23                     },
 24                 ],
 25             },
 26             {
 27                 name: '比较跨两个八度的复合音程',
 28             }
 29         ]
 30     },
 31     {
 32         name: '音程辨认',
 33         childrens: [
 34             {
 35                 name: '辨认小二度和大二度音程',
 36                 childrens: [
 37                     {
 38                         name: '分辨小二度和大二度音程-上行',
 39                         intervalId: 1,
 40                         questionType: "br",
 41                     },
 42                     {
 43                         name: '分辨小二度和大二度音程-下行',
 44                         intervalId: 2,
 45                         questionType: "br",
 46                     },
 47                     {
 48                         name: '分辨小二度和大二度音程-和声',
 49                         intervalId: 3,
 50                         questionType: "br",
 51                     },
 52                     {
 53                         name: '分辨小二度和大二度音程-上行下行',
 54                         intervalId: 4,
 55                         questionType: "br",
 56                     },
 57                     {
 58                         name: '分辨小二度和大二度音程-上行和声',
 59                         intervalId: 5,
 60                         questionType: "br",
 61                     },
 62                     {
 63                         name: '分辨小二度和大二度音程-下行和声',
 64                         intervalId: 6,
 65                         questionType: "br",
 66                     }
 67                 ]
 68             },
 69             {
 70                 name: '辨认小三度和大三度的音程',
 71                 childrens: [
 72                     {
 73                         name: "分辨小三度和大三度音程-上行",
 74                         intervalId: 7,
 75                         questionType: "br",
 76                     },
 77                     {
 78                         name: "分辨小三度和大三度音程-下行",
 79                         intervalId: 8,
 80                         questionType: "br",
 81                     },
 82                     {
 83                         name: "分辨小三度和大三度音程-和声",
 84                         intervalId: 9,
 85                         questionType: "br",
 86                     },
 87                     {
 88                         name: "分辨小三度和大三度音程-上行下行",
 89                         intervalId: 10,
 90                         questionType: "br",
 91                     },
 92                     {
 93                         name: "分辨小三度和大三度音程-上行和声",
 94                         intervalId: 11,
 95                         questionType: "br",
 96                     },
 97                     {
 98                         name: "分辨小三度和大三度音程-下行和声",
 99                         intervalId: 12,
100                         questionType: "br",
101                     }
102                 ]
103             },
104         ]
105     },
106 ]
treeData

2,迭代生成树形菜单

 1         // 生成dom tree
 2         function generateDomTree(treeData, config = {
 3             label: 'name',
 4             childrens: 'childrens'
 5         }) {
 6             var label = config.label // 要显示的字段名
 7             var childrensKey = config.childrens // 子节点字段名
 8             var container = generateDomEle('ul')
 9             /* 
10              fragment 菜单容器
11              menuList 待遍历菜单
12              show 展开菜单
13             */
14             function deep(fragment, menuList, show) {
15                 for (const menu of menuList) {
16                     var liDom = generateDomEle('li', {
17                         innerText: menu[label]
18                     })
19                     var childrens = menu[childrensKey]
20                     // 子节点存在
21                     if (childrens && childrens.length) {
22                         fragment.appendChild(liDom) // 插入节点
23                         var urlDom = generateDomEle('ul') // 生成下一级菜单
24                         liDom.classList.add('menu') // 添加菜单标识
25                         if (!show) {
26                             // 关闭菜单
27                             liDom.classList.add('close')
28                         }
29                         fragment.appendChild(urlDom)
30 
31                         deep(urlDom, childrens, show) // 迭代
32                     } else {
33                         liDom.classList.add('menu-item') // 添加菜单项标识
34                         fragment.appendChild(liDom)
35                     }
36                 }
37             }
38             deep(container, treeData, false)
39             return container
40         }
generateDomTree

3,给菜单添加点击展开关闭操作

 1         var container = generateDomTree(treeData)
 2         container.addEventListener('click', function (e) {
 3             var target = e.target
 4             // 点中 li
 5             if (target.tagName == 'LI') {
 6                 // 点中菜单
 7                 if (target.classList.contains('menu')) {
 8                     target.classList.toggle('close') // 如果关闭则展开,展开则关闭
 9                 } else {
10                     //做你想做的
11                 }
12             }
13         }, false)
open or close

4,相关代码解释说明

4-1,代码中用到的自定义方法代码片段

1 // 生成dom节点
2 function generateDomEle(tagName, property) {
3     var property = property || {}
4     var ele = document.createElement(tagName)
5     for (var key in property) {
6         ele[key] = property[key]
7     }
8     return ele
9 }
snippet code

4-2,效果图

4-3,生成的dom结构

 

 

 

 

@萍2樱释 ღ( ´・ᴗ・` )

 

posted @ 2020-07-26 18:32  不忘初心dbsdxq  阅读(2634)  评论(0编辑  收藏  举报
TOP 文章底部