js实现无限级目录树




原理

N叉树的遍历

每个data的数据格式如下:

var oneChoice = {
	name: 'A',
	child: [
		{
			name: 'A-1'
			child:[
				{...},
				{...}
			]
		},
		{
		
		}
	]
}

最终拼接成的html格式如下。

<li>
    <span>-</span>AAA
    <ul>
        <li><span>-</span>a1
            <ul style="display: block;">
                <li><span>-</span>a1-1</li>
                <li><span>-</span>a1-2</li>
            </ul>
        </li>
        <li><span>-</span>a2</li>
        <li><span>-</span>a3
            <ul style="display: block;">
                <li><span>-</span>a3-1</li>
                <li><span>-</span>a3-2
                    <ul style="display: block;">
                        <li><span>-</span>a3-2-1</li>
                        <li><span>-</span>a3-2-2</li>
                    </ul>
                </li>
            </ul>
        </li>
    </ul>
</li>
</ul>

实际上就是一个N叉树的遍历。

那是什么遍历呢?

很显然,是一个前序遍历,遵循着“根左右”的顺序。从根开始,先将第一层第一个选项中所有的子孙选项展开,再到第一层第二个选项…

当然,也有其他的做法,例如菜单展开的时候,只希望展开第一层中的选项,而第二第三层的选项等用户点击后再进行加载。那么这种情况可以考虑使用层级遍历。

这里我按照前序遍历的方式来写,可以先来做一做这道题,做完之后自然明白该怎么写了。

589. N叉树的前序遍历

/*
// Definition for a Node.
class Node {
    public int val;
    public List<Node> children;

    public Node() {}

    public Node(int _val) {
        val = _val;
    }

    public Node(int _val, List<Node> _children) {
        val = _val;
        children = _children;
    }
};
*/

class Solution {
    List<Integer> res = new ArrayList<>();

    public List<Integer> preorder(Node root) {
        if (root == null) {
            return res;
        }
        helper(root);
        return res;
    }
    
    private void helper(Node root) {
        if (root == null) {
            return;
        }
        res.add(root.val);
        for(Node oneNode: root.children){
            if(oneNode != null){
                helper(oneNode);
            }
        }
    }
}

框架就可以写出来了。

// 返回对应格式的html str
function createTree(data) {
    if(data为空){
        return '';
    }
    
    var str = '<ul>';
    for (选项 in data.child) {
        str += <li> ;
        str += '<span>-</span>' + 选项.name;
        
        // 递归
        if (选项.child) {
            str += createTree(data[i].child);
        }
        str += '</li>';
    };
    str += '</ul>';
    
    return str;
};

显示/隐藏功能

这个功能不难实现,只要使用display: none;和display:visible;来控制这块ul是否显示就行了。

  • 如果子ul的display状态为visible,则点击将会
    • 隐藏子ul
    • 将-变成+
  • 如果子ul的状态为none,则点击将会
    • 显示子ul
    • 将+变成-
//单击事件
$(".lists ul li").click(function (event) {

    //判断有没有ul子级
    if ($(this).find("ul").is(":visible")) {
        //隐藏   +
        $(this).find("ul").hide();
        $(this).find("span").text("+");
    } else {
        //显示  -
        $(this).find("ul").show();
        $(this).find("span").text("-");
    }
})

防止冒泡

就是在触发子类的函数时,防止父类也执行了子类中的行为。

如图,如果不阻止来自子类的冒泡事件,点击了b1-1-1-1时,就会使得BBB,乃至菜单root也会被关闭掉,这显然是不行的。

img

//单击事件
$(".lists ul li").click(function (event) {
    event.stopPropagation(); //阻止事件冒泡

    //判断有没有ul子级
    if ($(this).find("ul").is(":visible")) {
        //隐藏   +
        $(this).find("ul").hide();
        $(this).find("span").text("+");
    } else {
        //显示  -
        $(this).find("ul").show();
        $(this).find("span").text("-");
    }
})

完整代码

<!DOCTYPE html>
<html>

<head>
    <meta charset='utf-8'>
    <meta http-equiv='X-UA-Compatible' content='IE=edge'>
    <title>Page Title</title>
    <meta name='viewport' content='width=device-width, initial-scale=1'>
    <style>
        ul {
            list-style-type: none;
        }

        ul li span {
            color: red;
            padding-right: 10px;
        }
    </style>

</head>

<body>
    <div class="lists"></div>

    <script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
    <script>
        var data = [
            {
                name: 'AAA',
                child: [
                    {
                        name: 'a1',
                        child: [
                            {
                                name: 'a1-1'
                            }, {
                                name: 'a1-2'
                            }
                        ]
                    },
                    {
                        name: 'a2'
                    },
                    {
                        name: 'a3',
                        child: [
                            {
                                name: 'a3-1'
                            }, {
                                name: 'a3-2',
                                child: [
                                    {
                                        name: 'a3-2-1'
                                    }, {
                                        name: 'a3-2-2'
                                    }
                                ]
                            }
                        ]
                    }
                ]
            },
            {
                name: 'BBB',
                child: [
                    {
                        name: 'b1',
                        child: [
                            {
                                name: 'b1-1',
                                child: [
                                    {
                                        name: 'b1-1-1',
                                        child: [
                                            {
                                                name: 'b1-1-1-1',
                                                child: [
                                                    {
                                                        name: 'b1-1-1-1-1',
                                                        child: [
                                                            {
                                                                name: 'b1-1-1-1-1-1'
                                                            },
                                                        ]
                                                    },
                                                ]
                                            },
                                        ]
                                    },
                                ]
                            },

                        ]
                    },
                    {
                        name: 'b2'
                    },
                    {
                        name: 'b3'
                    }
                ]
            },
            {
                name: 'CCC',
                child: [
                    {
                        name: 'c1'
                    }
                ]
            },
            {
                name: 'DDD'
            }
        ];

        $(function () {
            //递归
            function createTree(data) {
                var str = '<ul>';
                for (var i = 0; i < data.length; i++) {
                    str += '<li><span>-</span>' + data[i].name;
                    if (data[i].child) {
                        str += createTree(data[i].child);
                    }
                    str += '</li>';
                };
                str += '</ul>';
                return str;
            };

            $(".lists").html(createTree(data));

            //单击事件
            $(".lists ul li").click(function (event) {
                event.stopPropagation(); //阻止事件冒泡

                //判断有没有ul子级
                if ($(this).find("ul").is(":visible")) {
                    //隐藏   +
                    $(this).find("ul").hide();
                    $(this).find("span").text("+");
                } else {
                    //显示  -
                    $(this).find("ul").show();
                    $(this).find("span").text("-");
                }
            })
        })
    </script>
</body>

</html>

img

参考资料

《前端面试题》

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值