我们可以使用递归地方法写树形菜单栏,树形菜单栏的应用场景有很多,使用递归地方法可以快速地转化数据格式。使用for循环,一次只能到一层,要嵌套,并且数据不一定有几层,但递归的方法可以无限的转化数据格式。
下面我们要写一个树形结构,要求打开页面只有父级显示在页面上,子级默认隐藏,只有有子级的父级才会有下拉箭头,并且鼠标放在选中的内容上有hover效果,选中的元素会和其他的元素有明显的区别。点击父级展开下拉箭头颠倒,子级出现。
HTML中只写了一个结构:
<div class="content"> </div>
css样式:
<style> * { margin: 0; padding: 0; } .content { width: 200px; background-color: #535d62; } .aline { width: 200px; height: 40px; display: flex; line-height: 40px; } .pic { width: 20px; height: 20px; margin-left: 26px; margin-top: 10px; } .text { font-size: 14px; color: #ffffff; display: flex; margin-left: 20px; } .down { width: 16px; height: 16px; margin-top: 15px; margin-left: 50px; /*设置所有动画的效果:0.3s 淡出效果*/ transition: all .3s ease-out; } /* Hover样式 */ .aline:hover { background-color: #7d7d7d; transition: 0.5s; } .aline:hover .text { color: #88baa7; transition: 0.5s; } .aline.selected { background-color: #7d7d7d; color: #88baa7; } .childs { display: none; } .down-rotate { transform: rotate(180deg); /* 可选的过渡效果 */ transition: transform 0.3s ease; } </style>
js代码:
// 递归的数据格式转化 // 参数: data 表示扁平数据,一维的全部数据 // 参数: pid 表示数据的父级id 一级数据pid=0 // 参数: arr 数组,函数出来过后将其返回至调用函数位置,函数处理的结果 function treeData(data, pid, arr = []) { // for循环 遍历数组 for (let i in data) { // 判断数据的pid是否等于父级的id if (data[i].pid == pid) { // 把数据放在arr中 arr.push(data[i]); // 把子元素放在父级元素中 arr[arr.length - 1].children = treeData(data, data[i].id) }; }; // 返回arr的值 return arr; }; // 声明变量tree_data 等于所有的父级元素 let tree_data = treeData(arr, 0); console.log(tree_data); function tree_menu(data) { // 整个背景 let str = '<div class="content">'; // for循环遍历数组 for (let i = 0; i < data.length; i++) { // 判断是否有子级 if (data[i].children.length > 0) { // 当有子元素时拼接名称和下拉箭头和拼接图标 str += ` <div class="aline" onclick="click_show(this)"> <img class="pic" src="${data[i].img}" alt="" /><div class="text">${data[i].name} <img class="down" src="img/向下箭头_小.png" alt="" />` str += `</div> </div> <div class="childs" style="display:none"> ${tree_menu(data[i].children)} </div>` } else { // 否则只拼接名称 str += `<div class="aline"> <img class="pic" src="${data[i].img}" alt="" /> <div class="text">${data[i].name}</div> </div>` }; }; str += '</div>' return str; }; // 在页面内输入 document.write(tree_menu(tree_data)); // 用于跟踪当前选中的元素 let option = null; function click_show(e) { // 获取下一个同级元素,即包含子项的div let childsDiv = e.nextElementSibling; // 当前被点击的.aline元素 let alineElement = e; // 如果已经有一个元素被选中,则移除其.selected类 if (option) { option.classList.remove('selected'); }; // 更新当前选中的元素 option = alineElement; // 判断如果数据和数据的名称是否为childs if (childsDiv && childsDiv.className === 'childs') { // 获取当前父元素内的箭头图片 let downArrow = e.querySelector(".down"); // 切换子菜单的显示状态 if (childsDiv.style.display === 'none') { // 让子级显示 childsDiv.style.display = 'block'; // 添加被选中的样式 alineElement.classList.add('selected'); // 旋转箭头 downArrow.classList.add("down-rotate"); } else { childsDiv.style.display = 'none'; // 重置箭头 downArrow.classList.remove("down-rotate"); }; }; };