树状目录导航

曾经有个项目,前端静态资源服务器的HTML文件全部在同一层目录中,文件名按模块以“-”分隔。随着项目的扩大,文件数不断增加,到最后多达200+个页面文件。在需要定位某个页面时,即便通过搜索,也是一件很头疼的事情。为此需要寻找一种更方便定位、更直观的方式。常见的文章、书籍的树状目录索引便是普遍的方式。

思路

首先,获取到所有header,利用jQuery的:header即可。然后对其进行遍历,将层级和父子关系等信息附加到节点上。因为是层级关系,这里有个默认的前提条件,上下级之间是连续的,也即下一节点是上一节点的直接子节点,或者是上一节点的任一父节点的兄弟节点。在此条件下,经过2层迭代,即可生成所需要节点数据。

然后,作为入口,对所有节点进行遍历,找到树根的H1节点,对其进行递归,遍历出属于该根节点的目录树。每个递归过程,判断当前节点的兄弟节点关系,并依据判断结果对当前节点进行格式化输出,将其附加到节点信息中。递归结束后,对当前节点进行输出,即为预期所需要的内容。

实现

根据上述思路,实现如下:

javascriptCopy code
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
function initTree ($docDom) { var nodesArr = [], i = 0, j = 0, $headers = $docDom.find(':header'); $.each($headers.prevObject, function (idx, v) { var $curHeader = $(v), curLevel = parseInt(v.tagName.charAt(1), 10), nodeObj = {}; nodeObj.name = $curHeader.attr('name'); nodeObj.text = $curHeader.html(); nodeObj.link = '<a href="#' + $curHeader.attr('name') + '">' + $curHeader.html() + '</a>'; nodeObj.level = curLevel; nodeObj.parent = ''; nodeObj.children = []; nodeObj.childrenIndex = []; nodeObj.index = idx; nodeObj.parentIndex = -1; nodeObj.padStr = ''; nodesArr.push(nodeObj); }); for ( i = 1; i < nodesArr.length; i++) { if (nodesArr[i].level === nodesArr[i - 1].level + 1) { nodesArr[i].parent = nodesArr[i - 1].name; nodesArr[i].parentIndex = nodesArr[i - 1].index; nodesArr[i - 1].children.push(nodesArr[i].name); nodesArr[i - 1].childrenIndex.push(nodesArr[i].index); } else if (nodesArr[i].level <= nodesArr[i - 1].level) { for ( j = i - 2; j >= 0; j--) { if (nodesArr[i].level === nodesArr[j].level + 1) { nodesArr[i].parent = nodesArr[j].name; nodesArr[i].parentIndex = nodesArr[j].index; nodesArr[j].children.push(nodesArr[i].name); nodesArr[j].childrenIndex.push(nodesArr[i].index); break; } } } } return nodesArr; } function hasSiblings (dataArr, curNode) { //1-父节点有兄弟节点,2-父节点无兄弟节点,3-父节点为根节点,4-当前节点为根节点 var result = 0; if (curNode.parentIndex > -1) { if (dataArr[curNode.parentIndex].parentIndex > -1) { result = (dataArr[dataArr[curNode.parentIndex].parentIndex].children.length > 1) ? 1 : 2; } else { result = 3; } } else { result = 4; } return result; } function isLastSibling (dataArr, curNode) { //1-父节点为最后一个兄弟节点,2-父节点非最后一个兄弟节点,3-父节点为根节点,4-当前节点为根节点 var childNodes, result; if (curNode.parentIndex > -1) { if (dataArr[curNode.parentIndex].parentIndex > -1) { childNodes = dataArr[dataArr[curNode.parentIndex].parentIndex].children; result = ($.inArray(dataArr[curNode.parentIndex].name, childNodes) === childNodes.length - 1) ? 1 : 2; } else { result = 3; } } else { result = 4; } return result; } function iterateChildren (dataArr, curNode) { $.each(curNode.childrenIndex, function (i, v) { var tmpParent = dataArr[v], siblingFlag, lastSiblingFlag; while (tmpParent.parentIndex > -1) { siblingFlag = hasSiblings(dataArr, tmpParent); lastSiblingFlag = isLastSibling(dataArr, tmpParent); if (siblingFlag === 1 && lastSiblingFlag === 2) { dataArr[v].padStr = '┆ ' + dataArr[v].padStr; } else if (siblingFlag === 1 && lastSiblingFlag === 1) { dataArr[v].padStr = ' ' + dataArr[v].padStr; } else if (siblingFlag === 2) { dataArr[v].padStr = ' ' + dataArr[v].padStr; } else if (siblingFlag === 3 || siblingFlag === 4) { break; } tmpParent = dataArr[tmpParent.parentIndex]; } if (i === curNode.childrenIndex.length - 1) { dataArr[v].padStr += '└┄'; } else { dataArr[v].padStr += '├┄'; } iterateChildren(dataArr, dataArr[v]); }); } function displayTree ($docDom) { var nodesArr = initTree($docDom); for ( i = 0; i < nodesArr.length; i++) { if (nodesArr[i].level === 1) { iterateChildren(nodesArr, nodesArr[i]); } console.log(nodesArr[i].padStr + nodesArr[i].link); } }
function initTree ($docDom) { var nodesArr = [], i = 0, j = 0, $headers = $docDom.find(':header'); $.each($headers.prevObject, function (idx, v) { var $curHeader = $(v), curLevel = parseInt(v.tagName.charAt(1), 10), nodeObj = {}; nodeObj.name = $curHeader.attr('name'); nodeObj.text = $curHeader.html(); nodeObj.link = '' + $curHeader.html() + ''; nodeObj.level = curLevel; nodeObj.parent = ''; nodeObj.children = []; nodeObj.childrenIndex = []; nodeObj.index = idx; nodeObj.parentIndex = -1; nodeObj.padStr = ''; nodesArr.push(nodeObj); }); for ( i = 1; i < nodesArr.length; i++) { if (nodesArr[i].level === nodesArr[i - 1].level + 1) { nodesArr[i].parent = nodesArr[i - 1].name; nodesArr[i].parentIndex = nodesArr[i - 1].index; nodesArr[i - 1].children.push(nodesArr[i].name); nodesArr[i - 1].childrenIndex.push(nodesArr[i].index); } else if (nodesArr[i].level <= nodesArr[i - 1].level) { for ( j = i - 2; j >= 0; j--) { if (nodesArr[i].level === nodesArr[j].level + 1) { nodesArr[i].parent = nodesArr[j].name; nodesArr[i].parentIndex = nodesArr[j].index; nodesArr[j].children.push(nodesArr[i].name); nodesArr[j].childrenIndex.push(nodesArr[i].index); break; } } } } return nodesArr; } function hasSiblings (dataArr, curNode) { //1-父节点有兄弟节点,2-父节点无兄弟节点,3-父节点为根节点,4-当前节点为根节点 var result = 0; if (curNode.parentIndex > -1) { if (dataArr[curNode.parentIndex].parentIndex > -1) { result = (dataArr[dataArr[curNode.parentIndex].parentIndex].children.length > 1) ? 1 : 2; } else { result = 3; } } else { result = 4; } return result; } function isLastSibling (dataArr, curNode) { //1-父节点为最后一个兄弟节点,2-父节点非最后一个兄弟节点,3-父节点为根节点,4-当前节点为根节点 var childNodes, result; if (curNode.parentIndex > -1) { if (dataArr[curNode.parentIndex].parentIndex > -1) { childNodes = dataArr[dataArr[curNode.parentIndex].parentIndex].children; result = ($.inArray(dataArr[curNode.parentIndex].name, childNodes) === childNodes.length - 1) ? 1 : 2; } else { result = 3; } } else { result = 4; } return result; } function iterateChildren (dataArr, curNode) { $.each(curNode.childrenIndex, function (i, v) { var tmpParent = dataArr[v], siblingFlag, lastSiblingFlag; while (tmpParent.parentIndex > -1) { siblingFlag = hasSiblings(dataArr, tmpParent); lastSiblingFlag = isLastSibling(dataArr, tmpParent); if (siblingFlag === 1 && lastSiblingFlag === 2) { dataArr[v].padStr = '┆ ' + dataArr[v].padStr; } else if (siblingFlag === 1 && lastSiblingFlag === 1) { dataArr[v].padStr = ' ' + dataArr[v].padStr; } else if (siblingFlag === 2) { dataArr[v].padStr = ' ' + dataArr[v].padStr; } else if (siblingFlag === 3 || siblingFlag === 4) { break; } tmpParent = dataArr[tmpParent.parentIndex]; } if (i === curNode.childrenIndex.length - 1) { dataArr[v].padStr += '└┄'; } else { dataArr[v].padStr += '├┄'; } iterateChildren(dataArr, dataArr[v]); }); } function displayTree ($docDom) { var nodesArr = initTree($docDom); for ( i = 0; i < nodesArr.length; i++) { if (nodesArr[i].level === 1) { iterateChildren(nodesArr, nodesArr[i]); } console.log(nodesArr[i].padStr + nodesArr[i].link); } }

调用

javascriptCopy code
  • 1
displayTree(...);
displayTree(...);

输入1:

xmlCopy code
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
<h1 name="t1">1</h1> <h2 name="t2">2</h2> <h3 name="t3">3</h3> <h2 name="t4">4</h2> <h3 name="t5">5</h3> <h4 name="t16">16</h4> <h5 name="t17">17</h5> <h6 name="t18">18</h6> <h3 name="t6">6</h3> <h2 name="t19">19</h2> <h1 name="t7">7</h1> <h2 name="t8">8</h2> <h3 name="t9">9</h3> <h3 name="t10">10</h3> <h3 name="t11">11</h3> <h2 name="t12">12</h2> <h2 name="t13">13</h2> <h3 name="t14">14</h3> <h1 name="t15">15</h1>

1

2

3

4

5

16

17
18

6

19

7

8

9

10

11

12

13

14

15

输出1:

htmlCopy code
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
<a href="#t1">1</a> ├┄<a href="#t2">2</a> ┆ └┄<a href="#t3">3</a> ├┄<a href="#t4">4</a> ┆ ├┄<a href="#t5">5</a> ┆ ┆ └┄<a href="#t16">16</a> ┆ ┆ └┄<a href="#t17">17</a> ┆ ┆ └┄<a href="#t18">18</a> ┆ └┄<a href="#t6">6</a> └┄<a href="#t19">19</a> <a href="#t7">7</a> ├┄<a href="#t8">8</a> ┆ ├┄<a href="#t9">9</a> ┆ ├┄<a href="#t10">10</a> ┆ └┄<a href="#t11">11</a> ├┄<a href="#t12">12</a> └┄<a href="#t13">13</a> └┄<a href="#t14">14</a> <a href="#t15">15</a>
1 ├┄2 ┆ └┄3 ├┄4 ┆ ├┄5 ┆ ┆ └┄16 ┆ ┆ └┄17 ┆ ┆ └┄18 ┆ └┄6 └┄19 7 ├┄8 ┆ ├┄9 ┆ ├┄10 ┆ └┄11 ├┄12 └┄13 └┄14 15

输入2:

xmlCopy code
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
<h1 name="t1">1</h1> <h2 name="t2">2</h2> <h3 name="t3">3</h3> <h4 name="t4">4</h4> <h5 name="t5">5</h5> <h6 name="t6">6</h6> <h6 name="t7">7</h6> <h5 name="t8">8</h5> <h6 name="t9">9</h6> <h6 name="t10">10</h6> <h4 name="t11">11</h4> <h5 name="t12">12</h5> <h6 name="t13">13</h6> <h6 name="t14">14</h6> <h5 name="t15">15</h5> <h6 name="t16">16</h6> <h6 name="t17">17</h6> <h3 name="t18">18</h3> <h4 name="t19">19</h4> <h5 name="t20">20</h5> <h6 name="t21">21</h6> <h6 name="t22">22</h6> <h5 name="t23">23</h5> <h6 name="t24">24</h6> <h6 name="t25">25</h6> <h4 name="t26">26</h4> <h5 name="t27">27</h5> <h6 name="t28">28</h6> <h6 name="t29">29</h6> <h5 name="t30">30</h5> <h6 name="t31">31</h6> <h6 name="t32">32</h6> <h2 name="t33">33</h2> <h3 name="t34">34</h3> <h4 name="t35">35</h4> <h5 name="t36">36</h5> <h6 name="t37">37</h6> <h6 name="t38">38</h6> <h5 name="t39">39</h5> <h6 name="t40">40</h6> <h6 name="t41">41</h6> <h4 name="t42">42</h4> <h5 name="t43">43</h5> <h6 name="t44">44</h6> <h6 name="t45">45</h6> <h5 name="t46">46</h5> <h6 name="t47">47</h6> <h6 name="t48">48</h6> <h3 name="t49">49</h3> <h4 name="t50">50</h4> <h5 name="t51">51</h5> <h6 name="t52">52</h6> <h6 name="t53">53</h6> <h5 name="t54">54</h5> <h6 name="t55">55</h6> <h6 name="t56">56</h6> <h4 name="t57">57</h4> <h5 name="t58">58</h5> <h6 name="t59">59</h6> <h6 name="t60">60</h6> <h5 name="t61">61</h5> <h6 name="t62">62</h6> <h6 name="t63">63</h6>

1

2

3

4

5
6
7
8
9
10

11

12
13
14
15
16
17

18

19

20
21
22
23
24
25

26

27
28
29
30
31
32

33

34

35

36
37
38
39
40
41

42

43
44
45
46
47
48

49

50

51
52
53
54
55
56

57

58
59
60
61
62
63

输出2(二叉树):

htmlCopy code
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
<a href="#t1">1</a> ├┄<a href="#t2">2</a> ┆ ├┄<a href="#t3">3</a> ┆ ┆ ├┄<a href="#t4">4</a> ┆ ┆ ┆ ├┄<a href="#t5">5</a> ┆ ┆ ┆ ┆ ├┄<a href="#t6">6</a> ┆ ┆ ┆ ┆ └┄<a href="#t7">7</a> ┆ ┆ ┆ └┄<a href="#t8">8</a> ┆ ┆ ┆ ├┄<a href="#t9">9</a> ┆ ┆ ┆ └┄<a href="#t10">10</a> ┆ ┆ └┄<a href="#t11">11</a> ┆ ┆ ├┄<a href="#t12">12</a> ┆ ┆ ┆ ├┄<a href="#t13">13</a> ┆ ┆ ┆ └┄<a href="#t14">14</a> ┆ ┆ └┄<a href="#t15">15</a> ┆ ┆ ├┄<a href="#t16">16</a> ┆ ┆ └┄<a href="#t17">17</a> ┆ └┄<a href="#t18">18</a> ┆ ├┄<a href="#t19">19</a> ┆ ┆ ├┄<a href="#t20">20</a> ┆ ┆ ┆ ├┄<a href="#t21">21</a> ┆ ┆ ┆ └┄<a href="#t22">22</a> ┆ ┆ └┄<a href="#t23">23</a> ┆ ┆ ├┄<a href="#t24">24</a> ┆ ┆ └┄<a href="#t25">25</a> ┆ └┄<a href="#t26">26</a> ┆ ├┄<a href="#t27">27</a> ┆ ┆ ├┄<a href="#t28">28</a> ┆ ┆ └┄<a href="#t29">29</a> ┆ └┄<a href="#t30">30</a> ┆ ├┄<a href="#t31">31</a> ┆ └┄<a href="#t32">32</a> └┄<a href="#t33">33</a> ├┄<a href="#t34">34</a> ┆ ├┄<a href="#t35">35</a> ┆ ┆ ├┄<a href="#t36">36</a> ┆ ┆ ┆ ├┄<a href="#t37">37</a> ┆ ┆ ┆ └┄<a href="#t38">38</a> ┆ ┆ └┄<a href="#t39">39</a> ┆ ┆ ├┄<a href="#t40">40</a> ┆ ┆ └┄<a href="#t41">41</a> ┆ └┄<a href="#t42">42</a> ┆ ├┄<a href="#t43">43</a> ┆ ┆ ├┄<a href="#t44">44</a> ┆ ┆ └┄<a href="#t45">45</a> ┆ └┄<a href="#t46">46</a> ┆ ├┄<a href="#t47">47</a> ┆ └┄<a href="#t48">48</a> └┄<a href="#t49">49</a> ├┄<a href="#t50">50</a> ┆ ├┄<a href="#t51">51</a> ┆ ┆ ├┄<a href="#t52">52</a> ┆ ┆ └┄<a href="#t53">53</a> ┆ └┄<a href="#t54">54</a> ┆ ├┄<a href="#t55">55</a> ┆ └┄<a href="#t56">56</a> └┄<a href="#t57">57</a> ├┄<a href="#t58">58</a> ┆ ├┄<a href="#t59">59</a> ┆ └┄<a href="#t60">60</a> └┄<a href="#t61">61</a> ├┄<a href="#t62">62</a> └┄<a href="#t63">63</a>
1 ├┄2 ┆ ├┄3 ┆ ┆ ├┄4 ┆ ┆ ┆ ├┄5 ┆ ┆ ┆ ┆ ├┄6 ┆ ┆ ┆ ┆ └┄7 ┆ ┆ ┆ └┄8 ┆ ┆ ┆ ├┄9 ┆ ┆ ┆ └┄10 ┆ ┆ └┄11 ┆ ┆ ├┄12 ┆ ┆ ┆ ├┄13 ┆ ┆ ┆ └┄14 ┆ ┆ └┄15 ┆ ┆ ├┄16 ┆ ┆ └┄17 ┆ └┄18 ┆ ├┄19 ┆ ┆ ├┄20 ┆ ┆ ┆ ├┄21 ┆ ┆ ┆ └┄22 ┆ ┆ └┄23 ┆ ┆ ├┄24 ┆ ┆ └┄25 ┆ └┄26 ┆ ├┄27 ┆ ┆ ├┄28 ┆ ┆ └┄29 ┆ └┄30 ┆ ├┄31 ┆ └┄32 └┄33 ├┄34 ┆ ├┄35 ┆ ┆ ├┄36 ┆ ┆ ┆ ├┄37 ┆ ┆ ┆ └┄38 ┆ ┆ └┄39 ┆ ┆ ├┄40 ┆ ┆ └┄41 ┆ └┄42 ┆ ├┄43 ┆ ┆ ├┄44 ┆ ┆ └┄45 ┆ └┄46 ┆ ├┄47 ┆ └┄48 └┄49 ├┄50 ┆ ├┄51 ┆ ┆ ├┄52 ┆ ┆ └┄53 ┆ └┄54 ┆ ├┄55 ┆ └┄56 └┄57 ├┄58 ┆ ├┄59 ┆ └┄60 └┄61 ├┄62 └┄63