无限极下拉菜单(两种方式实现:递归/递归+DOM)

84 篇文章 3 订阅

1.第一种思路:递归

无论哪种思路实现都需注意不能生成一级菜单后直接渲染到页面,而是需要生成所有的DOM节点及内容后,才一次性渲染到页面。

思路:

根据pid和id进行关联是否是父子级菜单

最终是将所以递归获得的数据渲染到页面,所以递归函数需要有返回值;

展开收缩思路:

找到当前元素,如果是展开的,直接收缩;

如果是收缩的,获取当前元素下的所有ul标签,将所有同级元素全部隐藏,再将当前元素进行显示

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script src="data.js"></script>
    <style>
        .view ul ul{
            display: none;
        }

        .show{
            display: block !important;
        }
    </style>
</head>
<body>
    <div class="view">
        <!-- <ul>
            <li>
                <p>一级标题</p>
                <ul>
                    <li>
                        <p>二级标题</p>
                        <ul>
                            <li>
                                ...
                            </li>
                        </ul>
                    </li>
                </ul>
            </li>
        </ul> -->
    </div>
<script>
{
    /*
        思路:
            根据pid和id进行关联是否是父子级菜单
            最终是将所以递归获得的数据渲染到页面,所以递归函数需要有返回值;
        展开收缩思路:
            找到当前元素,如果是展开的,直接收缩;
            如果是收缩的,获取当前元素下的所有ul标签,将所有同级元素全部隐藏,再将当前元素进行显示

    */
   let view = document.querySelector(".view");

   let getChild = (id) =>{
       //筛选pid等于id的情况就表示有子级
       return data.filter(item=>item.pid == id);
   }

   //最终是不断根据pid等于id的情况进行递归,所以需要将这个写成一个函数不断进行递归
//    let inner = '';
//    data.forEach(item=>{
//        inner +=`
//             <ul>
//                 <li>
//                     <p>${item.title}</p>
//                 </li>
//             </ul>
//        `;
//    });

//最终要将所以递归到的inner渲染到页面,所以需要有返回值
let createUl = (data)=>{
    //不能在循环里面生成ul,否则会生成多个多个ul
    let inner = '<ul>';
    data.forEach(item=>{
        inner +=`
                
                    <li>
                        <p>${item.title}</p>
                        ${
                            // 如果当前id还能找到对应pid的子级就继续查找
                            getChild(item.id)&&createUl(getChild(item.id))
                        }
                    </li>
        `;
    });
    inner += '</ul>';
    return inner;
}
view.innerHTML = createUl(getChild(-1));

//获取到所有的p标签
let allP = document.querySelectorAll("p");
allP.forEach(item=>{
    item.onclick = () =>{
        let curUl = item.nextElementSibling;
        if(curUl){
            // 1. 当前是展开状态,点击之后就收缩起来
            if(curUl.classList.contains("show")){
                curUl.classList.remove("show");
            } else {
                // 当前是收缩状态,就把所有的同级收缩起来,然后展开当前的
                let allUl = curUl.parentNode.parentNode.querySelectorAll("ul");
                
                //所有的同级收起来
                allUl.forEach(item=>{
                    item.classList.remove("show");
                });
                //展开当前
                curUl.classList.add("show");
            }
        }
        
    };
});
}
</script>
</body>
</html>

2.第二种思路实现:递归+DOM

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script src="data.js"></script>
    <style>
        .view ul ul{
            display: none;
        }

        .show{
            display: block !important;
        }
    </style>
</head>
<body>
    <div class="view">
        <!-- <ul>
            <li>
                <p>一级标题</p>
                <ul>
                    <li>
                        <p>二级标题</p>
                        <ul>
                            <li>
                                ...
                            </li>
                        </ul>
                    </li>
                </ul>
            </li>
        </ul> -->
    </div>
<script>
{
    /*
        思路:根据pid和id进行关联是否是父子级菜单
        最终是将所以递归获得的数据渲染到页面,所以递归函数需要有返回值
        不能生成一级菜单就返回,要等所有的ul都生成后再返回
    */
   let view = document.querySelector(".view");

   let getChild = (id) =>{
       //筛选pid等于id的情况就表示有子级
       return data.filter(item=>item.pid == id);
   }

   //最终是不断根据pid等于id的情况进行递归,所以需要将这个写成一个函数不断进行递归
//最终要将所以递归到的inner渲染到页面,所以需要有返回值
let createUl = (data)=>{
    let ul = document.createElement("ul");
    data.forEach(item=>{
        let li = document.createElement("li");
        let p = document.createElement("p");
        
        li.appendChild(p);
        p.innerHTML = item.title; 

        //子级菜单每个都加在li上
        if(getChild(item.id)){
            li.appendChild(createUl(getChild(item.id)));
        }

        ul.appendChild(li);   
    });
    return ul;
}
view.appendChild(createUl(getChild(-1)));

//获取到所有的p标签
let allP = document.querySelectorAll("p");
allP.forEach(item=>{
    item.onclick = () =>{
        let curUl = item.nextElementSibling;
        if(curUl){
            // 1. 当前是展开状态,点击之后就收缩起来
            if(curUl.classList.contains("show")){
                curUl.classList.remove("show");
            } else {
                // 2.当前是收缩状态,就把所有的同级收缩起来,然后展开当前的
                let allUl = curUl.parentNode.parentNode.querySelectorAll("ul");
                
                //所有的同级收起来
                allUl.forEach(item=>{
                    item.classList.remove("show");
                });
                //展开当前
                curUl.classList.add("show");
            }
        }
    };
});

}
</script>
</body>
</html>

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值