<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>树形组件</title>
</head>
<link rel="stylesheet" type="text/css" href="task.css">
<body>
<fieldset id="tree-module">
<legend>树形组件</legend>
<input type="text" id="searchText">&nbsp;
<button id="search">搜索</button><button id="clear">清除</button>
<br>
<p id="result"></p>
<div id="tree-area">
<div class="nodebody-visible"><label class="node-header"><div class="arrow empty-arrow"></div> <span class="node-title">前端攻城狮</span><span>&nbsp;&nbsp;</span><img class="addIcon" src="data:images/add.png"></label></div>
</div>
</fieldset>
<script src="task.js"></script>
</body>
</html>

//////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////

span{
margin:0;
padding:0;
}
#tree-module{
display: inline-block;
width: 400px;
position: relative;
left: 50%;
margin-left: -200px;
}
#clear{
margin-left: 10px;
}
#result{
font-size: 14px;
font-family: "Microsoft YaHei UI";
text-align: center;
height: 16px;
}

/* 以下是结点部分的样式 */
.nodebody-visible {
display: flex;
flex-direction: column;
flex-wrap: nowrap;
justify-content: flex-start;
margin: 5px 18px;
background-color: #ffffff;
user-select:none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
}
.nodebody-hidden {
display: none;
}
.arrow{
display: inline-block;
margin-right: 6px;
}
.right-arrow{
width: 0;
height: 0;
border-left: 10px solid #6898c2;
border-top: 5px solid transparent;
border-bottom: 5px solid transparent;
cursor: pointer;
}
.down-arrow{
width: 0;
height: 0;
border-top: 10px solid #6898c2;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
cursor: pointer;
}
.empty-arrow{
border-left:10px solid transparent;
}
.node-title{
font-family: "Microsoft YaHei UI";
font-size: 14px;
color: #6898c2;
line-height: 1.5em;
}
.node-title:hover{
color: blue;
cursor: pointer;
}
.node-title-highlight{
color: red;
font-weight: bold;
}
.addIcon,.deleteIcon{
display: none;
vertical-align: top;
}
.addIcon:hover,.deleteIcon:hover{
cursor: pointer;
}
.node-header:hover>.addIcon,.node-header:hover>.deleteIcon{
display: inline;
}
br{
clear: both;
}
legend {
font-family: "黑体";
font-size: 20px;
}
.root, fieldset {
float: left;
}

/////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////////

// ==========================================封装TreeNode================================================
function TreeNode(obj) {
this.parent = obj.parent;
this.childs = obj.childs || [];
this.data = obj.data || "";
this.selfElement = obj.selfElement; // 访问对应的DOM结点
this.selfElement.TreeNode = this; // 对应的DOM结点访问回来
}
// 原型模式封装公共操作
TreeNode.prototype = {
constructor: TreeNode,
// 解耦样式操作,四个参数表示是否改变箭头、可见性、改为高亮、改为普通,后两个参数可省略
render: function (arrow, visibility, toHighlight, deHighlight) {
if (arguments.length < 3) {
toHighlight = false;
deHighlight = false;
}
if (arrow) {
if (this.isLeaf()) { // 是个叶节点,设为空箭头
this.selfElement.getElementsByClassName("arrow")[0].className = "arrow empty-arrow";
}
else if (this.isFolded()) { // 折叠状态,设为右箭头
this.selfElement.getElementsByClassName("arrow")[0].className = "arrow right-arrow";
}
else { // 展开状态,设为下箭头
this.selfElement.getElementsByClassName("arrow")[0].className = "arrow down-arrow";
}
}
if (visibility) { // 改变可见性
if (this.selfElement.className.indexOf("nodebody-visible") == -1) { // 本不可见,改为可见
this.selfElement.className = this.selfElement.className.replace("hidden", "visible");
}
else { //改为不可见
this.selfElement.className = this.selfElement.className.replace("visible", "hidden");
}
}
if (toHighlight) { // 设为高亮
this.selfElement.getElementsByClassName("node-title")[0].className = "node-title node-title-highlight";
}
if (deHighlight) { // 取消高亮
this.selfElement.getElementsByClassName("node-title")[0].className = "node-title";
}
},
// 删除结点,DOM会自动递归删除子节点,TreeNode递归手动删除子节点
deleteNode: function () {
var i;
// 递归删除子节点
if(!this.isLeaf()){
for(i=0;i<this.childs.length;i++){
this.childs[i].deleteNode();
}
}
this.parent.selfElement.removeChild(this.selfElement);// 移除对应的DOM结点
for (i = 0; i < this.parent.childs.length; i++) { // 从父节点删除该孩子
if (this.parent.childs[i] == this) {
this.parent.childs.splice(i, 1);
break;
}
}
// 调整父结点箭头样式
this.parent.render(true, false);
},
// 增加子节点
addChild: function (text) {
if (text == null) return this;
if (text.trim() == "") {
alert("节点内容不能为空!");
return this;
}
// 先增加子节点,再渲染自身样式
// 若当前节点关闭,则将其展开
if(!this.isLeaf() && this.isFolded()){
this.toggleFold();
}
// 创建新的DOM结点并附加
var newNode = document.createElement("div");
newNode.className = "nodebody-visible";
var newHeader = document.createElement("label");
newHeader.className = "node-header";
var newSymbol = document.createElement("div");
newSymbol.className = "arrow empty-arrow";
var newTitle = document.createElement("span");
newTitle.className = "node-title";
newTitle.innerHTML = text;
var space = document.createElement("span");
space.innerHTML = "&nbsp;&nbsp;";
var newDelete = document.createElement("img");
newDelete.className = "deleteIcon";
newDelete.src = "images/delete.png";
var newAdd = document.createElement("img");
newAdd.className = "addIcon";
newAdd.src = "images/add.png";
newHeader.appendChild(newSymbol);
newHeader.appendChild(newTitle);
newHeader.appendChild(space);
newHeader.appendChild(newAdd);
newHeader.appendChild(newDelete);
newNode.appendChild(newHeader);
this.selfElement.appendChild(newNode);
// 创建对应的TreeNode对象并添加到子节点队列
this.childs.push(new TreeNode({parent: this, childs: [], data: text, selfElement: newNode}));
// 渲染自身样式
this.render(true, false);
return this; // 返回自身,以便链式操作
},
// 展开、收拢结点
toggleFold: function () {
if (this.isLeaf()) return this; // 叶节点,无需操作
// 改变所有子节点的可见状态
for (var i=0;i<this.childs.length;i++)
this.childs[i].render(false, true);
// 渲染本节点的箭头
this.render(true, false);
return this; // 返回自身,以便链式操作
},
// 判断是否为叶结点
isLeaf: function(){
return this.childs.length == 0;
},
// 判断结点是否处于折叠状态
isFolded: function(){
if(this.isLeaf()) return false; // 叶结点返回false
if(this.childs[0].selfElement.className == "nodebody-visible") return false;
return true;
}
};
//=======================================以上是封装TreeNode的代码=============================================

//=============================================事件绑定区===============================================
// 创建根节点对应的TreeNode对象
var root = new TreeNode({parent: null, childs: [], data: "前端攻城狮", selfElement: document.getElementsByClassName("nodebody-visible")[0]});
// 为root绑定事件代理,处理所有节点的点击事件
addEvent(root.selfElement, "click", function (e) {
var target = e.target || e.srcElement;
var domNode = target;
while (domNode.className.indexOf("nodebody") == -1) domNode = domNode.parentNode; // 找到类名含有nodebody前缀的DOM结点
selectedNode = domNode.TreeNode; // 获取DOM对象对应的TreeNode对象
// 如果点在节点文字或箭头上
if (target.className.indexOf("node-title") != -1 || target.className.indexOf("arrow") != -1) {
selectedNode.toggleFold(); // 触发toggle操作
}
else if (target.className == "addIcon") { // 点在加号上
selectedNode.addChild(prompt("请输入子结点的内容:"));
}
else if (target.className == "deleteIcon") { // 点在减号上
selectedNode.deleteNode();
}
});

// 给root绑定广度优先搜索函数,无需访问DOM,返回一个搜索结果队列
root.search = function (query) {
var resultList = [];
// 广度优先搜索
var queue = []; // 辅助队列,顺序存储待访问结点
var current = this;
// 当前结点入队
queue.push(current);
while (queue.length > 0) {
// 从“待访问”队列取出队首结点访问,并将其所有子节点入队
current = queue.shift();
// 还原当前结点颜色
current.render(false, false, false, true);
// 读取当前结点data
if (current.data == query) resultList.push(current); //找到了
// 将当前结点的所有孩子节点入“待访问”队
for (var i=0;i<current.childs.length;i++) {
queue.push(current.childs[i]);
}
}
return resultList;
};
// 搜索并显示结果
addEvent(document.getElementById("search"), "click", function () {
var text = document.getElementById("searchText").value.trim();
if(text == "") {
document.getElementById("result").innerHTML = "请输入查询内容!";
return;
}
// 执行搜索
var resultList = root.search(text);
// 处理搜索结果
if (resultList.length == 0) {
document.getElementById("result").innerHTML = "没有查询到符合条件的结点!";
}
else {
document.getElementById("result").innerHTML = "查询到" + resultList.length + "个符合条件的结点";
// 将所有结果结点沿途展开,结果结点加粗红色展示
var pathNode;
for (var x = 0;x<resultList.length;x++) {
pathNode = resultList[x];
pathNode.render(false, false, true, false);
while (pathNode.parent != null) {
if (pathNode.selfElement.className == "nodebody-hidden") pathNode.parent.toggleFold(); // 若是收拢状态,则展开
pathNode = pathNode.parent;
}
}
}
});
// 清除搜索结果
addEvent(document.getElementById("clear"), "click", function () {
document.getElementById("searchText").value = "";
root.search(null); // 清除高亮样式
document.getElementById("result").innerHTML = "";
});
//==================================================================================================

//=======================================Demo展示区==================================================
//动态生成Demo树
root.addChild("技术").addChild("IT公司").addChild("谈笑风生");
root.childs[0].addChild("HTML5").addChild("CSS3").addChild("JavaScript").addChild("PHP").addChild("Node.JS").toggleFold();
root.childs[0].childs[4].addChild("JavaScript").toggleFold();
root.childs[1].addChild("百度").addChild("腾讯").addChild("大众点评").toggleFold();
root.childs[2].addChild("身经百战").addChild("学习一个").addChild("吟两句诗").toggleFold();
root.childs[2].childs[2].addChild("苟利国家生死以").toggleFold();
//初始化查询Demo值
document.getElementById("searchText").value = "JavaScript";
//==================================================================================================

// 跨浏览器兼容的工具函数
function addEvent(element, type, handler) {
if (element.addEventListener) {
element.addEventListener(type, handler);
}
else if (element.attachEvent) {
element.attachEvent("on" + type, handler);
}
else {
element["on" + type] = handler;
}
}

JS与树本(复杂)的更多相关文章

  1. 下拉的DIV+CSS+JS二级树型菜单

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  2. js无限级树菜单

    以前做网站,树形菜单一般都很简单,自己定义风格样式,简单的js控制,后来原来网上很多文章都在讨论Js树型菜单,看了几个实例,发现这个树比较简单好用. http://hovertree.com/texi ...

  3. Ext.js中树勾选的四种操作

    最近在做控件优化的时候产品提了一个需求,对树的勾选要满足四种勾选方案: 1.点击一次根节点,当根节点和子节点均未选中的情况下,根节点和子节点全都选中. 2.第二次点击根节点,当根节点和部分或全部子节点 ...

  4. 用D3.js画树状图

    做项目遇到一个需求,将具有层级关系的词语用树状图的形式展示它们之间的关系,像这样: 或者是这样: 上面的图片只是样例,跟我下面的代码里面用的数据不同 网上有很多这种数据可视化展示的js控件,我这里选择 ...

  5. 原生JS实现树状结构列表

    树状结构列表,这个技术点之前有写过了,是基于vue讲解,但似乎都没有解决痛点,最基础的原生JS该怎么实现呢? 这篇文章会全面详细的介绍树状结构列表的实现,从数据处理成树状结构,到动态生成dom节点渲染 ...

  6. js 查找树节点 数组去重

    //查找树节点function findData(curOrg, id) { var array = []; if ((typeof curOrg == 'object') && (c ...

  7. [js] 渲染树构建、布局及绘制

    渲染树构建.布局及绘制

  8. js 目录树

    <!doctype html> <html> <head> <meta charset="utf-8"> <title> ...

  9. js 遍历树的层级关系的实现

    1.遍历树的层级关系 1)先整理数据 2)找到id和数据的映射关系 3)然后找到父节点的数据,进行存储 test() { const list = [ { id: ", parentId: ...

随机推荐

  1. iis7 64位 操作excel的一系列问题(未完待续)

    查了半天发现是IIS跑在64位环境下引起的.而64位下,是木有Access数据库的驱动的(包括Excel也不行). 解决办法是:在目标网站的应用程序池中选择高级设置,然后将启用32位应用程序设置为tr ...

  2. YbSoftwareFactory 代码生成插件【十三】:Web API 的安全性

    ASP.NET Web API 可非常方便地创建基于 HTTP 的 Services,这些服务可以非常方便地被几乎任何形式的平台和客户端(如浏览器.Windows客户端.Android设备.IOS等) ...

  3. windows+caffe(六)——convert.bat

    convert.bat的格式为 convert_imageset.exe的位置+空格+FLAGS+空格+图片所在的位置+空格+你生成的list的位置+空格+将要生成的db格式要保存的位置 建议都使用绝 ...

  4. 分表的一个记录---Ruby

    sql1=" UPDATE user_red_info_"sql2=" SET status = '#{status}', update_time = '#{update ...

  5. 在SpringMVC框架下实现数据的国际化(即数据实现多国文字之间的转换)

    在eclipse中javaEE环境下:导入必要的架包 web.xml配置文件: <?xml version="1.0" encoding="UTF-8"? ...

  6. Easyui-combobox-checkbox-带复选框的下拉框

    $.post("getSubInsuranceTypeList.do",{parent_id:node.id},function(result){                  ...

  7. winform 移动窗体,和窗体阴影(引用)

    无边框窗体移动://窗体移动API [DllImport("user32.dll")] public static extern bool ReleaseCapture(); [D ...

  8. Duiib 创建不规则窗口(转载)

    方法一: 转载:http://blog.csdn.net/chenlycly/article/details/46447297 转载:http://blog.csdn.net/harvic880925 ...

  9. smarty模板引擎

    1.    使用smarty 1.1 项目引入 // 3, 连接数据库,提取相关数据 $title = "Smarty模板引擎"; $content = "Smarty模 ...

  10. 续并查集学习笔记——Closing the farm题解

    在很多时候,并查集并不是一个完整的解题方法,而是一种思路. 通过以下题目来体会并查集逆向运用的思想. Description Farmer John and his cows are planning ...