javascript tree

(function(wdo) {
    var w = wdo || window,
        dom = w.document;
    var Class = {
        create: function() {
            return function() {
                this.initialize.apply(this, arguments);
            }
        }
    };
    
    var Stack = Class.create();
    Stack.prototype = {
        initialize: function() {
            this.stack = new Array();
        },
        push: function(o) {
            this.stack.push(o);
        },
        pop: function() {
            if(this.stack.length - 1 < 0) 
                return null;
            var o = this.stack[this.stack.length-1];
            this.stack.splice(this.stack.length-1, 1);
            return o;
        },
        isEmpty: function() {
            if(this.stack.length > 0)
                return false;
            else 
                return true;
        },
        size: function() {
            return this.stack.length;
        }
    };

    var Tree = Class.create();
    Tree.prototype = {
        initialize: function(options) {
            this._treedata = options.treedata;
            this._renderTo = options.renderTo;
            this._showRoot = options.showRoot;

            this._htmlRootNode = null;
            this._clickEvt     = null;
            this._imgUrl = {
                //minus:  "url(images/minus.gif)",
                //minus1: "url(images/minus1.gif)",
                minus2: "url(images/minus2.gif)",           
                minus3: "url(images/minus3.gif)",           
                //minus4: "url(images/minus4.gif)",
                //minus5: "url(images/minus5.gif)",
                
                //plus:  "url(images/plus.gif)",
                //plus1: "url(images/plus1.gif)",
                plus2: "url(images/plus2.gif)",             
                plus3: "url(images/plus3.gif)",             
                //plus4: "url(images/plus4.gif)",
                //plus5: "url(images/plus5.gif)",

                line:  "url(images/line.gif)",              
                //line1: "url(images/line1.gif)",
                line2: "url(images/line2.gif)",             
                line3: "url(images/line3.gif)",             
                //line4: "url(images/line4.gif)",

                oFile: "url(images/folderOpen_bak.gif)",    
                cFile: "url(images/folderClosed_bak.gif)",  

                leaf:  "url(images/leaf_bak.gif)"           
            };

            this.loopTree(this._treedata[0]);
        },
        addClickEvt: function(fn) {
            this._clickEvt = fn;
        },
        elastic: function(dpy, flag, img1, img2, img3) {
            var stack = new Stack();
            //判断是否有根节点
            if(this._showRoot) {
                //stack.push(this._htmlRootNode);
                stack.push(this._htmlRootNode.childrenbox);
            }
            else {
                var list = this._htmlRootNode.childrenbox.childNodes;
                for(var i = 1 ; i < list.length ; i+=2) {
                    stack.push(list[i]);
                }
            }
            while(!stack.isEmpty()) {
                var obj = stack.pop();
                obj.style.display = dpy;
                if(obj._first) {
                    obj._first.isExpand = flag;
                    if(obj._first.isLast) {
                        obj._first.style.backgroundImage = img1;
                    }
                    else {
                        obj._first.style.backgroundImage = img2;
                    }
                    obj._second.style.backgroundImage = img3;
                }
                var arr = obj.childNodes;
                for(var i = 1  ; i < arr.length ; i+=2) {
                    stack.push(arr[i]);
                }
            }
        },
        expand: function() {
            this.elastic('block', true, this._imgUrl.minus2, this._imgUrl.minus3, this._imgUrl.oFile);
        },
        shrink: function() {
            this.elastic('none', false, this._imgUrl.plus2, this._imgUrl.plus3, this._imgUrl.cFile);
        },
        getStringByteLength: function (val){
            var Zhlength=0;// 全角
            var Enlength=0;// 半角
            for(var i=0;i<val.length;i++){
                if(val.substring(i, i + 1).match(/[^\x00-\xff]/ig) != null)
                    Zhlength+=1;
                else
                    Enlength+=1;
            }
            // 返回当前字符串字节长度
            return (Zhlength*2)+Enlength;
        },
        createNode: function(ops) {
            var text = ops.text,      //节点的文本信息
                data = ops.data,      //节点的用户数据
                parent = ops.parent,  //节点的父亲节点
                isLeaf = ops.isLeaf,  //是否为页节点
                isLastNode = ops.isLastNode ; //判断是否为父亲的最后一个子节点
            //节点容器          ==========
            var nodebox = dom.createElement('div');
                nodebox.style.cssText = 'border:0px solid red;height:18px;font-size:0px;line-height:0px;';
                nodebox.userData = data;
            //处理节点前面的图标 =========
            var stack = new Stack(),
                tempNode = parent,
                count = 0;
            while(tempNode) {
                count++;
                stack.push(tempNode);
                tempNode = tempNode._parent;
            }
            if(!this._showRoot) { //判断是否显示根节点
                count--;
                stack.pop();
            }
            //计算节点容器的宽度
            count += 2;
            var len = this.getStringByteLength(text)*6+count*18;
            nodebox.style.width = (len+10)+'px';

            while(!stack.isEmpty()) {
                var node = dom.createElement('div');
                node.style.cssText = 'width:18px;height:18px;float:left;';
                var element = stack.pop();
                if(element == null) {
                    continue;
                }
                else if(element.isLast) {
                    node.style.backgroundImage = '';
                }
                else {
                    node.style.backgroundImage = this._imgUrl.line;
                }
                nodebox.appendChild(node);
            }
            stack = null;
            var self = this;

            //子节点容器
            var childrenbox = dom.createElement('div');
            childrenbox.style.cssText = 'font-size:0px;line-height:0px;';

            //判断是否为叶节点
            var node = dom.createElement('div');
            nodebox.isLast = isLastNode;
            if(!isLeaf) {
                node.style.cssText = 'width:18px;height:18px;float:left;background-image:'+this._imgUrl.minus3;
                node.isExpand = true;
                node.isLast = isLastNode;
                node.isFirst = true;
                node.onclick = function() {
                    //是否第一次点击
                    if(this.isFirst) {
                        this._height = nodebox.childrenbox.clientHeight;
                        this.isFirst = false;
                    }
                    var targetHeight = 0;
                    if(this.isExpand) {
                        childrenbox.style.display = 'none';
                        targetHeight = 0;
                        if(this.isLast) {
                            this.style.backgroundImage = self._imgUrl.plus2;
                        }
                        else {
                            this.style.backgroundImage = self._imgUrl.plus3;
                        }
                        fileNode.style.backgroundImage = self._imgUrl.cFile;
                        this.isExpand = false;
                    }
                    else {
                        childrenbox.style.display = 'block';
                        targetHeight = this._height;
                        if(this.isLast) {
                            this.style.backgroundImage = self._imgUrl.minus2;
                        }
                        else {
                            this.style.backgroundImage = self._imgUrl.minus3;
                        }
                        fileNode.style.backgroundImage = self._imgUrl.oFile;
                        this.isExpand = true;
                    }
                }
                //判断是否为父亲的最后一个子节点
                if(isLastNode) {
                    node.style.backgroundImage = this._imgUrl.minus2;
                }
                nodebox.appendChild(node);
                //添加文件夹
                var fileNode = dom.createElement('div');
                fileNode.style.cssText = 'width:18px;height:18px;float:left;background-image:'+this._imgUrl.oFile;
                nodebox.appendChild(fileNode);

                childrenbox._first = node;
                childrenbox._second = fileNode;
            }
            else {
                node.style.cssText = 'width:18px;height:18px;float:left;background-image:'+this._imgUrl.line3;
                //判断是否为父亲的最后一个节点
                if(isLastNode) {
                    node.style.backgroundImage = this._imgUrl.line2;
                }
                nodebox.appendChild(node);
                node = dom.createElement('div');
                node.style.cssText = 'width:18px;height:18px;float:left;background-image:'+this._imgUrl.leaf;
                nodebox.appendChild(node);
            }
            

            //加入文本信息
            var node = dom.createElement('div');
            node.style.cssText = 'height:18px;font-size:12px;line-height:18px;text-align:left;float:left;cursor:pointer;';
            node.innerHTML = text;
            node.onclick = function() {
                //alert(text);
                self._clickEvt && self._clickEvt(text);
            }
            nodebox.appendChild(node);

            //引用
            nodebox.childrenbox = childrenbox;
            //将当前节点加入到目标节点
            if(parent) {
                parent.childrenbox.appendChild(nodebox);
                parent.childrenbox.appendChild(childrenbox);
                nodebox._parent = parent;
            }
            return nodebox;
        },
        loopTree: function(root) {
            var nodeStack = new Stack(),
                htmlStack = new Stack(),
                node = null,
                isLeaf = true,
                htmlNode = null,
                htmlRootNode = null;
            
            nodeStack.push(root);
            //创建根节点
            htmlNode = htmlRootNode = this.createNode({
                text: root.name,
                data: root.data,
                parent: htmlNode,
                isLeaf: false,
                isLastNode: true
            });
            htmlStack.push(htmlNode);
            
            while(!nodeStack.isEmpty()) {
                node = nodeStack.pop();
                htmlNode = htmlStack.pop();
                
                //判断是否是叶节点
                if(node.children) {
                    for(var i = 0 ; i < node.children.length ; i++) {
                        nodeStack.push(node.children[i]);

                        //判断是否为叶节点
                        if(node.children[i].children) 
                            isleaf = false;
                        else isleaf = true;

                        //判断是否为当前子节点集的最后一个节点
                        var isLast = false;
                        if(i == node.children.length-1) {
                            isLast = true;
                        }
                
                        //创建子节点
                        var tempNode = this.createNode({
                            text: node.children[i].name,
                            data: node.children[i].data,
                            parent: htmlNode,
                            isLeaf: isleaf,
                            isLastNode: isLast
                        });
                        htmlStack.push(tempNode);
                    }
                }
            }
            if(this._showRoot) {
                dom.getElementById(this._renderTo).appendChild(htmlRootNode);
            }
            dom.getElementById(this._renderTo).appendChild(htmlRootNode.childrenbox);
            //设置根节点
            this._htmlRootNode = htmlRootNode;
        }
    }
    w.JsTree = Tree;
} (window));

以上是javascript tree核心库, 下面是调用方式

//数据格式
var jsonTree = [{
    name: 'ROOT根节点',
    data: '用户数据'
    children: [
        {name: 'java'},
        {name: 'object-c'},
        {name: 'javascript'}
    ]
}];
var tree = new JsTree({
    treedata: jsonTree,
    renderTo: 'treebox',
    showRoot: false
});
//添加节点单击事件
tree.addClickEvt(function(o) {
    alert(o);
});
// 展开
tree.expand();
// 收缩
tree.shrink();

目标只实现了三个接口,添加单机事件,展开,收缩... 最后来一个列子吧