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

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>
发布了95 篇原创文章 · 获赞 115 · 访问量 12万+

猜你喜欢

转载自blog.csdn.net/qq_34569497/article/details/98489708