使用js手写树形插件

最近工作的一个模块上使用的树形分类插件无法达到多级联动效果,于是我就进行重新编码,这里记录了一下具体过程。

**注意:**加载子树,子树是否选中状态有所调整,demo中没有修正,下面博客中已完善–》dealLoadChildrenTree 看一下此函数即可明白

具体功能展示:
在这里插入图片描述

后面会上传具体编码示例,可执行操作的demo

一、结构

  1. 后台加载的树形数据结构
	[{"code":"sunwuk_1","isChildren":0,"name":"孙悟空"},
	{"code":"zhubajie_1","isChildren":0,"name":"猪八戒"},
	{"code":"tangseng_1","isChildren":1,"name":"唐僧"}]
  1. 前台处理过的数据结构—基本就是这种结构
		<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>
  1. 数据加载,这个其实就是一个ajax的异步调用,动态实现每个分类子树的加载,需要注意的是避免同一个分类子树的多次加载demo中会标注这个
  2. 分类选择操作,这是一个难点的操作,其主要精力都是在实现这个功能上面。

二、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了。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值