当前位置: 首页 → 爱前端 → 

JavaScript

抓取文章标题生成树状目录导航

 

树状目录导航

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

思路

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

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

实现

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

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);
    }
}

调用

displayTree(...);

输入1:

<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:

<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>

输入2:

<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>

输出2(二叉树):

<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>
🔚
 

*

*

*

*