Flex+JS:实现可视化的多叉树遍历、查询、聚焦、添加、删除节点

来源:互联网 发布:知乎删除的福利 编辑:程序博客网 时间:2024/06/14 22:56

基于上次的二叉树,现在变成多叉树的遍历了。多叉树的遍历不再分为前中后序遍历,而是深度优先遍历广度优先遍历。其中深度优先遍历又分为递归非递归


遍历算法

深度优先遍历

递归版本
function recursionOrder(node) {    nodeArr.push(node);    if(node==null || node.children.length == 0) return;    for(var i = 0; i < node.children.length; i++) {        recursionOrder(node.children[i]);    }}
非递归版本
function deepOrder(rootnode) {    nodeArr.push(rootnode);    var queue = [];    for(var i = 0; i < rootnode.children.length; i++) {        queue.push(rootnode.children[i]);    }    while(queue.length) {        var child = queue.shift();        nodeArr.push(child);        //由于unshift()是向前添加元素,所以使用它必须从后往前添加子节点        var tempLength = child.children.length-1;        while(child.children[tempLength]!=undefined) {            queue.unshift(child.children[tempLength]);            tempLength--;        }    }}

广度优先遍历

function wideOrder(rootnode) {    nodeArr.push(rootnode);    var queue = [];    for(var i = 0; i < rootnode.children.length; i++) {        queue.push(rootnode.children[i]);    }    while(queue.length) {        var child = queue.shift();        nodeArr.push(child);        var tempLength = 0;        while(child.children[tempLength]!=undefined) {            queue.push(child.children[tempLength]);            tempLength++;        }    }}

配合show函数就可以看到其遍历过程啦~

function show() {    for(var i = 0; i < nodeArr.length; i++) {        time = setTimeout((function(num){            return function() {                if(num-1 >= 0) {                    nodeArr[num-1].style.backgroundColor = "white";                }                nodeArr[num].style.backgroundColor = "orange";            }        })(i), i*1000);    }}

查询节点

在这一步,我们必须知道在数组nodeArr中的每一个节点的文本是什么才能保证show函数遍历到该节点后停止。
那么如何获取到每个节点的文本节点呢?

获取节点文本

前面文章有提到innerText和value,但是在这里根本不适用。
首先,div没有value属性,无法通过value获取到文本。其次,div下面还有很多子节点,使用innerText返回的将是包含其子节点文本的所有文本内容。
上网查了大半天也没找到想要的答案,所以,还是书好哇
《javaScript高级程序设计》就有提到关于文本节点的获取(p270

//没有内容,即没有文本节点<div></div>//有空格,有1个文本节点<div></div>//有内容,有1个文本节点<div>hello world!</div>

获取文本节点的方式

var text = div.firstChild;

修改文本节点内容

text.nodeValue = "Hello world!";

恍然大悟!
所以我获取文本的方式是

for(var i = 0; i < nodeArr.length; i++) {    textNode[i] = nodeArr[i].firstChild.nodeValue;    textNode[i] = textNode[i].trim();}

trim()是为了删除文本左右两边的空白。
查找函数我是这样写的

search_btn.onclick = function() {    init();    recursionOrder(rootNode);    var textNode = [];    for(var i = 0; i < nodeArr.length; i++) {        textNode[i] = nodeArr[i].firstChild.nodeValue;        textNode[i] = textNode[i].trim();    }    if(textNode.indexOf(input.value)!=-1&&textNode.indexOf(input.value)< textNode.length-1) {        nodeArr.splice(textNode.indexOf(input.value)+1, textNode.length-textNode.indexOf(input.value)-1);            textNode.splice(textNode.indexOf(input.value)+1, textNode.length-textNode.indexOf(input.value)-1);        show();    } else if(textNode.indexOf(input.value)==-1){        alert("没查找到您想要的!");    } else {        show();    }}

splice()那一行简直惨不忍睹,还是自己想想怎么做吧。我的思路是数组找到用户查询的关键字后就将关键字后面的其他节点删除了,再show()就可以看到只遍历到关键字部分了。


聚焦节点

这个功能主要是提供点击某个节点,该节点背景颜色改变的功能。
我在这里遇到的问题是我一旦点击了某个节点并改变其背景颜色值,其子节点的背景颜色也会被改变。
这是因为子节点默认继承父节点的背景颜色值
解决的方法很简单,在点击之前初始化一下,将所有节点的背景颜色值改为白色。
至于如何定位到某个div上我是查来的函数,具体原理就是使用event.target获取到点击的节点并传入到处理函数中就好了。
补充:定位div的函数其实用到了事件委托,就是可以利用事件冒泡只需指定一个事件处理程序管理一类事件处理。此处为最高层次的wrap指定了一个事件处理程序。

function processInnderDiv(domDiv){    init();    domDiv.style.backgroundColor = "#900";    currentDiv = domDiv;}document.getElementById("wrap").addEventListener("click",function(event){    processInnderDiv(event.target);},false);

删除节点

聚焦到某个节点后若用户点击了删除按钮,就将该节点及其所有子节点删除掉。
看到上面如何获取聚焦到的节点的引用

currentDiv = domDiv;

直接将这个节点的outerHTML该为空就好了呀~

delete_btn.onclick = function() {    if(currentDiv!=undefined)        currentDiv.outerHTML = "";}

添加节点

添加节点则是聚焦到某个节点后若用户点击了添加按钮,则在该节点下增加一个子节点,节点内容为输入框中内容,插入在其子节点的最后一个位置。
大同小异,直接上代码

add_btn.onclick = function() {    if (currentDiv!=undefined) {        var str = "<div>"+inputDiv.value+"<div></div></div>";        currentDiv.outerHTML+=str;    } }

结果图

初始化

这里写图片描述

递归遍历过程

这里写图片描述

查询”短袖“

这里写图片描述

聚焦”家电“

这里写图片描述

删除合资

这里写图片描述

添加进口

这里写图片描述


总结

我想到一个问题——如果我在递归遍历过程中,又点击深度非递归遍历此时是什么情况?
答案是同时进行。
这得改改,我想的是给setTimeout赋值给t,然后点击其他按钮前用clearTimeout(t)清除计时器。
但是没用啊…
我再看看…


我知道了!

我认为若我在循环中一直令time = setTimeout的话,time在被清除时其他的setTimeout没被清除啊,因为time只是改变了引用。
所以最好的方法是令time是一个大小为节点个数的数组。

//设置时time[i] = setTimeout(...);//清除时for(var i = 0; i < div.length; i++) {    div[i].style.backgroundColor = "white";    clearTimeout(time[i]);}

完美解决~

0 0