最近工作的一个模块上使用的树形分类插件无法达到多级联动效果,于是我就进行重新编码,这里记录了一下具体过程。
**注意:**加载子树,子树是否选中状态有所调整,demo中没有修正,下面博客中已完善–》dealLoadChildrenTree 看一下此函数即可明白
具体功能展示:
后面会上传具体编码示例,可执行操作的demo
一、结构
- 后台加载的树形数据结构
[{"code":"sunwuk_1","isChildren":0,"name":"孙悟空"},
{"code":"zhubajie_1","isChildren":0,"name":"猪八戒"},
{"code":"tangseng_1","isChildren":1,"name":"唐僧"}]
- 前台处理过的数据结构—基本就是这种结构
<li class="tree-open">
<div class="tree-tit">
<b class="tree-state" onclick="loadTreeChildren2(this)" id="4624_2"></b>
<i class="tree-check" onclick="checkOnclick2(this)" id="4624_2"
title="三国演义"></i>
<span style="color:black">三国演义</span>
</div>
<ul class="tree-sub">
<li>
<i class="tree-check" onclick="checkOnclick2(this)"
id="cz_0" title="曹操"></i>
<span style="color:black">曹操</span>
</li>
<li>
<i class="tree-check" onclick="checkOnclick2(this)" id="zhfx_0"
title="孙权"></i>
<span style="color:black">孙权</span>
</li>
</ul>
</li>
- 数据加载,这个其实就是一个ajax的异步调用,动态实现每个分类子树的加载,需要注意的是避免同一个分类子树的多次加载demo中会标注这个
- 分类选择操作,这是一个难点的操作,其主要精力都是在实现这个功能上面。
二、demo
**注意:**由于使用了一些选择器,需要自己引入一个jquery插件。每一步的详细思路全部都有注释,需要一定的递归理解
主要是点选函数的编写checkOnclick2,多结合注释与分类树形的数据结构里面思路即简单明了
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title> new document </title>
<meta name="generator" content="editplus" />
<meta name="author" content="" />
<meta name="keywords" content="" />
<meta name="description" content="" />
<script src="jquery-1.8.1.js"></script>
<!--link href="tree.css" rel="stylesheet"-->
</head>
<style>
.tree-drop {
position: relative;
z-index: 10;
}
.tree-drop .tree-drop-hd {
height: 28px;
border: 1px solid #e7ebee;
background: #fff;
cursor: pointer;
}
.tree-drop .tree-drop-hd .tree-txt {
margin-right: 26px;
background: #fff;
height: 26px;
line-height: 26px;
overflow: hidden;
}
.tree-drop .tree-drop-hd .tree-pic {
position: relative;
float: right;
width: 26px;
height: 26px;
border-left: 1px solid #e7ebee;
background: #f8f8f8;
}
.tree-drop .tree-drop-hd .tree-pic i {
-webkit-backface-visibility: hidden;
position: absolute;
left: 9px;
top: 11px;
width: 0;
height: 0;
border-width: 4px 4px 0 4px;
border-style: solid dashed dashed;
border-color: #97a4ac transparent transparent;
font-size: 0;
line-height: 0;
}
.tree-drop.tree-drop-active .tree-drop-hd .tree-pic i {
-webkit-transform: rotate(180deg);
transform: rotate(180deg);
}
.tree-drop-bd {
position: absolute;
top: 200px;
left: 200px;
z-index: 1200;
width: auto;
background: #f8f8f8;
border: 1px solid #e7ebee;
min-height: 100px;
max-height: 400px;
padding: 10px 20px 10px 10px;
overflow-x: hidden;
overflow-y: auto;
display: none;
}
.tree {
width: auto;
}
.tree li {
line-height: 30px;
padding-left: 22px;
position: relative;
white-space: nowrap;
}
.tree li > .tree-state {
background: url(tree01.png) no-repeat;
}
.tree .tree-sub {
display: none;
}
.tree .tree-check {
float: left;
margin: 8px 5px 0 0;
width: 14px;
height: 14px;
background: url(check01.png) no-repeat;
}
.tree .tree-check.tree-checked {
background: url(check03.png) no-repeat;
}
.tree .tree-check.tree-checked-part {
background: url(check02.png) no-repeat;
}
.tree .tree-state {
position: absolute;
top: 7px;
left: 0;
width: 16px;
height: 16px;
background: url(tree02.png) no-repeat;
}
.tree .tree-open > .tree-sub {
display: block;
}
.tree .tree-open > .tree-tit > .tree-state {
background: url(tree01.png) no-repeat;
}
.tree-box {
border: 1px solid #e7ebee;
padding: 16px;
}
li {list-style-type:none;}
</style>
<body>
<!--树形选中id-->
<input type='text' id='codesId'>
<!--树形选中名称-->
<input type='text' id='codenames'>
<div>
<!--容器-->
<ul class='tree' id='tree2'>
<li class="tree-open">
<div class="tree-tit">
<b class="tree-state" onclick="loadTreeChildren2(this)" id="MZSJ_1"></b>
<i class="tree-check" onclick="checkOnclick2(this)" id="MZSJ_1"
title="名著书籍"></i>
<span style="color:black">名著书籍</span>
</div>
<ul class="tree-sub">
<li class="tree-open">
<div class="tree-tit">
<b class="tree-state" onclick="loadTreeChildren2(this)" id="4624_2"></b>
<i class="tree-check" onclick="checkOnclick2(this)" id="4624_2"
title="三国演义"></i>
<span style="color:black">三国演义</span>
</div>
<ul class="tree-sub">
<li>
<i class="tree-check" onclick="checkOnclick2(this)"
id="cz_0" title="曹操"></i>
<span style="color:black">曹操</span>
</li>
<li>
<i class="tree-check" onclick="checkOnclick2(this)" id="zhfx_0"
title="孙权"></i>
<span style="color:black">孙权</span>
</li>
</ul>
</li>
<li class="tree-open">
<div class="tree-tit">
<b class="tree-state" onclick="loadTreeChildren2(this)" id="4574_2"></b>
<i class="tree-check" onclick="checkOnclick2(this)" id="4574_2"
title="水浒传"></i>
<span style="color:black">水浒传</span>
</div>
<ul class="tree-sub">
<li>
<i class="tree-check" onclick="checkOnclick2(this)" id="yinguanghui_0"
title="及时雨"></i>
<span style="color:black">及时雨</span>
</li>
<li>
<i class="tree-check" onclick="checkOnclick2(this)" id="cdb_0"
title="花和尚"></i>
<span style="color:black">花和尚</span>
</li>
</ul>
</li>
<li>
<i class="tree-check" onclick="checkOnclick2(this)" id="6252_2"
title="西游记"></i>
<span style="color:black">西游记</span>
</li>
<li>
<i class="tree-check" onclick="checkOnclick2(this)" id="CDA_1"
title="红楼梦"></i>
<span style="color:black">红楼梦</span>
</li>
</ul>
</li>
</ul>
</div>
</body>
</html>
<script>
//注意
$(function(){
//打开页面初次加载数据
//loadTreeParent2('url路径','参数');
});
//--------------------------------------------------------tree start---
//1. 初始化变量
var pCodes2 = ""; //用于记录已经请求加载过的子树数据的子树
var treeUrl2 = ""; //路径,加载树形数据时,只有参数是不一样的
var treeCodes2 = ""; //选中值id,一般用于选择后提交的值
var treeNames2 = ""; //选中值名称,一般用于显示选中值
//2. 初始化加载数据--一般首次来到页面自动进行一次数据加载,后续则根据想要查看的各个子树继续加载。---其实可以跟下面二次加载子树数据合并成一个函数
function loadTreeParent2(url, pcode) {
pCodes2 = "";
pCodes2 += pcode + ",";
treeUrl2 = url;
var tmp =$("#tree2").length;
if ($("#tree2").length > 0) {
$("#tree2").html("");
$("#tree-txt2").html("");
$.post(basePath + treeUrl2, {
pcode : pcode
}, function(data) {
var d = loadTreeCom2(data);
//alert(d);
$("#tree2").append(loadTreeCom2(data));
});
$("#tree-txt2").html(treeNames2);
return false;
} else {
$("body")
.append(
"<div class='tree-drop-bd' id='tree-down2'><ul class='tree' id='tree2'></ul></div>");
$.post(basePath + treeUrl2, {
pCode : pCode
}, function(data) {
var d = loadTreeCom2(data);
alert(d);
$("#tree2").append(loadTreeCom2(data));
});
$("#tree-txt2").html(treeNames2);
return false;
}
}
/**
*JSON 数据示例
[{"code":"sunwuk_1","isChildren":0,"name":"孙悟空"},
{"code":"zhubajie_1","isChildren":0,"name":"猪八戒"},
{"code":"tangseng_1","isChildren":1,"name":"唐僧"}]
*/
/**
*数据解析--
* 解析后:无子树数据
<i class="tree-check" οnclick="checkOnclick2(this)" id="CDA_1"
title="红楼梦"></i>
<span style="color:black">红楼梦</span>
解析后有子树数据:
<div class="tree-tit">
<b class="tree-state" οnclick="loadTreeChildren2(this)" id="4624_2"></b> ---->多了一个用于表示加载数据的 ‘加减’ 符号
<i class="tree-check" οnclick="checkOnclick2(this)" id="4624_2"
title="三国演义"></i>
<span style="color:black">三国演义</span>
</div>
*/
function loadTreeCom2(data) {
data = eval("(" + data + ")");
var isChildrenStr = ""; //有子树
var isNotChildren = ""; //无子树
for (var i = 0; i < data.length; i++) {
var str = "";
str += "<li>";
if (data[i].isChildren == 1) {
str += "<div class='tree-tit'><b class='tree-state' οnclick='loadTreeChildren2(this)' id='"
+ data[i].code + "'></b>";
}
str += "<i class='tree-check' οnclick='checkOnclick2(this)' id='" + data[i].code + "' title='" + data[i].name + "'></i>";
str += '<span style=\'color:black\'>'+data[i].name+ '</span>';
if (data[i].isChildren == 1) {
str += "</div>";
}
str += "</li>";
if(data[i].isChildren == 1){
isChildrenStr += str;
}else{
isNotChildren += str;
}
}
return isChildrenStr+isNotChildren;
}
//加载子树
function loadTreeChildren2(obj) {
$(obj).parent().parent().toggleClass("tree-open");
var pcode = $(obj).attr("id");
//已经加载过数据不在二次请求
if (pCodes2.indexOf("," + pcode + ",") >= 0) {
return false;
}
$.post(basePath + treeUrl2, {
pcode : pcode
}, function(data) {
var str = "<ul class='tree-sub'>";
str += loadTreeCom2(data);
str += "</ul>";
$(obj).parent().parent().append(str);
//初次加载当前子树,判断子树是否选中,若选中则其加载的所有子节点初始化为选中状态
dealLoadChildrenTree(obj);
});
//初次加载数据---标记,后续打开关闭子树时无需二次请求
pCodes2 += pcode + ",";
return false;
}
/**
*加载完成子树后,判断当前子树根节点是否为选中状态,若为选中状态,则其加载的所有子树应该也为选中状态
*/
function dealLoadChildrenTree(obj){
//1. 判断当前子树根节点状态
var checkNode = $(obj).next("i");
var status = isCheckStatus(checkNode);
//2. 是否处理其子节点状态
if(status){
//3. 获取所有子元素并设置选中状态
var childrenArr = $(checkNode).parent().parent().children("ul").children("li");
//alert(childrenArr.length);
//3.1 遍历处理所有子节点
for (var i = 0; i < childrenArr.length; i++) {
var chilNode = childrenArr.eq(i).find("i").eq(0);
//3.2 选中当前节点
selectCheckeChildren(chilNode);
}
}else{
return false;
}
}
/**
*判断当前节点是否为选中状态
*/
function isCheckStatus(obj){
var cla = obj.attr('class');
if(cla.indexOf("tree-checked")<0){
return false;
}
return true;
}
/**
*分类选择时间处理----选中,取消功能
*/
function checkOnclick2(obj) {
//$("#hl").val($("#tree2").html());
$(obj).toggleClass("tree-checked");
//判断选中还是取消:
if ($(obj).attr("class").indexOf("tree-checked") >= 0) {
//1. 选中其当前节点
selectCheckeChildren(obj);
//2. 选中当前节点值
var oid = $(obj).attr("id");
if ((";" + treeCodes2).indexOf(";" + oid + ";") < 0) {
treeCodes2 += oid + ";";
treeNames2 += $(obj).attr("title") + ";";
}
selectCurrNodeIsParentStatus(obj);
} else {
//从当前出发向下处理
removeCheckedChildren(obj);
//从当前出发向上处理
removeCurrentParentNodeChecked(obj);
}
//如果当前所有子节点全部
$("#codesId").val(treeCodes2);
$("#codenames").val(treeNames2);
return false;
}
/**
*选中当前节点判断其兄弟节点的状态进而决定其父节点状态
*/
function selectCurrNodeIsParentStatus(obj){
//1. 判断当前节点是否有效
//2. 判断当前节点是否是整棵树的根节点-是则终止
if(isTreeRootNode(obj)){
return false;
}
//3. 加载其所有兄弟节点
//3.1 判断其当前节点是否是子树
var isRootNode = isParentTree(obj);
//3.2 获取其父节点
var parentNode;//父节点
var siblingArr;//兄弟节点
if(isRootNode){
parentNode = $(obj).parent().parent().parent().parent();
siblingArr = $(obj).parent().parent().siblings();
}else{
parentNode = $(obj).parent().parent().parent();
siblingArr = $(obj).parent().siblings();
}
//alert($(parentNode).html());
//3.3 加载其所有兄弟节点
var res=true;
for(var i=0 ; i<siblingArr.length;i++){
var sibling = siblingArr[i];
var clv = $(sibling).find("i").eq(0).attr('class');
//4. 判断其兄弟节点是否有未选中状态,有则终止
if(clv.indexOf("tree-checked")<0){
//res=false;
return false;
}
}
if(res){
//5. 若其兄弟节点全是选中状态则,则判断当前节点是否是子树根节点--用户加载其父节点
var parentI_Node = parentNode.children("div").find("i").eq(0);
//6. 获取其父节点,从父节点开始选中其所有子节点
selectCheckeChildren(parentI_Node);
var oid = $(parentI_Node).attr("id");
if ((";" + treeCodes2).indexOf(";" + oid + ";") < 0) {
treeCodes2 += oid + ";";
treeNames2 += $(parentI_Node).attr("title") + ";";
}
//7. 传入父节点,递归调用
selectCurrNodeIsParentStatus(parentI_Node);
}else{
return false;
}
}
/**
*选中当前节点及其所有自己子节点,取消其选中值
*/
function selectCheckeChildren(obj){
//1. 判断当前节点是否有效
//2. 选中当前节点状态
isCheckedNode(obj);
//3. 判断当前节点是否为子树根节点
var res = isParentTree(obj);
//4. 若为子树根节点,则加载其子节点数组遍历并递归调用其子节点
if(res){
var childrenArr = $(obj).parent().parent().children("ul").children("li");
for (var i = 0; i < childrenArr.length; i++) {
//所有子节点取消
var chilNode = childrenArr.eq(i).find("i").eq(0);
//递归调用
selectCheckeChildren(chilNode);
}
}else{
return false;
}
}
/**
*取消当前节点--递归向上处理其父节点及兄弟节点
*/
function removeCurrentParentNodeChecked(obj){
//1. 判断当前节点是否有效
//2. 判断当前节点是否为整颗树的根节点-是则终止
if(isTreeRootNode(obj)){
return false;
}
//3. 获取并判断其父节点是否是选中状态
var isCheck = isParentNodeChecked(obj);
if(!isCheck){
//3.1 父节点未选中--终止
return false;
}
//4.判断当前节点是否是一颗子树根节点--用于获取父节点
var isRootNode = isParentTree(obj);
//4.1 获取当前节点的父节点及兄弟节点数组
var curParentNode;
var siblingArr;//兄弟节点
if(isRootNode){
curParentNode = $(obj).parent().parent().parent().parent();
siblingArr = $(obj).parent().parent().siblings();
}else{
curParentNode = $(obj).parent().parent().parent();
siblingArr = $(obj).parent().siblings();
}
//5. 获取当前节点的父节点的状态节点
var curI_Node = curParentNode.children("div").find("i").eq(0);
//5.1 取消父节点选中状态
isUnChecked(curI_Node);
//var siblingArr = curParentNode.children().children("ul").children("li");
//6. 处理其兄弟节点--既然父节点是选中状态,那么其所有兄弟节点必然也是选中状态
for(var i = 0;i < siblingArr.length;i++){
//6.1处理每一个兄弟节点
var sib = $(siblingArr[i]).find("i").eq(0);
//var clv = $(sibling).find("i").eq(0).attr('class');
selectCheckeChildren(sib);
//6.2. 选中当前节点值
var oid = $(sib).attr("id");
if ((";" + treeCodes2).indexOf(";" + oid + ";") < 0) {
treeCodes2 += oid + ";";
treeNames2 += $(sib).attr("title") + ";";
}
}
//7. 以当前父节点向上递归处理
removeCurrentParentNodeChecked(curI_Node);
}
/**
*取消选中节点值及其所有子节点
*/
function removeCheckedChildren(obj){
//1. 判断其节点是否有效
//2. 取消当前节点值
isUnChecked(obj);
//3. 判断其是否为子树根节点
var res = isParentTree(obj);
if(res){
//3.1 若为子树则加载其子节点数组遍历递归处理
//由于只有当前节点是子树时才有子节点
var childrenArr = $(obj).parent().parent().children("ul").children("li");
for (var i = 0; i < childrenArr.length; i++) {
//所有子节点取消
var chilNode = childrenArr.eq(i).find("i").eq(0);
//递归调用
removeCheckedChildren(chilNode);
}
}else{
//3.2 无子节点则终止此次调用
return false;
}
}
/**
*判断当前节点是否是root根节点
*/
function isTreeRootNode(obj){
var isRootNode = isParentTree(obj);
var curNode;
if(isRootNode){
curNode = $(obj).parent().parent().parent();
}else{
curNode = $(obj).parent().parent();
}
var rootid = $(curNode).attr("id");
if(rootid == "tree2"){
return true;
}
return false;
}
/**
*判断其父节点是否为选中状态
*/
function isParentNodeChecked(obj){
//1. 当前节点是否是一个子树根节点
var isRootNode = isParentTree(obj);
var curParentNode;
//2. 获取其父节点
if(isRootNode){
curParentNode = $(obj).parent().parent().parent().parent();
}else{
curParentNode = $(obj).parent().parent().parent();
}
//3. 加载去父节点的选择框节点
var curI_Node = curParentNode.children("div").find("i").eq(0);
var cla = $(curI_Node).attr('class');
//4. 判断其父节点是否为选中状态
if(cla.indexOf("tree-checked")<0){
return false;
}else{
return true;
}
}
/**
*只选中当前节点状态
*/
function isChecked(obj){
var cla = obj.attr('class');
if(cla.indexOf("tree-checked")<0){
obj.toggleClass("tree-checked");
}
}
/**
*选中当前节点并取消其选中值
*/
function isCheckedNode(obj){
//1. 选中状态
var cla = $(obj).attr('class');
if(cla.indexOf("tree-checked")<0){
$(obj).toggleClass("tree-checked");
}
//2. 取消其选中值
if ((";" + treeCodes2).indexOf(";" + $(obj).attr("id") + ";") >= 0) {
treeCodes2 = treeCodes2.replace($(obj).attr("id") + ";", "");
treeNames2 = treeNames2.replace($(obj).attr("title") + ";", "");
}
//3. 更新选中值
$("#codesId").val(treeCodes2);
}
/**
*取消当前节点及其选中值
*/
function isUnChecked(obj){
//1. 取消选中状态
var cla = $(obj).attr('class');
if(cla.indexOf("tree-checked")>0){
$(obj).removeClass("tree-checked");
}
//2. 取消其选中值
if ((";" + treeCodes2).indexOf(";" + $(obj).attr("id") + ";") >= 0) {
treeCodes2 = treeCodes2.replace($(obj).attr("id") + ";", "");
treeNames2 = treeNames2.replace($(obj).attr("title") + ";", "");
}
//3. 更新选中值
$("#codesId").val(treeCodes2);
}
/**
*判断当前节点是否是一个子树根节点,tree-tit 是子树根节点专属类型
*/
function isParentTree(obj){
var res = false;
if($(obj).parent().attr("class")=='tree-tit')
res = true;
return res;
}
//--------------------------------------------------------tree end---
</script>
下载地址:https://download.csdn.net/download/qq_35241080/11161506
其实上面已经是完善的全部demo了。