浏览 4115 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2009-07-18
我们分成3部分看,第一部分是前面几个以Node结尾的类,这些类代表是树上的节点,其中最顶层的是Node,这个类是其他几种Node的父类。Node中有节点所具有的所有属性和方法,比如children,parent,depth,expand(),collapse()等等。 第二部分是TreeView这个类,这个类就像是一棵树的骨架,他有根有树干有树枝,可以把上面介绍的Node节点挂在Treeview上,Treeview会统一管理记录这些节点。通过TreeView你可以很方便的定位到一个或一些节点,比如 getNodeByElement(),getNodeByIndex, getNodeByProperty(),getNodesByProperty()。通过TreeView可以控制一棵树是展开还是合拢,可以移除一些节点等等。TreeView就是在全局上控制树上的Nodes。 第三部分是最后三个类,这三个类主要是个树增加一些动画的效果。 既然TreeView是整棵树的总枢纽,我们就先从TreeView入手(由于TreeView的 源码太长,我就不直接贴出来,大家可以对照着源码看,碰到比较重要的代码我会把它们贴出来)。 首先是TreeView的构造函数:YAHOO.widget.TreeView ( id ),构造方法只有一个参数,就是HTML中的Div元素的ID,这就像是给树挖个坑中下去,以后树在页面上显示就有地方了。构造方法里面调用了TreeView的Init方法,这个方法我们稍后再说。 TreeView里面有不少属性,不过大多都不用太深究,无非是用来与HTML元素做绑定,记录TreeView的状态等等,这些属性我们只要构建好了树,就由树内部做管理,一般情况下我们都不会使用到。下面挑几个重要的属性做说明: YAHOO.widget.TreeView.nodeCount,这是一个静态属性,这个属性记录TreeView上面Node的个数,也就是每次生成一个Node到TreeView上,这个属性值就增加1,这个属性有一个作用就是给Node分配一个唯一的标识,每个Node都有一个属性叫做index,这个index就是由这个属性来分配的。由于这个属性值只增不减,所以永远不会出现重复的。 YAHOO.widget.TreeView.trees,这是一个私有属性,它是一个关联数组,用来缓存所有的TreeView实例,每次生成一个TreeView的实例就会往trees里面设置一个key(TreeView的Id)和value(TreeView的实例)。这样方便以后通过Id可以直接取到TreeView的实例。TreeView有一个方法:YAHOO.widget.TreeView.getTree(treeId),看看它的实现: YAHOO.widget.TreeView.getTree = function(treeId) { var t = YAHOO.widget.TreeView.trees[treeId];//通过trees属性取值 return (t) ? t : null; }; YAHOO.widget.TreeView.counter,这个也是私有属性,用来给TreeView生成一个唯一的ID, generateId: function(el) { var id = el.id; if (!id) { id = "yui-tv-auto-id-" + YAHOO.widget.TreeView.counter; ++YAHOO.widget.TreeView.counter; } return id; }, 这个属性的值同样是递增的,generateId方法生成的Id,在TreeView的init方法中就直接赋值给Treeview实例。 说到TreeView的init方法,我们就仔细看看它做了什么。 init: function(id) { this.id = id; if ("string" !== typeof id) { this._el = id; this.id = this.generateId(id);//通过YAHOO.widget.TreeView.counter生成 } this.createEvent("animStart", this); this.createEvent("animComplete", this); this.createEvent("collapse", this); this.createEvent("collapseComplete", this); this.createEvent("expand", this); this.createEvent("expandComplete", this); this._nodes = []; // store a global reference YAHOO.widget.TreeView.trees[this.id] = this; // Set up the root node this.root = new YAHOO.widget.RootNode(this); var LW = YAHOO.widget.LogWriter; this.logger = (LW) ? new LW(this.toString()) : YAHOO; this.logger.log("tree init: " + this.id); }, Init方法做的事情很简单,给TreeView分配一个Id,创建一些事件,把TreeView实例加入到YAHOO.widget.TreeView.trees数组中,生成一个RootNode。还记得我们往树上加第一个节点的时候,父节点就是用的这个RootNode,通过treeView.getRoot()这个方法可以取得。 在上次的那个例子中,最后调用了treeView.draw()来显示树,现在我们看看这个方法的实现: draw: function() { var html = this.root.getHtml(); this.getEl().innerHTML = html; this.firstDraw = false; }, this.root.getHtml()这个方法通过RootNode的getHtml得到节点的Html表示,this.getEl(),取得TreeView对应Div的对象,然后设置innerHTML。 TreeView类的介绍就到这,TreeView还有很多控制相关的方法,比如expand,collpase(),removeChildren(),getNodeByIndex()等等,这些方法实现都比较简单,而且不知道他们的实现对使用也没有什么影响,所以就不对这些方法做具体的介绍,有兴趣可以自己查看源码。 下面要说的是几个以Node结尾的类,先从顶层的YAHOO.widget.Node开始。 YAHOO.widget.Node = function(oData, oParent, expanded) { if (oData) { this.init(oData, oParent, expanded); } }; ………. init: function(oData, oParent, expanded) { this.data = oData; this.children = []; this.index = YAHOO.widget.TreeView.nodeCount; ++YAHOO.widget.TreeView.nodeCount; this.expanded = expanded; this.logger = new YAHOO.widget.LogWriter(this.toString()); this.createEvent("parentChange", this); if (oParent) { oParent.appendChild(this); } }, ……… 上面这个是构造函数和Init方法,生成一个Node需要三个参数,第一个oData,这个属性可以是任意类型的,可以是Object,这一点很重要,以后扩展树的时候可以传入你定义好的数据。第二个属性是oParent,就是你构造这个节点的父亲,值可以是树上的其他节点,第三个属性expanded,有true和false两个值,true的时候默认节点展开,false的时候不展开。 Init方法做一些初始化工作,this.index = YAHOO.widget.TreeView.nodeCount;这个是给每个Node一个唯一Id,之前介绍TreeView类的时候有说到YAHOO.widget.TreeView.nodeCount这个属性。This.children用来存储它的所有子节点,最后oParent.appendChild(this);把新生成的Node加入到oParent的children数组中。 下面把构造的过程画一张图 (图中的椭圆为属性): 我把余下的方法进行了下简单的分类便于大家理解掌握 /*插入节点的方法*/ appendTo(parentNode) { return parentNode.appendChild(this); }//很常用的插入节点的方法 但被插入到了参数节点子节点的最后一个 insertBefore: function(node) 如果这个节点在一个树里则先从这棵树里把该节点删除(注意:appendTo可没有删) 然后获取参数Node父节点的孩子列表以及该参数Node的位置以此操作该孩子列表在参数节点的位置前面添加 实现添加。然后重定向该节点和参数Node的前后sibling属性 最后一步 调用applyParent()方法 重设depth同时也把自己的子节点也引过来了 insertAfter: function(node) 机理同上 只在第二步操作的位置不一样罢了 /*几个获取ID的方法*/ getElId() 返回这个节点容器div的id id结构:"ygtv" + this.index getChildrenElId() 返回节点的Children节点DIV的id 注意是所有子节点在一个DIV里 id结构 "ygtvc" + this.index; getToggleElId() 返回节点的toggle的id id结构:"ygtvt"+ this.index toggle区域就是 树种节点的左边的+ -号的区域 *这些个方法都对应有通过id获取到对象的方法 getEl() getChildrenEl() getToggleEl() /*节点的收起展开*/(可以添加一些自定义的事件) collapse() 隐藏起子节点(如果有必要的话可创建子节点) 核心是:一堆判断之后 调用 this.hideChildren()方法 此方法即获取孩子区域的对象将其display属性设为none 当然了,也调用了this.updateIcon()方法 此方法用于更新了左边的+ -显示 expand(boolean) 显示子节点 核心:也是一堆的判断 之后调用this.showChildren() 此方法即获取孩子区域的对象将其display属性设为"" expandAll() 和 collapseAll() 很显然 这两个方法是同时展开或收起所有节点以下的节点 /*这个跟动态加载相关*/ setDynamicLoad(fnDataLoader, iconMode) 设置第一次展开节点时动态加载子节点数据的方法 获取方法通过参数fnDataLoader传递给节点的dataLoader属性 fnDataLoader方法的参数 第一个是Node节点类型 第二个是回调函数 如果不设置回调函数的话 在第一次加载之后节点 会关闭动态生成(即设dynamicLoadComplete为true )因为你不指定的话 会调用默认的 setDynamicLoad()第二个参数可选 int型 会赋给iconMode属性(具体用途见前面的属性解析) /*这个很重要跟自定义节点有关*/ getNodeHtml() 这个方法被设计用来被重写的 本身返回空字符串 以便支持不同种的Node. 方法会在生成节点的时候把取得的html写入节点,从而产生了各种不同的节点样式 getHtml() 这个方法= getNodeHtml()+getChildrenHtml() getChildrenHtml() 在构建树的时候会被调用 我们总是先构建装子节点的div 但并不提供进去信息 除非这个节点被展开 refresh() 将节点getHtml()获得的html写入到子节点的区域里 getStyle() 获取节点应有的左边+ -号状态 updateIcon()方法会调用 isRoot() 返回节点是否为根节点 isChildOf(parentNode) 获取该节点在父节点的孩子节点列表中的位置 insertBefore/insertAfter方法第二步调用了该方法我自己测试的从0开始。 getSiblings() 返回所有的兄弟节点 包括节点自己 isDynamic() 返回节点的子节点是否应该是被动态生成的 getAncestor: function(depth) 返回节点某一深度的祖先 YAHOO.widget.Node就介绍到这,接下来看看继承自YAHOO.widget.Node的其他Node。YAHOO.widget.RootNode的源码如下: YAHOO.widget.RootNode = function(oTree) { this.init(null, null, true); this.tree = oTree; }; YAHOO.extend(YAHOO.widget.RootNode, YAHOO.widget.Node, { // overrides YAHOO.widget.Node getNodeHtml: function() { return ""; }, toString: function() { return "RootNode"; }, loadComplete: function() { this.tree.draw(); }, collapse: function() {}, expand: function() {} }); 很简单的实现,从构造函数来看,就是给RootNode指定一个tree属性。其Init方法都基本传入的是null,调用YAHOO.widget.Node的Init方法。getNodeHtml返回””,就是说RootNode不能显示出来,是一个隐藏的节点。其他几个方法的实现都很简单,不用多说。 YAHOO.widget.HTMLNode继承自YAHOO.widget.Node,看源码可以发现HTMLNode多加了三个属性,构造函数也加了一个属性,叫hasIcon,这个属性是用来控制是否要显示树前面的那些加号、减号以及虚线等图标的。 YAHOO.widget.HTMLNode = function(oData, oParent, expanded, hasIcon) { if (oData) { this.init(oData, oParent, expanded); this.initContent(oData, hasIcon); } }; YAHOO.extend(YAHOO.widget.HTMLNode, YAHOO.widget.Node, { contentStyle: "ygtvhtml", contentElId: null, html: null, 增加的三个属性分别是: contentStyle: "ygtvhtml",设置显示的CSS样式 contentElId: null,给Content Element设置一个ID html: null,用来显示的Html内容 它重写的方法有toString(),getNodeHtml(),getNodeHtml()是节点显示的主要方法。 它增加的方法有initContent: function(oData, hasIcon),setHtml: function(o),这些方法的实现都很简单,不用详细说明。 YAHOO.widget.TextNode是比较常用的一种Node,它根据你传入的oData来构造显示,如果传入的oData是String,则把String当作Label显示出来,如果传入的是Object,则根据Object的label属性来显示,同时还可以给Object设置style,title等属性,可以很灵活的控制节点要显示成什么样,要显示哪些信息。 具体的显示它重写了getNodeHtml()方法。新加了onLabelClick方法,在点击树节点的Label的时候触发。 最后是YAHOO.widget.MenuNode ,它继承自YAHOO.widget.TextNode,只从写了toString()方法。 通过分析上面几个继承自YAHOO.widget.Node的Node,可以发现实现一个Node很简单,在init方法里面加入一些新的操作,加入一些新的属性,最后重写一些方法,就能得出符合自己要求的Node。YUI在Node的设计上做得是相当不错的,给了我们很方便的方法来扩展已有的Node。下一次我将会详细的讲解如何构建满足自己需求的Node。 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |