在开发中经常遇到文件树这种需求,一般在网上搜到的,都是一些写的比较死的,不容易修改,很难达到自己的个性化需求,其实文件树也不难,只是用到的递归算法,从根节点一直递归到叶子节点,下面通过这个例子,给大家讲一下,文件树的实现。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
ul, li {
list-style: none;
/ / 取消掉ul, li的默认样式
}
ul {
padding-left: 20px;
}
.close { /**关闭按钮**/
display: inline-block;
15px;
height: 15px;
background: url("close.png") no-repeat center;
background-size: contain;
}
.open { /**打开按钮**/
display: inline-block;
15px;
height: 15px;
background: url("open.png") no-repeat center;
background-size: contain;
}
.leaf { /**叶子节点**/
display: inline-block;
15px;
height: 15px;
background: url("leaf.png") no-repeat center;
background-size: contain;
}
.leafName {
color: green;
padding-left: 10px;
padding-right: 10px;
}
.openTrue {
color: gray;
}
</style>
</head>
<body>
<div id="tree"></div>
<script type="text/javascript" src="../../../static/plugins/jquery/jquery-1.9.1.js"></script>
<script>
//第一种数据结构包含子元素
var arr = [
{
name: "父节点1 - 展开", open: true,
children: [
{
name: "父节点11 - 折叠",
children: [
{name: "叶子节点111", nid: "nid"},
{name: "叶子节点112",},
{name: "叶子节点113"},
{name: "叶子节点114"}
]
},
{
name: "父节点12 - 折叠",
children: [
{name: "叶子节点121"},
{name: "叶子节点122"},
{name: "叶子节点123"},
{name: "叶子节点124"}
]
},
{name: "父节点13 - 没有子节点", isParent: true}
]
},
{
name: "父节点2 - 折叠",
children: [
{
name: "父节点21 - 展开",
children: [
{name: "叶子节点211"},
{name: "叶子节点212"},
{name: "叶子节点213"},
{name: "叶子节点214"}
]
},
{
name: "父节点22 - 折叠",
children: [
{name: "叶子节点221"},
{name: "叶子节点222"},
{name: "叶子节点223"},
{name: "叶子节点224"}
]
},
]
},
];
//第二种数据结构有父级id的
var arr1 = [
{id: 1, pId: 0, name: "父节点1 - 展开", open: true},
{id: 11, pId: 1, name: "父节点11 - 折叠"},
{id: 111, pId: 11, name: "叶子节点111"},
{id: 112, pId: 11, name: "叶子节点112"},
{id: 113, pId: 11, name: "叶子节点113"},
{id: 114, pId: 11, name: "叶子节点114"},
{id: 12, pId: 1, name: "父节点12 - 折叠"},
{id: 121, pId: 12, name: "叶子节点121"},
{id: 122, pId: 12, name: "叶子节点122"},
{id: 123, pId: 12, name: "叶子节点123"},
{id: 124, pId: 12, name: "叶子节点124"},
{id: 13, pId: 1, name: "父节点13 - 没有子节点", isParent: true},
{id: 2, pId: 0, name: "父节点2 - 折叠"},
];
paintingTree(arr, "tree")
//渲染树
//arr为数据,id为树的容器的id
function paintingTree(arr, id) {
if (arr[0]) {
//判断是哪一种数据结构
if (arr[0]["pId"] !== undefined) {//如果是还有父元素id的第二种数据结构,那么先转换成第一种数据结构
arr = removeEmptyFromPaintData(arr)
}
}
var str = ""
//渲染树
function createTree(arr) {
if (arr) {//判断arr是否有效
var children = arr;
str += "<ul>";
for (var j = 0; j < children.length; j++) {
str += "<li>"
if (children[j]["children"]) {//判断是否有子节点
if (children[j]["open"]) {//判断是否是展开的
str += "<div open='true'><span class='close'></span><span class='openTrue'>" + children[j]["name"] + "</span></div>";
} else {
str += "<div open='false'><span class='open'></span><span class='openTrue'>" + children[j]["name"] + "</span></div>";
}
} else {
if (children[j]["isParent"]) {//判断是否是父节点
str += "<div open='true'><span class='open'></span>" + children[j]["name"] + "</div>";
} else {
str += "<div><span class='leaf'></span><span class='leafName'></span>" + children[j]["name"] + "</div>";
}
}
createTree(children[j]["children"])//递归调用方法,一层一层渲染
str += "</li>"
}
str += "</ul>";
}
}
//渲染树
createTree(arr)
$("#" + id).hide()//渲染完成后先进行隐藏
$("#" + id).html(str)
$("[open=true]").each(function () {//含有[open=true]属性的下一个元素(ul)进行展示
$(this).next().show()
})
$("[open=false]").each(function () {//含有[open=false]属性的下一个元素(ul)进行隐藏
$(this).next().hide();
})
$(document).on("click", ".close", function () {//给含有[open=true]属性的元素绑定点击事件
$(this).parent().next().hide();//当前元素的父元素(div)的下一个元素(ul)隐藏
$(this).addClass("open").removeClass("close");//当前元素样式改为关闭样式
})
$(document).on("click", ".open", function () {
$(this).parent().next().show();
$(this).addClass("close").removeClass("open");
})
$("#" + id).show();
/**把简单的数据转化成渲染数据
第一个参数和第二个参数是同一个数组**/
function createPaintDataFromSimpleData(zNodes, Nodes) {
for (var i = 0; i < zNodes.length; i++) {//循环第一个参数
if (!!zNodes[i]) {//如果这个元素是有效的 不为"" 0 false null undefind
zNodes[i]["children"] = [];//给元素增加children属性,并复制为空数组
for (var j = 0; j < Nodes.length; j++) {//循环第二个参数
if (!!Nodes[j]) {//如果这个元素是有效的 不为"" 0 false null undefind
if (Nodes[j]["pId"] == zNodes[i]["id"]) {//如果第二个参数元素中的pid有等于第一个参数中当前元素的id的,那么把第二个参数中符合条件的元素都放到第一个参数当前元素children中
if (Nodes[j]) {//保证这个值是有效的
zNodes[i]["children"].push(Nodes[j]);
Nodes[j] = "";//为什么不直接删掉,因为要保证循环的正常进行,就要保证数组的长度
}
}
}
}
if (zNodes[i]["children"].length == 0) {//如果循环下来,children的长度为0,那么到此第一个参数的当前元素,到此结束
zNodes[i]["children"] = false
} else {//如果循环下来,children的长度不为0,第一个参数的当前元素,递归循环
createPaintDataFromSimpleData(zNodes[i]["children"], zNodes);
}
}
}
return zNodes;//最后得到的元素是含有空字符串的元素 所以下一步,要去除空字符串
}
//对渲染数据清除空元素
function removeEmptyFromPaintData(nodes) {
var data = createPaintDataFromSimpleData(nodes, nodes);
var arr = [];
for (var i = 0; i < data.length; i++) {
if (!!data[i]) {
arr.push(data[i]);//只存放不为空字符串的元素
}
}
return arr;
}
}
</script>
</body>
</html>