<!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. maven nexus 私服的搭建学习

    之前对maven有过初步的了解与认识,自己也创建过项目使用其来管理,但都是非常粗浅的操作,今天在高人的指点下,也学着在自己的电脑上搭建一个maven私服,虽然技术难度也不高,但为了更深层次的提高,这些 ...

  2. 如何通过cmd检查自己电脑是否安装了oracle

    随便一个oracle命令,例如imp,如果提示输入用户名,就表示安装了oracle 1.直接运行sqlplus,然后要求输入用户名和密码.如果你是管理员的身份,应该在用户名后加as sysdba(以下 ...

  3. 谷歌 HTML/CSS 规范 2016-12-30

    背景 这篇文章定义了 HTML 和 CSS 的格式和代码规范,旨在提高代码质量和协作效率. 通用样式规范 协议 省略图片.样式.脚本以及其他媒体文件 URL 的协议部分(http:,https:),除 ...

  4. LR中线程和进程的区别

    LoadRunner中的进程与线程    1.进程与线程的区别: 进程和线程的区别是什么?进程和线程都是由操作系统所体会的程序运行的基本单元,系统利用该基本单元实现系统对应用的并发性.进程和线程的区别 ...

  5. java性能监控常用命令

    jps -m -l:主要用来输出JVM中运行的进程状态信息 jstack -l pid 来观察锁持有情况 jsatck pid | grep pid(十六进制):输出进程pid的堆栈信息 jmap - ...

  6. C# Socket编程笔记

    1.按惯例先来介绍下socket      Windows中的很多东西都是从Unix领域借鉴过来的,Socket也是一样.在Unix中,socket代表了一种文件描述符(在Unix中一切都是以文件为单 ...

  7. selenium-JS点击(项目应用)

    public static JavascriptExecutor jse; 声明一个js public LogoutWebElements(WebDriver driver){        Logo ...

  8. QQ模拟自动登录实现

    QQ模拟自动登录实现 本篇文章主要介绍"QQ模拟自动登录实现(带验证码)",主要涉及到java 实现QQ自动登录(带验证码)方面的内容,对于java 实现QQ自动登录(带验证码)感 ...

  9. css-position

    值 描述 absolute 生成绝对定位的元素,相对于 static 定位以外的第一个父元素进行定位. 元素的位置通过 "left", "top", " ...

  10. AS 重装系统之后配置

    重新安装了win7 系统,一起的AS 放在其他盘里 1.重新安装java sdk 配置java 环境. 2,从新配置AS 工作界面及各种配置 3,重新安装genymotion 并在as 中配置 出现 ...