我们可以使用递归地方法写树形菜单栏,树形菜单栏的应用场景有很多,使用递归地方法可以快速地转化数据格式。使用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");
};
};
};