原生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櫻釋 ღ( ´・ᴗ・` )

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM