<!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. YbSoftwareFactory 代码生成插件【二十四】:MVC中实现动态自定义路由

    上一篇介绍了 公文流转系统 的实现,本篇介绍下MVC下动态自定义路由的实现. 在典型的CMS系统中,通常需要为某个栏目指定个友链地址,通过指定友链地址,该栏目的地址更人性化.方便记忆,也有利用于搜索引 ...

  2. 报表控件NCreport教程:子查询系统设计

    数据报表中经常需要用到主从数据关系,比如发票.订单等一类的特殊文件,在报表控件NCreport中也不例外.数据报表至少有一个表头和一个通过 主键和外键关联的相关细节数据集.子查询系统则是通过父数据源驱 ...

  3. dr.wondr博士随笔之某古旧非智能机T6XXX 恢复一例

    大家好!欢迎再次来到dr.Wonde的微博! 今次我给大家带来索尼爱立信古董机T650i的取证工作展示! 首先请出今天我们的主角索尼爱立信 T650i>> 然后在工厂模式下,连接设备,看图 ...

  4. oracle迁移postgres之-oracle_fdw

    1. 安装oracle_fdw 在编译安装前,需要设置postgres的环境变量,如在.bash_profile中增加: export ORACLE_HOME=/u01/app/oracle expo ...

  5. 如何用Java代码列出一个目录下所有的文件?

    目录文件夹 File file=new File("H:\\"); for(File temp:file.listFiles()){//Java5的新特性之一就是增强的for循环. ...

  6. Volley全方位解析,带你从源码的角度彻底理解

    Volley 是 Google 推出的轻量级 Android 异步网络请求框架和图片加载框架.在 Google I/O 2013 大会上发布.其适用场景是数据量小,通信频繁的网络操作. 主要特点: ( ...

  7. 瘋子C语言笔记(结构体/共用体/枚举篇)

    (一)结构体类型 1.简介: 例: struct date { int month; int day; int year; }; struct student { int num; char name ...

  8. 配置L2TP IPsec VPN (CentOS 6.5)

    1. 安装相关包 yum install -y ppp iptables make gcc gmp-devel xmlto bison flex libpcap-devel lsof vim-enha ...

  9. 如何让你的App适配iOS7?

    随着苹果在2013年9月18日发布iOS7最新的系统以来,iOS各种设备升级到iOS7的数字就已经不断刷新记录.目前据外界统计iOS7设备装机量已经达到2.5亿部,已占iOS设备的64%.由此可见让自 ...

  10. 【RabbitMQ】RabbitMQ的一些基础概念

    工作中使用的是RabbitMQ,需要对其进行熟悉.使用之前,弄清楚它是什么东西,解决什么问题. 场景 一些不必实时执行的任务 开发中,有一些任务并无须实时执行,比如: 会员更新个人信息,更新会员信息之 ...