UPDATE:回答网友提出的设置节点的自定义图片的问题,同时欢迎大家提问,我尽量在第一时间回复,详见最后 2009-11-03

项目中经常会遇到树形数据的展现,包括导航,选择等功能,所以树控件在大多项目中都是必须的。那一个实用的树应该具备什么功能呢?

根据我的项目实践情况,主要是几个关键点:

1:支持静态的树,即一次性将全部数据加载到客户端。

2:异步树,即一次只加载一级或若干级节点,子节点可以异步加载数据。

3:Checkbox树(可能是静态树也可能是异步树),用于选择(如选择组织机构,选择数据字典项)等,最好是能够支持节点级联(这个是难点)

4:能够承载大数据量,并性能表现优异

5:能够在主流浏览器中运行良好

那我要打造的TreeView就是为了实现这个5个主要指标的。

先来看下效果图

打造jQuery的高性能TreeView_ico

上图是中国行政区域的数据树,总共得节点是3500+。

那么我们要开工了;

1:第一个确定的节点Dom结构(即用什么样的HTML来构建节点)

  • 比较土的是table套table的(样式上好控制,但是大数据量,和层次较深的树,这种结构肯定顶不住的)
  • 还有一种是比较新鲜的UL套LI的方式,这是现下很多书采取的方式如​​Jquery.treeview​​就是采用的这种格式,好处比较明显就是结构简洁明了,
    而且在不支持Js的浏览器上,同样也能呈现出树的形状(这种情况其实咱可以忽略),但是Jquery.treeview的节点在IE下,特别是IE6下无法被内部元素撑开,(IE7,8当达到一定深度时无法撑开),请奇怪的现象(我猜测是因为使用padding来做缩进,margin-left:负值来控制图标位置有关,但是修改起来难度也较大),在这种情况下书会变形(Jquery.treeview)就有这种问题,只能通过设置节点的width来解决。

打造jQuery的高性能TreeView_html_02

JQuery.treeview的节点结构

打造jQuery的高性能TreeView_html_03

Jquery.TreeView  IE6 下 展开第三级即出现错位

打造jQuery的高性能TreeView_html_04​ IE8下展开到第5级

  • 还有一些是div套table的方式,CSDN的导航树就是这种,是种折中的方法(节点也不算太复杂,而且CSS也比较好写),如下图所示
    打造jQuery的高性能TreeView_html_05

 

 

 

而我采用的也是第二种方式,但是缩进采用了填空的方式,即缩进的位置用空白的图片填充来避免Jquery.treeview的问题

打造jQuery的高性能TreeView_数据_06

我的树节点结构

确定了节点的HTML我们就可以来写CSS了。有了效果图,有个节点结构接着就编写CSS了

下面是CSS的完整代码




​+ View Code​​​​?​



1


2


3


4


5


6


7


8


9


10


11


12


13


14


15


16


17


18


19


20


21


22


23


24


25


26


27


28


29


30


31


32


33


34


35


36


37


38


39


40


41


42


43


44


45


46


47


48


49


50


51


52


53


54


55


56


57


58


59


60


61


62


63


64


65


66


67


68


69


70


71


72


73


74


75


76


77


78


79


80


81


82


83


84


85


86


87


88


89


90


91


92


93


94


95


96


97


98


99


100


101


102


103


104


105


106


107


108


109


110


111


112


113


114


115


116


117


118


119


120


121


122


123


124


125


126


127


128


129


130


131


132


133


134


135


136


137


138


139


140


141


142


143


144


145


146


147


148


149


150


151


152


153


154


155


156


157


158


159


160


161


162


163


164


165


166


167


168


169


170


171


172


173


174


175


176


177


178


179


180


181


182


183


184


185


186


187


188


189


190


191


192


193


194


195


196


197


198


199


200


201


202


203


204


205


206


207


208


209


210


211


212


213


214


215


216


217


218


219


220


221


222


223


224


225


226


227


228


229


230


231


232


233


234


235


236


237


238


239


240


241


242


243


244


245


246


247


248


249


250


251


252


253


254


255


256


257


258


259


260


261


262


263


264


265


266


267


268


269




​.ie .bbit-tree .bbit-tree-bwrap{​


 


​}​


​.bbit-tree ul,.bbit-tree li​


​{​


​list-style-type​​​​:​​​​none​​​​;​


​margin​​​​:​​​​0px​​​​;​


​padding​​​​:​​​​0px​​​​;​


​}​


​.bbit-tree-body​


​{​


​font-size​​​​:​​​​12px​​​​;​


​}​


​.bbit-tree-​​​​icon​​​​, .bbit-tree-ec-​​​​icon​​​​, .bbit-tree-node-cb,.bbit-tree-elbow-line, .bbit-tree-elbow, .bbit-tree-elbow-end, .bbit-tree-elbow-plus, .bbit-tree-elbow-minus, .bbit-tree-elbow-end-plus, .bbit-tree-elbow-end-minus{​


​border​​​​: ​​​​0​​ ​​none​​​​;​


​height​​​​: ​​​​18px​​​​;​


​margin​​​​: ​​​​0​​​​;​


​padding​​​​: ​​​​0​​​​;​


​vertical-align​​​​: ​​​​top​​​​;​


​width​​​​: ​​​​16px​​​​;​


​background-repeat​​​​: ​​​​no-repeat​​​​;​


​}​


​.bbit-tree-node-cb​


​{​


​height​​​​:​​​​16px​​​​;​


​}​


​.bbit-tree-node-collapsed .bbit-tree-node-​​​​icon​​​​, .bbit-tree-node-​​​​expanded​​ ​​.bbit-tree-node-​​​​icon​​​​, .bbit-tree-node-leaf .bbit-tree-node-​​​​icon​​​​{​


​border​​​​: ​​​​0​​ ​​none​​​​;​


​height​​​​: ​​​​18px​​​​;​


​margin​​​​: ​​​​0​​​​;​


​padding​​​​: ​​​​0​​​​;​


​vertical-align​​​​: ​​​​top​​​​;​


​width​​​​: ​​​​16px​​​​;​


​background-position​​​​:​​​​center​​​​;​


​background-repeat​​​​: ​​​​no-repeat​​​​;​


​}​


 


​.ie .bbit-tree-node-indent img, .ie .bbit-tree-node-​​​​icon​​​​, .ie .bbit-tree-ec-​​​​icon​​ ​​{​


​vertical-align​​​​:​​​​middle​​ ​​!important​​​​;​


​}​


 


​.bbit-tree-noicon .bbit-tree-node-​​​​icon​​​​{​


​width​​​​:​​​​0​​​​; ​​​​height​​​​:​​​​0​​​​;​


​}​


 


​/* No line styles 没有线的样式 */​


​.bbit-tree-no-lines .bbit-tree-elbow{​


​background​​​​:​​​​transparent​​​​;​


​}​


 


​.bbit-tree-no-lines .bbit-tree-elbow-end{​


​background​​​​:​​​​transparent​​​​;​


​}​


 


​.bbit-tree-no-lines .bbit-tree-elbow-line{​


​background​​​​:​​​​transparent​​​​;​


​}​


 


​/* Arrows Vista系统树的样式只有箭头*/​


​.bbit-tree-arrows .bbit-tree-elbow{​


​background​​​​:​​​​transparent​​​​;​


​}​


 


​.bbit-tree-arrows .bbit-tree-elbow-plus{​


​background​​​​:​​​​transparent​​ ​​no-repeat​​ ​​0​​ ​​0​​​​;​


​}​


 


​.bbit-tree-arrows .bbit-tree-elbow-minus{​


​background​​​​:​​​​transparent​​ ​​no-repeat​​ ​​-16px​​ ​​0​​​​;​


​}​


 


​.bbit-tree-arrows .bbit-tree-elbow-end{​


​background​​​​:​​​​transparent​​​​;​


​}​


 


​.bbit-tree-arrows .bbit-tree-elbow-end-plus{​


​background​​​​:​​​​transparent​​ ​​no-repeat​​ ​​0​​ ​​0​​​​;​


​}​


 


​.bbit-tree-arrows .bbit-tree-elbow-end-minus{​


​background​​​​:​​​​transparent​​ ​​no-repeat​​ ​​-16px​​ ​​0​​​​;​


​}​


 


​.bbit-tree-arrows .bbit-tree-elbow-line{​


​background​​​​:​​​​transparent​​​​;​


​}​


 


​.bbit-tree-arrows .bbit-tree-ec-over .bbit-tree-elbow-plus{​


​background-position​​​​:​​​​-32px​​ ​​0​​​​;​


​}​


 


​.bbit-tree-arrows .bbit-tree-ec-over .bbit-tree-elbow-minus{​


​background-position​​​​:​​​​-48px​​ ​​0​​​​;​


​}​


 


​.bbit-tree-arrows .bbit-tree-ec-over .bbit-tree-elbow-end-plus{​


​background-position​​​​:​​​​-32px​​ ​​0​​​​;​


​}​


 


​.bbit-tree-arrows .bbit-tree-ec-over .bbit-tree-elbow-end-minus{​


​background-position​​​​:​​​​-48px​​ ​​0​​​​;​


​}​


 


​.bbit-tree-elbow-plus, .bbit-tree-elbow-minus, .bbit-tree-elbow-end-plus, .bbit-tree-elbow-end-minus{​


​cursor​​​​:​​​​pointer​​​​;​


​}​


 


​.ie ul.bbit-tree-node-ct{​


​font-size​​​​:​​​​0​​​​;​


​line-height​​​​:​​​​0​​​​;​


​zoom:​​​​1​​​​;​


​}​


 


​.bbit-tree-node{​


​white-space​​​​: ​​​​nowrap​​​​;​


​}​


 


​.bbit-tree-node-el {​


​line-height​​​​:​​​​18px​​​​;​


​cursor​​​​:​​​​default​​​​;​


​/* cursor:pointer;*/​


​}​


 


​.bbit-tree-node a{​


​text-decoration​​​​:​​​​none​​​​;​


​-khtml-user-select:​​​​none​​​​;​


​-moz-user-select:​​​​none​​​​;​


​-webkit-user-select:ignore;​


​-kthml-user-focus:​​​​normal​​​​;​


​-moz-user-focus:​​​​normal​​​​;​


​-moz-​​​​outline​​​​: ​​​​0​​ ​​none​​​​;​


​outline​​​​:​​​​0​​ ​​none​​​​;​


​}​


 


​.bbit-tree-node a span{​


​text-decoration​​​​:​​​​none​​​​;​


​padding​​​​:​​​​1px​​ ​​3px​​ ​​1px​​ ​​2px​​​​;​


​}​


 


​.bbit-tree-node .bbit-tree-node-disabled .bbit-tree-node-​​​​icon​​​​{​


​-moz-opacity: ​​​​0.5​​​​;​


​opacity:.​​​​5​​​​;​


​filter: alpha(opacity=​​​​50​​​​);​


​}​


 


​.bbit-tree-node .bbit-tree-node-inline-​​​​icon​​​​{​


​background​​​​:​​​​transparent​​​​;​


​}​


 


​.bbit-tree-node a:hover{​


​text-decoration​​​​:​​​​none​​​​;​


​}​


 


 


 


​/* Fix for ie rootVisible:false issue,修正一个IEdebug */​


​.bbit-tree-root {​


​zoom:​​​​1​​​​;​


​}​


 


 


​/***********这里是图标了,可以在这里替换哦*****************/​


​.bbit-tree-node-​​​​expanded​​ ​​.bbit-tree-node-​​​​icon​​​​{​


​background-image​​​​:​​​​url​​​​(images/tree/folder-open.gif);​


​}​


 


​.bbit-tree-node-leaf .bbit-tree-node-​​​​icon​​​​{​


​background-image​​​​:​​​​url​​​​(images/tree/leaf.gif);​


​}​


 


​.bbit-tree-node-collapsed .bbit-tree-node-​​​​icon​​​​{​


​background-image​​​​:​​​​url​​​​(images/tree/folder.gif);​


​}​


 


​.bbit-tree-node-loading .bbit-tree-node-​​​​icon​​​​{​


​background-image​​​​:​​​​url​​​​(images/tree/loading.gif) ​​​​!important​​​​;​


​}​


 


​.bbit-tree-node .bbit-tree-node-inline-​​​​icon​​ ​​{​


​background-image​​​​: ​​​​none​​​​;​


​}​


 


​.bbit-tree-node-loading a span{​


​font-style​​​​: ​​​​italic​​​​;​


​color​​​​:​​​​#444444​​​​;​


​}​


 


​.bbit-tree-lines .bbit-tree-elbow{​


​background-image​​​​:​​​​url​​​​(images/tree/elbow.gif);​


​}​


 


​.bbit-tree-lines .bbit-tree-elbow-plus{​


​background-image​​​​:​​​​url​​​​(images/tree/elbow-plus.gif);​


​}​


 


​.bbit-tree-lines .bbit-tree-elbow-minus{​


​background-image​​​​:​​​​url​​​​(images/tree/elbow-minus.gif);​


​}​


 


​.bbit-tree-lines .bbit-tree-elbow-end{​


​background-image​​​​:​​​​url​​​​(images/tree/elbow-end.gif);​


​}​


 


​.bbit-tree-lines .bbit-tree-elbow-end-plus{​


​background-image​​​​:​​​​url​​​​(images/tree/elbow-end-plus.gif);​


​}​


 


​.bbit-tree-lines .bbit-tree-elbow-end-minus{​


​background-image​​​​:​​​​url​​​​(images/tree/elbow-end-minus.gif);​


​}​


 


​.bbit-tree-lines .bbit-tree-elbow-line{​


​background-image​​​​:​​​​url​​​​(images/tree/elbow-line.gif);​


​}​


 


​.bbit-tree-no-lines .bbit-tree-elbow-plus{​


​background-image​​​​:​​​​url​​​​(images/tree/elbow-plus-nl.gif);​


​}​


 


​.bbit-tree-no-lines .bbit-tree-elbow-minus{​


​background-image​​​​:​​​​url​​​​(images/tree/elbow-minus-nl.gif);​


​}​


 


​.bbit-tree-no-lines .bbit-tree-elbow-end-plus{​


​background-image​​​​:​​​​url​​​​(images/tree/elbow-end-plus-nl.gif);​


​}​


 


​.bbit-tree-no-lines .bbit-tree-elbow-end-minus{​


​background-image​​​​:​​​​url​​​​(images/tree/elbow-end-minus-nl.gif);​


​}​


 


​.bbit-tree-arrows .bbit-tree-elbow-plus{​


​background-image​​​​:​​​​url​​​​(images/tree/arrows.gif);​


​}​


 


​.bbit-tree-arrows .bbit-tree-elbow-minus{​


​background-image​​​​:​​​​url​​​​(images/tree/arrows.gif);​


​}​


 


​.bbit-tree-arrows .bbit-tree-elbow-end-plus{​


​background-image​​​​:​​​​url​​​​(images/tree/arrows.gif);​


​}​


 


​.bbit-tree-arrows .bbit-tree-elbow-end-minus{​


​background-image​​​​:​​​​url​​​​(images/tree/arrows.gif);​


​}​


 


​/*TreeNode 选中的Disabled的一些颜色,字体样式*/​


​.bbit-tree-node{​


​color​​​​:​​​​#000​​​​;​


​font​​​​: ​​​​normal​​ ​​11px​​ ​​arial​​​​, ​​​​tahoma​​​​, ​​​​helvetica​​​​, ​​​​sans-serif​​​​;​


​}​


 


​.bbit-tree-node a{​


​color​​​​:​​​​#000​​​​;​


​}​


​.bbit-tree-node a span{​


​color​​​​:​​​​#000​​​​;​


​}​


​.bbit-tree-node .bbit-tree-node-disabled a span{​


​color​​​​:​​​​gray​​ ​​!important​​​​;​


​}​


​.bbit-tree-node .bbit-tree-node-over {​


​background-color​​​​: ​​​​#eee​​​​;​


​}​


 


​.bbit-tree-node .bbit-tree-selected {​


​background-color​​​​: ​​​​#d9e8fb​​​​;​


​}​



上面了树的基本样式外,定义了一个有+号带line的样式和+号不带line的样式

打造jQuery的高性能TreeView_数据_07​ 这就是那个+号带line的样式

css中所用到的所有图片

打造jQuery的高性能TreeView_子节点_08​ ​打造jQuery的高性能TreeView_子节点_09​ ​打造jQuery的高性能TreeView_html_10​ ​打造jQuery的高性能TreeView_ico_11​ ​打造jQuery的高性能TreeView_数据_12​ ​打造jQuery的高性能TreeView_数据_13​ ​打造jQuery的高性能TreeView_html_14​ ​打造jQuery的高性能TreeView_子节点_15​ ​打造jQuery的高性能TreeView_jquery_16​ ​打造jQuery的高性能TreeView_ico_17​ ​打造jQuery的高性能TreeView_jquery_18​ ​打造jQuery的高性能TreeView_数据_19​ ​打造jQuery的高性能TreeView_html_20​ ​打造jQuery的高性能TreeView_子节点_21​ ​打造jQuery的高性能TreeView_子节点_22​ ​打造jQuery的高性能TreeView_数据_23

打造jQuery的高性能TreeView_html_24​ ​打造jQuery的高性能TreeView_jquery_25​ ​打造jQuery的高性能TreeView_jquery_26

2:确定数据结构




​?​



1


2


3


4


5


6


7


8


9


10


11


12


13


14


15


16


17


18


19


20


21


22


23




​var​​ ​​menudata = [{​


​id: ​​​​"0.1"​​​​,​​​​//唯一的ID即可​


​text: ​​​​"Beyondbit UI Demo"​​​​,​


​hasChildren: ​​​​true​​​​,​


​isexpand: ​​​​true​​​​,​


​complete: ​​​​true​​​​,​


​ChildNodes: [{​


​id: ​​​​"0.1.1"​​​​,​


​text: ​​​​"日期选择"​​​​,​


​hasChildren: ​​​​true​​​​,​


​isexpand: ​​​​false​​​​,​


​complete: ​​​​true​​​​,​


​ChildNodes: [{​


​id: ​​​​"0.1.1.1"​​​​,​


​text: ​​​​"控件演示"​​​​,​


​value: ​​​​"Testpages/datepickerDemo.htm"​​​​,​


​hasChildren: ​​​​false​​​​,​


​isexpand: ​​​​false​​​​,​


​complete: ​​​​true​​​​,​


​ChildNodes: ​​​​null​


​},​


​...​


​]​



 

 

这样的结构有个好处就数据本身是带层次的,非常利于遍历,在后面的级联关联中会看到

3: 面子做好了那就开始做里子了,编写脚本(Javascript)

我是JQuery得拥护者,所以自然js的框架自然是采用Jquery了

先上个完整代码,再逐一分析




​+ View Code​​​​?​



1


2


3


4


5


6


7


8


9


10


11


12


13


14


15


16


17


18


19


20


21


22


23


24


25


26


27


28


29


30


31


32


33


34


35


36


37


38


39


40


41


42


43


44


45


46


47


48


49


50


51


52


53


54


55


56


57


58


59


60


61


62


63


64


65


66


67


68


69


70


71


72


73


74


75


76


77


78


79


80


81


82


83


84


85


86


87


88


89


90


91


92


93


94


95


96


97


98


99


100


101


102


103


104


105


106


107


108


109


110


111


112


113


114


115


116


117


118


119


120


121


122


123


124


125


126


127


128


129


130


131


132


133


134


135


136


137


138


139


140


141


142


143


144


145


146


147


148


149


150


151


152


153


154


155


156


157


158


159


160


161


162


163


164


165


166


167


168


169


170


171


172


173


174


175


176


177


178


179


180


181


182


183


184


185


186


187


188


189


190


191


192


193


194


195


196


197


198


199


200


201


202


203


204


205


206


207


208


209


210


211


212


213


214


215


216


217


218


219


220


221


222


223


224


225


226


227


228


229


230


231


232


233


234


235


236


237


238


239


240


241


242


243


244


245


246


247


248


249


250


251


252


253


254


255


256


257


258


259


260


261


262


263


264


265


266


267


268


269


270


271


272


273


274


275


276


277


278


279


280


281


282


283


284


285


286


287


288


289


290


291


292


293


294


295


296


297


298


299


300


301


302


303


304


305


306


307


308


309


310


311


312


313


314


315


316


317


318


319


320


321


322


323


324


325


326


327


328


329


330


331


332


333


334


335


336


337


338


339


340


341


342


343


344


345


346


347


348


349


350


351


352


353


354


355


356


357


358


359


360


361


362


363


364


365


366


367


368


369


370


371


372


373


374


375


376


377


378


379


380


381


382


383




​/****************************************​


​author:xuanye.wan@gmail.com​


​page:http://xuanye.cnblogs.com/​


​***************************************/​


​(​​​​function​​​​($) {​


​$.fn.swapClass = ​​​​function​​​​(c1, c2) {​


​return​​ ​​this​​​​.removeClass(c1).addClass(c2);​


​}​


​$.fn.switchClass = ​​​​function​​​​(c1, c2) {​


​if​​ ​​(​​​​this​​​​.hasClass(c1)) {​


​return​​ ​​this​​​​.swapClass(c1, c2);​


​}​


​else​​ ​​{​


​return​​ ​​this​​​​.swapClass(c2, c1);​


​}​


​}​


​$.fn.treeview = ​​​​function​​​​(settings) {​


​var​​ ​​dfop =​


​{​


​method: ​​​​"POST"​​​​,​


​datatype: ​​​​"json"​​​​,​


​url: ​​​​false​​​​,​


​cbiconpath: ​​​​"/images/icons/"​​​​,​


​icons: [​​​​"checkbox_0.gif"​​​​, ​​​​"checkbox_1.gif"​​​​, ​​​​"checkbox_2.gif"​​​​],​


​showcheck: ​​​​false​​​​, ​​​​//是否显示选择            ​


​oncheckboxclick: ​​​​false​​​​, ​​​​//当checkstate状态变化时所触发的事件,但是不会触发因级联选择而引起的变化​


​onnodeclick: ​​​​false​​​​,​


​cascadecheck: ​​​​true​​​​,​


​data: ​​​​null​​​​,​


​clicktoggle: ​​​​true​​​​, ​​​​//点击节点展开和收缩子节点​


​theme: ​​​​"bbit-tree-arrows"​​ ​​//bbit-tree-lines ,bbit-tree-no-lines,bbit-tree-arrows​


​}​


 


​$.extend(dfop, settings);​


​var​​ ​​treenodes = dfop.data;​


​var​​ ​​me = $(​​​​this​​​​);​


​var​​ ​​id = me.attr(​​​​"id"​​​​);​


​if​​ ​​(id == ​​​​null​​ ​​|| id == ​​​​""​​​​) {​


​id = ​​​​"bbtree"​​ ​​+ ​​​​new​​ ​​Date().getTime();​


​me.attr(​​​​"id"​​​​, id);​


​}​


 


​var​​ ​​html = [];​


​buildtree(dfop.data, html);​


​me.addClass(​​​​"bbit-tree"​​​​).html(html.join(​​​​""​​​​));​


​InitEvent(me);​


​html = ​​​​null​​​​;​


​//预加载图片​


​if​​ ​​(dfop.showcheck) {​


​for​​ ​​(​​​​var​​ ​​i = 0; i < 3; i++) {​


​var​​ ​​im = ​​​​new​​ ​​Image(16,16);​


​im.src = dfop.cbiconpath + dfop.icons[i];​


​}​


​}​


​//region ​


​function​​ ​​buildtree(data, ht) {​


​ht.push(​​​​"<div class='bbit-tree-bwrap'>"​​​​); ​​​​// Wrap ;​


​ht.push(​​​​"<div class='bbit-tree-body'>"​​​​); ​​​​// body ;​


​ht.push(​​​​"<ul class='bbit-tree-root "​​​​, dfop.theme, ​​​​"'>"​​​​); ​​​​//root​


​var​​ ​​l = data.length;​


​for​​ ​​(​​​​var​​ ​​i = 0; i < l; i++) {​


​buildnode(data[i], ht, 0, i, i == l - 1);​


​}​


​ht.push(​​​​"</ul>"​​​​); ​​​​// root and;​


​ht.push(​​​​"</div>"​​​​); ​​​​// body end;​


​ht.push(​​​​"</div>"​​​​); ​​​​// Wrap end;​


​}​


​//endregion​


​function​​ ​​buildnode(nd, ht, deep, path, isend) {​


​ht.push(​​​​"<li class='bbit-tree-node'>"​​​​);​


​ht.push(​​​​"<div id='"​​​​, id, ​​​​"_"​​​​, nd.id, ​​​​"' tpath='"​​​​, path, ​​​​"' unselectable='on'"​​​​);​


​var​​ ​​cs = [];​


​cs.push(​​​​"bbit-tree-node-el"​​​​);​


​if​​ ​​(nd.hasChildren) {​


​cs.push(nd.isexpand ? ​​​​"bbit-tree-node-expanded"​​ ​​: ​​​​"bbit-tree-node-collapsed"​​​​);​


​}​


​else​​ ​​{​


​cs.push(​​​​"bbit-tree-node-leaf"​​​​);​


​}​


​if​​ ​​(nd.classes) { cs.push(nd.classes); }​


 


​ht.push(​​​​" class='"​​​​, cs.join(​​​​" "​​​​), ​​​​"'>"​​​​);​


​//span indent​


​ht.push(​​​​"<span class='bbit-tree-node-indent'>"​​​​);​


​if​​ ​​(deep == 1) {​


​ht.push(​​​​"<img class='bbit-tree-icon' src='../Themes/Shared/images/s.gif'/>"​​​​);​


​}​


​else​​ ​​if​​ ​​(deep > 1) {​


​ht.push(​​​​"<img class='bbit-tree-icon' src='../Themes/Shared/images/s.gif'/>"​​​​);​


​for​​ ​​(​​​​var​​ ​​j = 1; j < deep; j++) {​


​ht.push(​​​​"<img class='bbit-tree-elbow-line' src='../Themes/Shared/images/s.gif'/>"​​​​);​


​}​


​}​


​ht.push(​​​​"</span>"​​​​);​


​//img​


​cs.length = 0;​


​if​​ ​​(nd.hasChildren) {​


​if​​ ​​(nd.isexpand) {​


​cs.push(isend ? ​​​​"bbit-tree-elbow-end-minus"​​ ​​: ​​​​"bbit-tree-elbow-minus"​​​​);​


​}​


​else​​ ​​{​


​cs.push(isend ? ​​​​"bbit-tree-elbow-end-plus"​​ ​​: ​​​​"bbit-tree-elbow-plus"​​​​);​


​}​


​}​


​else​​ ​​{​


​cs.push(isend ? ​​​​"bbit-tree-elbow-end"​​ ​​: ​​​​"bbit-tree-elbow"​​​​);​


​}​


​ht.push(​​​​"<img class='bbit-tree-ec-icon "​​​​, cs.join(​​​​" "​​​​), ​​​​"' src='../Themes/Shared/images/s.gif'/>"​​​​);​


​ht.push(​​​​"<img class='bbit-tree-node-icon' src='../Themes/Shared/images/s.gif'/>"​​​​);​


​//checkbox​


​if​​ ​​(dfop.showcheck && nd.showcheck) {​


​if​​ ​​(nd.checkstate == ​​​​null​​ ​​|| nd.checkstate == undefined) {​


​nd.checkstate = 0;​


​}​


​ht.push(​​​​"<img  id='"​​​​, id, ​​​​"_"​​​​, nd.id, ​​​​"_cb' class='bbit-tree-node-cb' src='"​​​​, dfop.cbiconpath, dfop.icons[nd.checkstate], ​​​​"'/>"​​​​);​


​}​


​//a​


​ht.push(​​​​"<a hideFocus class='bbit-tree-node-anchor' tabIndex=1 href='javascript:void(0);'>"​​​​);​


​ht.push(​​​​"<span unselectable='on'>"​​​​, nd.text, ​​​​"</span>"​​​​);​


​ht.push(​​​​"</a>"​​​​);​


​ht.push(​​​​"</div>"​​​​);​


​//Child​


​if​​ ​​(nd.hasChildren) {​


​if​​ ​​(nd.isexpand) {​


​ht.push(​​​​"<ul  class='bbit-tree-node-ct'  style='z-index: 0; position: static; visibility: visible; top: auto; left: auto;'>"​​​​);​


​if​​ ​​(nd.ChildNodes) {​


​var​​ ​​l = nd.ChildNodes.length;​


​for​​ ​​(​​​​var​​ ​​k = 0; k < l; k++) {​


​nd.ChildNodes[k].parent = nd;​


​buildnode(nd.ChildNodes[k], ht, deep + 1, path + ​​​​"."​​ ​​+ k, k == l - 1);​


​}​


​}​


​ht.push(​​​​"</ul>"​​​​);​


​}​


​else​​ ​​{​


​ht.push(​​​​"<ul style='display:none;'></ul>"​​​​);​


​}​


​}​


​ht.push(​​​​"</li>"​​​​);​


​nd.render = ​​​​true​​​​;​


​}​


​function​​ ​​getItem(path) {​


​var​​ ​​ap = path.split(​​​​"."​​​​);​


​var​​ ​​t = treenodes;​


​for​​ ​​(​​​​var​​ ​​i = 0; i < ap.length; i++) {​


​if​​ ​​(i == 0) {​


​t = t[ap[i]];​


​}​


​else​​ ​​{​


​t = t.ChildNodes[ap[i]];​


​}​


​}​


​return​​ ​​t;​


​}​


​function​​ ​​check(item, state, type) {​


​var​​ ​​pstate = item.checkstate;​


​if​​ ​​(type == 1) {​


​item.checkstate = state;​


​}​


​else​​ ​​{​​​​// 上溯​


​var​​ ​​cs = item.ChildNodes;​


​var​​ ​​l = cs.length;​


​var​​ ​​ch = ​​​​true​​​​;​


​for​​ ​​(​​​​var​​ ​​i = 0; i < l; i++) {​


​if​​ ​​((state == 1 && cs[i].checkstate != 1) || state == 0 && cs[i].checkstate != 0) {​


​ch = ​​​​false​​​​;​


​break​​​​;​


​}​


​}​


​if​​ ​​(ch) {​


​item.checkstate = state;​


​}​


​else​​ ​​{​


​item.checkstate = 2;​


​}​


​}​


​//change show           ​


​if​​ ​​(item.render && pstate != item.checkstate) {​


​var​​ ​​et = $(​​​​"#"​​ ​​+ id + ​​​​"_"​​ ​​+ item.id + ​​​​"_cb"​​​​);​


​if​​ ​​(et.length == 1) {​


​et.attr(​​​​"src"​​​​, dfop.cbiconpath + dfop.icons[item.checkstate]);​


​}​


​}​


​}​


​//遍历子节点​


​function​​ ​​cascade(fn, item, args) {​


​if​​ ​​(fn(item, args, 1) != ​​​​false​​​​) {​


​if​​ ​​(item.ChildNodes != ​​​​null​​ ​​&& item.ChildNodes.length > 0) {​


​var​​ ​​cs = item.ChildNodes;​


​for​​ ​​(​​​​var​​ ​​i = 0, len = cs.length; i < len; i++) {​


​cascade(fn, cs[i], args);​


​}​


​}​


​}​


​}​


​//冒泡的祖先​


​function​​ ​​bubble(fn, item, args) {​


​var​​ ​​p = item.parent;​


​while​​ ​​(p) {​


​if​​ ​​(fn(p, args, 0) === ​​​​false​​​​) {​


​break​​​​;​


​}​


​p = p.parent;​


​}​


​}​


​function​​ ​​nodeclick(e) {​


​var​​ ​​path = $(​​​​this​​​​).attr(​​​​"tpath"​​​​);​


​var​​ ​​et = e.target || e.srcElement;​


​var​​ ​​item = getItem(path);​


 


​//debugger;​


​if​​ ​​(et.tagName == ​​​​"IMG"​​​​) {​


​// +号需要展开​


​if​​ ​​($(et).hasClass(​​​​"bbit-tree-elbow-plus"​​​​) || $(et).hasClass(​​​​"bbit-tree-elbow-end-plus"​​​​)) {​


​var​​ ​​ul = $(​​​​this​​​​).next(); ​​​​//"bbit-tree-node-ct"​


​if​​ ​​(ul.hasClass(​​​​"bbit-tree-node-ct"​​​​)) {​


​ul.show();​


​}​


​else​​ ​​{​


​var​​ ​​deep = path.split(​​​​"."​​​​).length;​


​if​​ ​​(item.complete) {​


​item.ChildNodes != ​​​​null​​ ​​&& asnybuild(item.ChildNodes, deep, path, ul, item);​


​}​


​else​​ ​​{​


​$(​​​​this​​​​).addClass(​​​​"bbit-tree-node-loading"​​​​);​


​asnyloadc(ul, item, ​​​​function​​​​(data) {​


​item.complete = ​​​​true​​​​;​


​item.ChildNodes = data;​


​asnybuild(data, deep, path, ul, item);​


​});​


​}​


​}​


​if​​ ​​($(et).hasClass(​​​​"bbit-tree-elbow-plus"​​​​)) {​


​$(et).swapClass(​​​​"bbit-tree-elbow-plus"​​​​, ​​​​"bbit-tree-elbow-minus"​​​​);​


​}​


​else​​ ​​{​


​$(et).swapClass(​​​​"bbit-tree-elbow-end-plus"​​​​, ​​​​"bbit-tree-elbow-end-minus"​​​​);​


​}​


​$(​​​​this​​​​).swapClass(​​​​"bbit-tree-node-collapsed"​​​​, ​​​​"bbit-tree-node-expanded"​​​​);​


​}​


​else​​ ​​if​​ ​​($(et).hasClass(​​​​"bbit-tree-elbow-minus"​​​​) || $(et).hasClass(​​​​"bbit-tree-elbow-end-minus"​​​​)) {  ​​​​//- 号需要收缩                    ​


​$(​​​​this​​​​).next().hide();​


​if​​ ​​($(et).hasClass(​​​​"bbit-tree-elbow-minus"​​​​)) {​


​$(et).swapClass(​​​​"bbit-tree-elbow-minus"​​​​, ​​​​"bbit-tree-elbow-plus"​​​​);​


​}​


​else​​ ​​{​


​$(et).swapClass(​​​​"bbit-tree-elbow-end-minus"​​​​, ​​​​"bbit-tree-elbow-end-plus"​​​​);​


​}​


​$(​​​​this​​​​).swapClass(​​​​"bbit-tree-node-expanded"​​​​, ​​​​"bbit-tree-node-collapsed"​​​​);​


​}​


​else​​ ​​if​​ ​​($(et).hasClass(​​​​"bbit-tree-node-cb"​​​​)) ​​​​// 点击了Checkbox​


​{​


​var​​ ​​s = item.checkstate != 1 ? 1 : 0;​


​var​​ ​​r = ​​​​true​​​​;​


​if​​ ​​(dfop.oncheckboxclick) {​


​r = dfop.oncheckboxclick.call(et, item, s);​


​}​


​if​​ ​​(r != ​​​​false​​​​) {​


​if​​ ​​(dfop.cascadecheck) {​


​//遍历​


​cascade(check, item, s);​


​//上溯​


​bubble(check, item, s);​


​}​


​else​​ ​​{​


​check(item, s, 1);​


​}​


​}​


​}​


​}​


​else​​ ​​{​


​if​​ ​​(dfop.citem) {​


​$(​​​​"#"​​ ​​+ id + ​​​​"_"​​ ​​+ dfop.citem.id).removeClass(​​​​"bbit-tree-selected"​​​​);​


​}​


​dfop.citem = item;​


​$(​​​​this​​​​).addClass(​​​​"bbit-tree-selected"​​​​);​


​if​​ ​​(dfop.onnodeclick) {​


​dfop.onnodeclick.call(​​​​this​​​​, item);​


​}​


​}​


​}​


​function​​ ​​asnybuild(nodes, deep, path, ul, pnode) {​


​var​​ ​​l = nodes.length;​


​if​​ ​​(l > 0) {​


​var​​ ​​ht = [];​


​for​​ ​​(​​​​var​​ ​​i = 0; i < l; i++) {​


​nodes[i].parent = pnode;​


​buildnode(nodes[i], ht, deep, path + ​​​​"."​​ ​​+ i, i == l - 1);​


​}​


​ul.html(ht.join(​​​​""​​​​));​


​ht = ​​​​null​​​​;​


​InitEvent(ul);​


​}​


​ul.addClass(​​​​"bbit-tree-node-ct"​​​​).css({ ​​​​"z-index"​​​​: 0, position: ​​​​"static"​​​​, visibility: ​​​​"visible"​​​​, top: ​​​​"auto"​​​​, left: ​​​​"auto"​​​​, display: ​​​​""​​ ​​});​


​ul.prev().removeClass(​​​​"bbit-tree-node-loading"​​​​);​


​}​


​function​​ ​​asnyloadc(pul, pnode, callback) {​


​if​​ ​​(dfop.url) {​


​var​​ ​​param = builparam(pnode);​


​$.ajax({​


​type: dfop.method,​


​url: dfop.url,​


​data: param,​


​dataType: dfop.datatype,​


​success: callback,​


​error: ​​​​function​​​​(e) { alert(​​​​"error occur!"​​​​); }​


​});​


​}​


​}​


​function​​ ​​builparam(node) {​


​var​​ ​​p = [{ name: ​​​​"id"​​​​, value: encodeURIComponent(node.id) }​


​, { name: ​​​​"text"​​​​, value: encodeURIComponent(node.text) }​


​, { name: ​​​​"value"​​​​, value: encodeURIComponent(node.value) }​


​, { name: ​​​​"checkstate"​​​​, value: node.checkstate}];​


​return​​ ​​p;​


​}​


​function​​ ​​InitEvent(parent) {​


​var​​ ​​nodes = $(​​​​"li.bbit-tree-node>div"​​​​, parent);​


​nodes.each(​​​​function​​​​(e) {​


​$(​​​​this​​​​).hover(​​​​function​​​​() {​


​$(​​​​this​​​​).addClass(​​​​"bbit-tree-node-over"​​​​);​


​}, ​​​​function​​​​() {​


​$(​​​​this​​​​).removeClass(​​​​"bbit-tree-node-over"​​​​);​


​})​


​.click(nodeclick)​


​.find(​​​​"img.bbit-tree-ec-icon"​​​​).each(​​​​function​​​​(e) {​


​if​​ ​​(!$(​​​​this​​​​).hasClass(​​​​"bbit-tree-elbow"​​​​)) {​


​$(​​​​this​​​​).hover(​​​​function​​​​() {​


​$(​​​​this​​​​).parent().addClass(​​​​"bbit-tree-ec-over"​​​​);​


​}, ​​​​function​​​​() {​


​$(​​​​this​​​​).parent().removeClass(​​​​"bbit-tree-ec-over"​​​​);​


​});​


​}​


​});​


​});​


​}​


​function​​ ​​getck(items, c, fn) {​


​for​​ ​​(​​​​var​​ ​​i = 0, l = items.length; i < l; i++) {​


​items[i].checkstate == 1 && c.push(fn(items[i]));​


​if​​ ​​(items[i].ChildNodes != ​​​​null​​ ​​&& items[i].ChildNodes.length > 0) {​


​getck(items[i].ChildNodes, c, fn);​


​}​


​}​


​}​


​me[0].t = {​


​getSelectedNodes: ​​​​function​​​​() {​


​var​​ ​​s = [];​


​getck(treenodes, s, ​​​​function​​​​(item) { ​​​​return​​ ​​item });​


​return​​ ​​s;​


​},​


​getSelectedValues: ​​​​function​​​​() {​


​var​​ ​​s = [];​


​getck(treenodes, s, ​​​​function​​​​(item) { ​​​​return​​ ​​item.value });​


​return​​ ​​s;​


​},​


​getCurrentItem: ​​​​function​​​​() {​


​return​​ ​​dfop.citem;​


​}​


​};​


​return​​ ​​me;​


​}​


​//获取所有选中的节点的Value数组​


​$.fn.getTSVs = ​​​​function​​​​() {​


​if​​ ​​(​​​​this​​​​[0].t) {​


​return​​ ​​this​​​​[0].t.getSelectedValues();​


​}​


​return​​ ​​null​​​​;​


​}​


​//获取所有选中的节点的Item数组​


​$.fn.getTSNs = ​​​​function​​​​() {​


​if​​ ​​(​​​​this​​​​[0].t) {​


​return​​ ​​this​​​​[0].t.getSelectedNodes();​


​}​


​return​​ ​​null​​​​;​


​}​


​$.fn.getTCT = ​​​​function​​​​() {​


​if​​ ​​(​​​​this​​​​[0].t) {​


​return​​ ​​this​​​​[0].t.getCurrentItem();​


​}​


​return​​ ​​null​​​​;​


​}​


 


​})(jQuery);​



 

第一步:自然是所有Jquery的控件的第一步都是搭这个架子,兼容JQuery和$避免闭包,避免和其他类库冲突,接受一个参数(是个对象)




​?​



1


2


3


4


5


6




​;(​​​​function​​​​($) {​


​//也可以使用$.fn.extend(treeview:function(setting){}) ​


​$.fn.treeview = ​​​​function​​​​(settings) {​


​}​


 


​})(jQuery);​


那第二步:给控件加一些参数默认参数,同时能调用方法$.extend让最终调用时的参数覆盖默认的(如果没有则使用默认)



​?​



1


2


3


4


5


6


7


8


9


10


11


12


13


14


15




​var​​ ​​dfop ={​


​method: ​​​​"POST"​​​​,​​​​//默认采用POST提交数据​


​datatype: ​​​​"json"​​​​,​​​​//数据类型是json​


​url: ​​​​false​​​​,​​​​//异步请求的url​


​cbiconpath: ​​​​"/images/icons/"​​​​,​​​​//checkbox icon的目录位置​


​icons: [​​​​"checkbox_0.gif"​​​​, ​​​​"checkbox_1.gif"​​​​, ​​​​"checkbox_2.gif"​​​​],​​​​//checkbxo三态的图片​


​showcheck: ​​​​false​​​​, ​​​​//是否显示checkbox          ​


​oncheckboxclick: ​​​​false​​​​, ​​​​//点击checkbox时触发的事件​


​onnodeclick: ​​​​false​​​​,​​​​//点击node触发的时间​


​cascadecheck: ​​​​true​​​​,​​​​//是否启用级联​


​data: ​​​​null​​​​,​​​​//初始化数据             ​


​theme: ​​​​"bbit-tree-arrows"​​ ​​//三种风格备选bbit-tree-lines ,bbit-tree-no-lines,bbit-tree-arrows​


​}​


​//用传进来的参数覆盖默认,没传则保留​


​$.extend(dfop, settings);​


第三步:生成默认数据的HTML(根据我们的分析节点的Dom结构,数据的数据结构,生成节点那是非常的简单),,添加到当前容器中。最后是注册事件这里有一个非常重要的地方,即懒加载(没有展开的节点HTML是不生成的),这就要求我们在树内部要维护一套数据(开销很小),对于性能的提升那是相当的明显。另外一个重要的地方,就是使用一次生成所有展开节点的HTML并通过innerHTML属性来生成Dom,而不是通过append操作,因为直接操作innerHTML比通过dom原生的方法要快上N倍(节点越多,N越大),切记切记!



​?​



1


2


3


4


5


6


7


8


9


10


11


12


13




​var​​ ​​treenodes = dfop.data; ​​​​//内部的数据,其实直接用 dfop.data也可以​


​var​​ ​​me = $(​​​​this​​​​);​


​var​​ ​​id = me.attr(​​​​"id"​​​​);​


​if​​ ​​(id == ​​​​null​​ ​​|| id == ​​​​""​​​​) {​


​id = ​​​​"bbtree"​​ ​​+ ​​​​new​​ ​​Date().getTime();​


​me.attr(​​​​"id"​​​​, id);​


​}​​​​//全局唯一的ID​


 


​var​​ ​​html = [];​


​buildtree(dfop.data, html);​​​​//生成展开节点的HTML,push到数组中​


​me.addClass(​​​​"bbit-tree"​​​​).html(html.join(​​​​""​​​​));​


​InitEvent(me);​​​​//初始化事件​


​html = ​​​​null​​​​;​


在节点生成过程中,同时可生产节点的Path(节点路径),方便检索



​?​



1


2


3


4


5


6


7


8


9


10


11


12


13


14


15


16




​if​​ ​​(nd.hasChildren) { ​​​​//存在子节点​


​if​​ ​​(nd.isexpand) {​​​​//同时节点已经展开则输出子节点​


​ht.push(​​​​"<ul  class='bbit-tree-node-ct'  style='z-index: 0; position: static; visibility: visible; top: auto; left: auto;'>"​​​​);​


​if​​ ​​(nd.ChildNodes) {​


​var​​ ​​l = nd.ChildNodes.length;​


​for​​ ​​(​​​​var​​ ​​k = 0; k < l; k++) {​​​​//递归调用并生产节点的路径​


​nd.ChildNodes[k].parent = nd;​


​buildnode(nd.ChildNodes[k], ht, deep + 1, path + ​​​​"."​​ ​​+ k, k == l - 1);​


​}​


​}​


​ht.push(​​​​"</ul>"​​​​);​


​}​


​else​​ ​​{ ​​​​//否则是待输出状态​


​ht.push(​​​​"<ul style='display:none;'></ul>"​​​​);​


​}​


​}​


注册事件,接受参数parent,即从某一父节点开始附加事件(因为做了个hover效果,所以事件是在每个节点上,如果取消该效果,事件可直接附加Tree上通过Event的srcElement来分发可略提升性能)



​?​



1


2


3


4


5


6


7


8


9


10


11


12


13


14


15


16


17


18


19


20




​function​​ ​​InitEvent(parent) {​


​var​​ ​​nodes = $(​​​​"li.bbit-tree-node>div"​​​​, parent);​


​nodes.each(​​​​function​​​​(e) {​


​$(​​​​this​​​​).hover(​​​​function​​​​() {​


​$(​​​​this​​​​).addClass(​​​​"bbit-tree-node-over"​​​​); ​​​​//鼠标浮动节点的样式变化​


​}, ​​​​function​​​​() {​


​$(​​​​this​​​​).removeClass(​​​​"bbit-tree-node-over"​​​​);​


​})​


​.click(nodeclick)​​​​//node的onclick事件,这个是重点哦​


​.find(​​​​"img.bbit-tree-ec-icon"​​​​).each(​​​​function​​​​(e) { ​​​​//arrow的hover事件,为了实现vista那个风格的​


​if​​ ​​(!$(​​​​this​​​​).hasClass(​​​​"bbit-tree-elbow"​​​​)) {​


​$(​​​​this​​​​).hover(​​​​function​​​​() {​


​$(​​​​this​​​​).parent().addClass(​​​​"bbit-tree-ec-over"​​​​);​


​}, ​​​​function​​​​() {​


​$(​​​​this​​​​).parent().removeClass(​​​​"bbit-tree-ec-over"​​​​);​


​});​


​}​


​});​


​});​


​}​


这里最主要的还是node的click事件,因为他要处理的事情很多,如树的展开收缩(如果子节点不存在,但是hasChildren为真,同时complete属性不为真则需要异步加载子节点,如子节点存在,但是没有Render那么就要Render),点击checkbox要出发级联的事件和oncheckbox事件,点击其他则触发配置条件的nodeonclick事件,这一切都通过前面event的源元素的class来区分点击的对象



​?​



1


2


3


4


5


6


7


8


9


10


11


12


13


14


15


16


17


18


19


20


21


22


23


24


25


26


27


28


29


30


31


32


33


34


35


36


37


38


39


40


41


42


43


44


45


46


47


48


49


50


51


52


53


54


55


56


57


58


59


60


61


62


63


64


65


66


67


68


69


70


71


72


73


74


75




​function​​ ​​nodeclick(e) {​


​var​​ ​​path = $(​​​​this​​​​).attr(​​​​"tpath"​​​​);​​​​//获取节点路径​


​var​​ ​​et = e.target || e.srcElement;​​​​//获取事件源​


​var​​ ​​item = getItem(path);​​​​//根据path获取节点的数据​


​//debugger;​


​if​​ ​​(et.tagName == ​​​​"IMG"​​​​) {​


​// +号需要展开,处理加减号​


​if​​ ​​($(et).hasClass(​​​​"bbit-tree-elbow-plus"​​​​) || $(et).hasClass(​​​​"bbit-tree-elbow-end-plus"​​​​)) {​


​var​​ ​​ul = $(​​​​this​​​​).next(); ​​​​//"bbit-tree-node-ct"​


​if​​ ​​(ul.hasClass(​​​​"bbit-tree-node-ct"​​​​)) {​


​ul.show();​


​}​


​else​​ ​​{​


​var​​ ​​deep = path.split(​​​​"."​​​​).length;​


​if​​ ​​(item.complete) {​


​item.ChildNodes != ​​​​null​​ ​​&& asnybuild(item.ChildNodes, deep, path, ul, item);​


​}​


​else​​ ​​{​


​$(​​​​this​​​​).addClass(​​​​"bbit-tree-node-loading"​​​​);​


​asnyloadc(ul, item, ​​​​function​​​​(data) {​


​item.complete = ​​​​true​​​​;​


​item.ChildNodes = data;​


​asnybuild(data, deep, path, ul, item);​


​});​


​}​


​}​


​if​​ ​​($(et).hasClass(​​​​"bbit-tree-elbow-plus"​​​​)) {​


​$(et).swapClass(​​​​"bbit-tree-elbow-plus"​​​​, ​​​​"bbit-tree-elbow-minus"​​​​);​


​}​


​else​​ ​​{​


​$(et).swapClass(​​​​"bbit-tree-elbow-end-plus"​​​​, ​​​​"bbit-tree-elbow-end-minus"​​​​);​


​}​


​$(​​​​this​​​​).swapClass(​​​​"bbit-tree-node-collapsed"​​​​, ​​​​"bbit-tree-node-expanded"​​​​);​


​}​


​else​​ ​​if​​ ​​($(et).hasClass(​​​​"bbit-tree-elbow-minus"​​​​) || $(et).hasClass(​​​​"bbit-tree-elbow-end-minus"​​​​)) {  ​​​​//- 号需要收缩                    ​


​$(​​​​this​​​​).next().hide();​


​if​​ ​​($(et).hasClass(​​​​"bbit-tree-elbow-minus"​​​​)) {​


​$(et).swapClass(​​​​"bbit-tree-elbow-minus"​​​​, ​​​​"bbit-tree-elbow-plus"​​​​);​


​}​


​else​​ ​​{​


​$(et).swapClass(​​​​"bbit-tree-elbow-end-minus"​​​​, ​​​​"bbit-tree-elbow-end-plus"​​​​);​


​}​


​$(​​​​this​​​​).swapClass(​​​​"bbit-tree-node-expanded"​​​​, ​​​​"bbit-tree-node-collapsed"​​​​);​


​}​


​else​​ ​​if​​ ​​($(et).hasClass(​​​​"bbit-tree-node-cb"​​​​)) ​​​​// 点击了Checkbox​


​{​


​var​​ ​​s = item.checkstate != 1 ? 1 : 0;​


​var​​ ​​r = ​​​​true​​​​;​


​if​​ ​​(dfop.oncheckboxclick) { ​​​​//触发配置的函数​


​r = dfop.oncheckboxclick.call(et, item, s);​


​}​


​if​​ ​​(r != ​​​​false​​​​) {​​​​//如果返回值不为false,即checkbxo变化有效​


​if​​ ​​(dfop.cascadecheck) {​​​​//允许触发级联​


​//遍历​


​cascade(check, item, s);​​​​//则向下关联​


​//上溯​


​bubble(check, item, s); ​​​​//向上关联​


​}​


​else​​ ​​{​


​check(item, s, 1);​​​​//否则只管自己​


​}​


​}​


​}​


​}​


​else​​ ​​{​​​​//点击到了其他地方​


​if​​ ​​(dfop.citem) { ​​​​//上一个当前节点​


​$(​​​​"#"​​ ​​+ id + ​​​​"_"​​ ​​+ dfop.citem.id).removeClass(​​​​"bbit-tree-selected"​​​​);​


​}​


​dfop.citem = item;​​​​//这次的当前节点​


​$(​​​​this​​​​).addClass(​​​​"bbit-tree-selected"​​​​);​


​if​​ ​​(dfop.onnodeclick) {​


​dfop.onnodeclick.call(​​​​this​​​​, item);​


​}​


​}​


​}​



展开节点,异步请求的部分代码应该不是很复杂就不细诉了,关键来讲一下级联

级联有两个问题要处理,第一个是遍历子节点,第二个是上溯到祖节点,因为我们的数据结构这两个操作都显得非常简单




​?​



1


2


3


4


5


6


7


8


9


10


11


12


13


14


15


16


17


18


19


20


21




​//遍历子节点​


​function​​ ​​cascade(fn, item, args) {​


​if​​ ​​(fn(item, args, 1) != ​​​​false​​​​) {​


​if​​ ​​(item.ChildNodes != ​​​​null​​ ​​&& item.ChildNodes.length > 0) {​


​var​​ ​​cs = item.ChildNodes;​


​for​​ ​​(​​​​var​​ ​​i = 0, len = cs.length; i < len; i++) {​


​cascade(fn, cs[i], args);​


​}​


​}​


​}​


​}​


​//冒泡的祖先​


​function​​ ​​bubble(fn, item, args) {​


​var​​ ​​p = item.parent;​


​while​​ ​​(p) {​


​if​​ ​​(fn(p, args, 0) === ​​​​false​​​​) {​


​break​​​​;​


​}​


​p = p.parent;​


​}​


​}​



找到节点的同时都会触发check这个回调函数,来判断当前节点的状态,详细请看下面代码中的注释部分应该是比较清晰,描写了这个过程




​?​



1


2


3


4


5


6


7


8


9


10


11


12


13


14


15


16


17


18


19


20


21


22


23


24


25


26


27


28


29


30




​function​​ ​​check(item, state, type) {​


​var​​ ​​pstate = item.checkstate; ​​​​//当前状态​


​if​​ ​​(type == 1) {​


​item.checkstate = state; ​​​​//如果是遍历子节点,父是什么子就是什么​


​}​


​else​​ ​​{​​​​// 上溯 ,这个就复杂一些了​


​var​​ ​​cs = item.ChildNodes; ​​​​//获取当前节点的所有子节点​


​var​​ ​​l = cs.length; ​


​var​​ ​​ch = ​​​​true​​​​; ​​​​//是否不是中间状态 半选​


​for​​ ​​(​​​​var​​ ​​i = 0; i < l; i++) {​


​if​​ ​​((state == 1 && cs[i].checkstate != 1) || state == 0 && cs[i].checkstate != 0) {​


​ch = ​​​​false​​​​;​


​break​​​​;​​​​//他的子节点只要有一个没选中,那么他就是半选​


​}​


​}​


​if​​ ​​(ch) {​


​item.checkstate = state;​​​​//不是半选,则子节点是什么他就是什么​


​}​


​else​​ ​​{​


​item.checkstate = 2; ​​​​//半选​


​}​


​}​


​//change show 如果节点已输出,而其前后状态不一样,则变化checkbxo的显示         ​


​if​​ ​​(item.render && pstate != item.checkstate) {​


​var​​ ​​et = $(​​​​"#"​​ ​​+ id + ​​​​"_"​​ ​​+ item.id + ​​​​"_cb"​​​​);​


​if​​ ​​(et.length == 1) {​


​et.attr(​​​​"src"​​​​, dfop.cbiconpath + dfop.icons[item.checkstate]);​


​}​


​}​


​}​



至此我们树的主体功能已经完全实现了。其他就是公开一些方法等,大家可详见代码,示例中公开了两个一个当前选中的所有节点,另外一个当前的节点。 

大家可以通过以下网址查看文中的示例,selected拼错了,大家海涵! windows azure部署还是麻烦懒得修改了3500+节点一次加载,大家可以点击根节点的全选来看看速度

 ​​http://jscs.cloudapp.net/ControlsSample/BigTreeSample​​ 

异步加载,按需加载的情况也是非常常用的,使用的是SQL Azure服务器在美国ing,所以可能异步有点慢,本地数据源那是瞬间的

​http://jscs.cloudapp.net/ControlsSample/TreeAsnySample​

FAQ:

1:如何设置每个节点不同的图标?

回答:

其实不用扩展,本身就支持,只是没有说明而已,我们来看一下这个代码吧?在BuildNode方法中有这么一句?      

if (nd.classes) { cs.push(nd.classes); }


在节点的数据结构中可以设置属性classes ,该属性将作为节点特殊的Css Class 添加到节点上。那么利用这点,就可以设置节点的图标了

打造jQuery的高性能TreeView_ico_27

然后就是编写一个Style 即可

打造jQuery的高性能TreeView_html_28

最后来看下效果吧?

打造jQuery的高性能TreeView_jquery_29


 

 

 出处:http://www.cnblogs.com/xuanye/archive/2009/10/26/xjplugin_jquery_tree.html

 demo下载地址: ​​demo​​ 

UPDATE:回答网友提出的设置节点的自定义图片的问题,同时欢迎大家提问,我尽量在第一时间回复,详见最后 2009-11-03

项目中经常会遇到树形数据的展现,包括导航,选择等功能,所以树控件在大多项目中都是必须的。那一个实用的树应该具备什么功能呢?

根据我的项目实践情况,主要是几个关键点:

1:支持静态的树,即一次性将全部数据加载到客户端。

2:异步树,即一次只加载一级或若干级节点,子节点可以异步加载数据。

3:Checkbox树(可能是静态树也可能是异步树),用于选择(如选择组织机构,选择数据字典项)等,最好是能够支持节点级联(这个是难点)

4:能够承载大数据量,并性能表现优异

5:能够在主流浏览器中运行良好

那我要打造的TreeView就是为了实现这个5个主要指标的。

先来看下效果图

打造jQuery的高性能TreeView_ico

上图是中国行政区域的数据树,总共得节点是3500+。

那么我们要开工了;

1:第一个确定的节点Dom结构(即用什么样的HTML来构建节点)

  • 比较土的是table套table的(样式上好控制,但是大数据量,和层次较深的树,这种结构肯定顶不住的)
  • 还有一种是比较新鲜的UL套LI的方式,这是现下很多书采取的方式如​​Jquery.treeview​​就是采用的这种格式,好处比较明显就是结构简洁明了,
    而且在不支持Js的浏览器上,同样也能呈现出树的形状(这种情况其实咱可以忽略),但是Jquery.treeview的节点在IE下,特别是IE6下无法被内部元素撑开,(IE7,8当达到一定深度时无法撑开),请奇怪的现象(我猜测是因为使用padding来做缩进,margin-left:负值来控制图标位置有关,但是修改起来难度也较大),在这种情况下书会变形(Jquery.treeview)就有这种问题,只能通过设置节点的width来解决。

打造jQuery的高性能TreeView_html_02

JQuery.treeview的节点结构

打造jQuery的高性能TreeView_html_03

Jquery.TreeView  IE6 下 展开第三级即出现错位

打造jQuery的高性能TreeView_html_04​ IE8下展开到第5级

  • 还有一些是div套table的方式,CSDN的导航树就是这种,是种折中的方法(节点也不算太复杂,而且CSS也比较好写),如下图所示
    打造jQuery的高性能TreeView_html_05

 

 

 

而我采用的也是第二种方式,但是缩进采用了填空的方式,即缩进的位置用空白的图片填充来避免Jquery.treeview的问题

打造jQuery的高性能TreeView_数据_06

我的树节点结构

确定了节点的HTML我们就可以来写CSS了。有了效果图,有个节点结构接着就编写CSS了

下面是CSS的完整代码




​+ View Code​​​​?​



1


2


3


4


5


6


7


8


9


10


11


12


13


14


15


16


17


18


19


20


21


22


23


24


25


26


27


28


29


30


31


32


33


34


35


36


37


38


39


40


41


42


43


44


45


46


47


48


49


50


51


52


53


54


55


56


57


58


59


60


61


62


63


64


65


66


67


68


69


70


71


72


73


74


75


76


77


78


79


80


81


82


83


84


85


86


87


88


89


90


91


92


93


94


95


96


97


98


99


100


101


102


103


104


105


106


107


108


109


110


111


112


113


114


115


116


117


118


119


120


121


122


123


124


125


126


127


128


129


130


131


132


133


134


135


136


137


138


139


140


141


142


143


144


145


146


147


148


149


150


151


152


153


154


155


156


157


158


159


160


161


162


163


164


165


166


167


168


169


170


171


172


173


174


175


176


177


178


179


180


181


182


183


184


185


186


187


188


189


190


191


192


193


194


195


196


197


198


199


200


201


202


203


204


205


206


207


208


209


210


211


212


213


214


215


216


217


218


219


220


221


222


223


224


225


226


227


228


229


230


231


232


233


234


235


236


237


238


239


240


241


242


243


244


245


246


247


248


249


250


251


252


253


254


255


256


257


258


259


260


261


262


263


264


265


266


267


268


269




​.ie .bbit-tree .bbit-tree-bwrap{​


 


​}​


​.bbit-tree ul,.bbit-tree li​


​{​


​list-style-type​​​​:​​​​none​​​​;​


​margin​​​​:​​​​0px​​​​;​


​padding​​​​:​​​​0px​​​​;​


​}​


​.bbit-tree-body​


​{​


​font-size​​​​:​​​​12px​​​​;​


​}​


​.bbit-tree-​​​​icon​​​​, .bbit-tree-ec-​​​​icon​​​​, .bbit-tree-node-cb,.bbit-tree-elbow-line, .bbit-tree-elbow, .bbit-tree-elbow-end, .bbit-tree-elbow-plus, .bbit-tree-elbow-minus, .bbit-tree-elbow-end-plus, .bbit-tree-elbow-end-minus{​


​border​​​​: ​​​​0​​ ​​none​​​​;​


​height​​​​: ​​​​18px​​​​;​


​margin​​​​: ​​​​0​​​​;​


​padding​​​​: ​​​​0​​​​;​


​vertical-align​​​​: ​​​​top​​​​;​


​width​​​​: ​​​​16px​​​​;​


​background-repeat​​​​: ​​​​no-repeat​​​​;​


​}​


​.bbit-tree-node-cb​


​{​


​height​​​​:​​​​16px​​​​;​


​}​


​.bbit-tree-node-collapsed .bbit-tree-node-​​​​icon​​​​, .bbit-tree-node-​​​​expanded​​ ​​.bbit-tree-node-​​​​icon​​​​, .bbit-tree-node-leaf .bbit-tree-node-​​​​icon​​​​{​


​border​​​​: ​​​​0​​ ​​none​​​​;​


​height​​​​: ​​​​18px​​​​;​


​margin​​​​: ​​​​0​​​​;​


​padding​​​​: ​​​​0​​​​;​


​vertical-align​​​​: ​​​​top​​​​;​


​width​​​​: ​​​​16px​​​​;​


​background-position​​​​:​​​​center​​​​;​


​background-repeat​​​​: ​​​​no-repeat​​​​;​


​}​


 


​.ie .bbit-tree-node-indent img, .ie .bbit-tree-node-​​​​icon​​​​, .ie .bbit-tree-ec-​​​​icon​​ ​​{​


​vertical-align​​​​:​​​​middle​​ ​​!important​​​​;​


​}​


 


​.bbit-tree-noicon .bbit-tree-node-​​​​icon​​​​{​


​width​​​​:​​​​0​​​​; ​​​​height​​​​:​​​​0​​​​;​


​}​


 


​/* No line styles 没有线的样式 */​


​.bbit-tree-no-lines .bbit-tree-elbow{​


​background​​​​:​​​​transparent​​​​;​


​}​


 


​.bbit-tree-no-lines .bbit-tree-elbow-end{​


​background​​​​:​​​​transparent​​​​;​


​}​


 


​.bbit-tree-no-lines .bbit-tree-elbow-line{​


​background​​​​:​​​​transparent​​​​;​


​}​


 


​/* Arrows Vista系统树的样式只有箭头*/​


​.bbit-tree-arrows .bbit-tree-elbow{​


​background​​​​:​​​​transparent​​​​;​


​}​


 


​.bbit-tree-arrows .bbit-tree-elbow-plus{​


​background​​​​:​​​​transparent​​ ​​no-repeat​​ ​​0​​ ​​0​​​​;​


​}​


 


​.bbit-tree-arrows .bbit-tree-elbow-minus{​


​background​​​​:​​​​transparent​​ ​​no-repeat​​ ​​-16px​​ ​​0​​​​;​


​}​


 


​.bbit-tree-arrows .bbit-tree-elbow-end{​


​background​​​​:​​​​transparent​​​​;​


​}​


 


​.bbit-tree-arrows .bbit-tree-elbow-end-plus{​


​background​​​​:​​​​transparent​​ ​​no-repeat​​ ​​0​​ ​​0​​​​;​


​}​


 


​.bbit-tree-arrows .bbit-tree-elbow-end-minus{​


​background​​​​:​​​​transparent​​ ​​no-repeat​​ ​​-16px​​ ​​0​​​​;​


​}​


 


​.bbit-tree-arrows .bbit-tree-elbow-line{​


​background​​​​:​​​​transparent​​​​;​


​}​


 


​.bbit-tree-arrows .bbit-tree-ec-over .bbit-tree-elbow-plus{​


​background-position​​​​:​​​​-32px​​ ​​0​​​​;​


​}​


 


​.bbit-tree-arrows .bbit-tree-ec-over .bbit-tree-elbow-minus{​


​background-position​​​​:​​​​-48px​​ ​​0​​​​;​


​}​


 


​.bbit-tree-arrows .bbit-tree-ec-over .bbit-tree-elbow-end-plus{​


​background-position​​​​:​​​​-32px​​ ​​0​​​​;​


​}​


 


​.bbit-tree-arrows .bbit-tree-ec-over .bbit-tree-elbow-end-minus{​


​background-position​​​​:​​​​-48px​​ ​​0​​​​;​


​}​


 


​.bbit-tree-elbow-plus, .bbit-tree-elbow-minus, .bbit-tree-elbow-end-plus, .bbit-tree-elbow-end-minus{​


​cursor​​​​:​​​​pointer​​​​;​


​}​


 


​.ie ul.bbit-tree-node-ct{​


​font-size​​​​:​​​​0​​​​;​


​line-height​​​​:​​​​0​​​​;​


​zoom:​​​​1​​​​;​


​}​


 


​.bbit-tree-node{​


​white-space​​​​: ​​​​nowrap​​​​;​


​}​


 


​.bbit-tree-node-el {​


​line-height​​​​:​​​​18px​​​​;​


​cursor​​​​:​​​​default​​​​;​


​/* cursor:pointer;*/​


​}​


 


​.bbit-tree-node a{​


​text-decoration​​​​:​​​​none​​​​;​


​-khtml-user-select:​​​​none​​​​;​


​-moz-user-select:​​​​none​​​​;​


​-webkit-user-select:ignore;​


​-kthml-user-focus:​​​​normal​​​​;​


​-moz-user-focus:​​​​normal​​​​;​


​-moz-​​​​outline​​​​: ​​​​0​​ ​​none​​​​;​


​outline​​​​:​​​​0​​ ​​none​​​​;​


​}​


 


​.bbit-tree-node a span{​


​text-decoration​​​​:​​​​none​​​​;​


​padding​​​​:​​​​1px​​ ​​3px​​ ​​1px​​ ​​2px​​​​;​


​}​


 


​.bbit-tree-node .bbit-tree-node-disabled .bbit-tree-node-​​​​icon​​​​{​


​-moz-opacity: ​​​​0.5​​​​;​


​opacity:.​​​​5​​​​;​


​filter: alpha(opacity=​​​​50​​​​);​


​}​


 


​.bbit-tree-node .bbit-tree-node-inline-​​​​icon​​​​{​


​background​​​​:​​​​transparent​​​​;​


​}​


 


​.bbit-tree-node a:hover{​


​text-decoration​​​​:​​​​none​​​​;​


​}​


 


 


 


​/* Fix for ie rootVisible:false issue,修正一个IEdebug */​


​.bbit-tree-root {​


​zoom:​​​​1​​​​;​


​}​


 


 


​/***********这里是图标了,可以在这里替换哦*****************/​


​.bbit-tree-node-​​​​expanded​​ ​​.bbit-tree-node-​​​​icon​​​​{​


​background-image​​​​:​​​​url​​​​(images/tree/folder-open.gif);​


​}​


 


​.bbit-tree-node-leaf .bbit-tree-node-​​​​icon​​​​{​


​background-image​​​​:​​​​url​​​​(images/tree/leaf.gif);​


​}​


 


​.bbit-tree-node-collapsed .bbit-tree-node-​​​​icon​​​​{​


​background-image​​​​:​​​​url​​​​(images/tree/folder.gif);​


​}​


 


​.bbit-tree-node-loading .bbit-tree-node-​​​​icon​​​​{​


​background-image​​​​:​​​​url​​​​(images/tree/loading.gif) ​​​​!important​​​​;​


​}​


 


​.bbit-tree-node .bbit-tree-node-inline-​​​​icon​​ ​​{​


​background-image​​​​: ​​​​none​​​​;​


​}​


 


​.bbit-tree-node-loading a span{​


​font-style​​​​: ​​​​italic​​​​;​


​color​​​​:​​​​#444444​​​​;​


​}​


 


​.bbit-tree-lines .bbit-tree-elbow{​


​background-image​​​​:​​​​url​​​​(images/tree/elbow.gif);​


​}​


 


​.bbit-tree-lines .bbit-tree-elbow-plus{​


​background-image​​​​:​​​​url​​​​(images/tree/elbow-plus.gif);​


​}​


 


​.bbit-tree-lines .bbit-tree-elbow-minus{​


​background-image​​​​:​​​​url​​​​(images/tree/elbow-minus.gif);​


​}​


 


​.bbit-tree-lines .bbit-tree-elbow-end{​


​background-image​​​​:​​​​url​​​​(images/tree/elbow-end.gif);​


​}​


 


​.bbit-tree-lines .bbit-tree-elbow-end-plus{​


​background-image​​​​:​​​​url​​​​(images/tree/elbow-end-plus.gif);​


​}​


 


​.bbit-tree-lines .bbit-tree-elbow-end-minus{​


​background-image​​​​:​​​​url​​​​(images/tree/elbow-end-minus.gif);​


​}​


 


​.bbit-tree-lines .bbit-tree-elbow-line{​


​background-image​​​​:​​​​url​​​​(images/tree/elbow-line.gif);​


​}​


 


​.bbit-tree-no-lines .bbit-tree-elbow-plus{​


​background-image​​​​:​​​​url​​​​(images/tree/elbow-plus-nl.gif);​


​}​


 


​.bbit-tree-no-lines .bbit-tree-elbow-minus{​


​background-image​​​​:​​​​url​​​​(images/tree/elbow-minus-nl.gif);​


​}​


 


​.bbit-tree-no-lines .bbit-tree-elbow-end-plus{​


​background-image​​​​:​​​​url​​​​(images/tree/elbow-end-plus-nl.gif);​


​}​


 


​.bbit-tree-no-lines .bbit-tree-elbow-end-minus{​


​background-image​​​​:​​​​url​​​​(images/tree/elbow-end-minus-nl.gif);​


​}​


 


​.bbit-tree-arrows .bbit-tree-elbow-plus{​


​background-image​​​​:​​​​url​​​​(images/tree/arrows.gif);​


​}​


 


​.bbit-tree-arrows .bbit-tree-elbow-minus{​


​background-image​​​​:​​​​url​​​​(images/tree/arrows.gif);​


​}​


 


​.bbit-tree-arrows .bbit-tree-elbow-end-plus{​


​background-image​​​​:​​​​url​​​​(images/tree/arrows.gif);​


​}​


 


​.bbit-tree-arrows .bbit-tree-elbow-end-minus{​


​background-image​​​​:​​​​url​​​​(images/tree/arrows.gif);​


​}​


 


​/*TreeNode 选中的Disabled的一些颜色,字体样式*/​


​.bbit-tree-node{​


​color​​​​:​​​​#000​​​​;​


​font​​​​: ​​​​normal​​ ​​11px​​ ​​arial​​​​, ​​​​tahoma​​​​, ​​​​helvetica​​​​, ​​​​sans-serif​​​​;​


​}​


 


​.bbit-tree-node a{​


​color​​​​:​​​​#000​​​​;​


​}​


​.bbit-tree-node a span{​


​color​​​​:​​​​#000​​​​;​


​}​


​.bbit-tree-node .bbit-tree-node-disabled a span{​


​color​​​​:​​​​gray​​ ​​!important​​​​;​


​}​


​.bbit-tree-node .bbit-tree-node-over {​


​background-color​​​​: ​​​​#eee​​​​;​


​}​


 


​.bbit-tree-node .bbit-tree-selected {​


​background-color​​​​: ​​​​#d9e8fb​​​​;​


​}​



上面了树的基本样式外,定义了一个有+号带line的样式和+号不带line的样式

打造jQuery的高性能TreeView_数据_07​ 这就是那个+号带line的样式

css中所用到的所有图片

打造jQuery的高性能TreeView_子节点_08​ ​打造jQuery的高性能TreeView_子节点_09​ ​打造jQuery的高性能TreeView_html_10​ ​打造jQuery的高性能TreeView_ico_11​ ​打造jQuery的高性能TreeView_数据_12​ ​打造jQuery的高性能TreeView_数据_13​ ​打造jQuery的高性能TreeView_html_14​ ​打造jQuery的高性能TreeView_子节点_15​ ​打造jQuery的高性能TreeView_jquery_16​ ​打造jQuery的高性能TreeView_ico_17​ ​打造jQuery的高性能TreeView_jquery_18​ ​打造jQuery的高性能TreeView_数据_19​ ​打造jQuery的高性能TreeView_html_20​ ​打造jQuery的高性能TreeView_子节点_21​ ​打造jQuery的高性能TreeView_子节点_22​ ​打造jQuery的高性能TreeView_数据_23

打造jQuery的高性能TreeView_html_24​ ​打造jQuery的高性能TreeView_jquery_25​ ​打造jQuery的高性能TreeView_jquery_26

2:确定数据结构




​?​



1


2


3


4


5


6


7


8


9


10


11


12


13


14


15


16


17


18


19


20


21


22


23




​var​​ ​​menudata = [{​


​id: ​​​​"0.1"​​​​,​​​​//唯一的ID即可​


​text: ​​​​"Beyondbit UI Demo"​​​​,​


​hasChildren: ​​​​true​​​​,​


​isexpand: ​​​​true​​​​,​


​complete: ​​​​true​​​​,​


​ChildNodes: [{​


​id: ​​​​"0.1.1"​​​​,​


​text: ​​​​"日期选择"​​​​,​


​hasChildren: ​​​​true​​​​,​


​isexpand: ​​​​false​​​​,​


​complete: ​​​​true​​​​,​


​ChildNodes: [{​


​id: ​​​​"0.1.1.1"​​​​,​


​text: ​​​​"控件演示"​​​​,​


​value: ​​​​"Testpages/datepickerDemo.htm"​​​​,​


​hasChildren: ​​​​false​​​​,​


​isexpand: ​​​​false​​​​,​


​complete: ​​​​true​​​​,​


​ChildNodes: ​​​​null​


​},​


​...​


​]​



 

 

这样的结构有个好处就数据本身是带层次的,非常利于遍历,在后面的级联关联中会看到

3: 面子做好了那就开始做里子了,编写脚本(Javascript)

我是JQuery得拥护者,所以自然js的框架自然是采用Jquery了

先上个完整代码,再逐一分析




​+ View Code​​​​?​



1


2


3


4


5


6


7


8


9


10


11


12


13


14


15


16


17


18


19


20


21


22


23


24


25


26


27


28


29


30


31


32


33


34


35


36


37


38


39


40


41


42


43


44


45


46


47


48


49


50


51


52


53


54


55


56


57


58


59


60


61


62


63


64


65


66


67


68


69


70


71


72


73


74


75


76


77


78


79


80


81


82


83


84


85


86


87


88


89


90


91


92


93


94


95


96


97


98


99


100


101


102


103


104


105


106


107


108


109


110


111


112


113


114


115


116


117


118


119


120


121


122


123


124


125


126


127


128


129


130


131


132


133


134


135


136


137


138


139


140


141


142


143


144


145


146


147


148


149


150


151


152


153


154


155


156


157


158


159


160


161


162


163


164


165


166


167


168


169


170


171


172


173


174


175


176


177


178


179


180


181


182


183


184


185


186


187


188


189


190


191


192


193


194


195


196


197


198


199


200


201


202


203


204


205


206


207


208


209


210


211


212


213


214


215


216


217


218


219


220


221


222


223


224


225


226


227


228


229


230


231


232


233


234


235


236


237


238


239


240


241


242


243


244


245


246


247


248


249


250


251


252


253


254


255


256


257


258


259


260


261


262


263


264


265


266


267


268


269


270


271


272


273


274


275


276


277


278


279


280


281


282


283


284


285


286


287


288


289


290


291


292


293


294


295


296


297


298


299


300


301


302


303


304


305


306


307


308


309


310


311


312


313


314


315


316


317


318


319


320


321


322


323


324


325


326


327


328


329


330


331


332


333


334


335


336


337


338


339


340


341


342


343


344


345


346


347


348


349


350


351


352


353


354


355


356


357


358


359


360


361


362


363


364


365


366


367


368


369


370


371


372


373


374


375


376


377


378


379


380


381


382


383




​/****************************************​


​author:xuanye.wan@gmail.com​


​page:http://xuanye.cnblogs.com/​


​***************************************/​


​(​​​​function​​​​($) {​


​$.fn.swapClass = ​​​​function​​​​(c1, c2) {​


​return​​ ​​this​​​​.removeClass(c1).addClass(c2);​


​}​


​$.fn.switchClass = ​​​​function​​​​(c1, c2) {​


​if​​ ​​(​​​​this​​​​.hasClass(c1)) {​


​return​​ ​​this​​​​.swapClass(c1, c2);​


​}​


​else​​ ​​{​


​return​​ ​​this​​​​.swapClass(c2, c1);​


​}​


​}​


​$.fn.treeview = ​​​​function​​​​(settings) {​


​var​​ ​​dfop =​


​{​


​method: ​​​​"POST"​​​​,​


​datatype: ​​​​"json"​​​​,​


​url: ​​​​false​​​​,​


​cbiconpath: ​​​​"/images/icons/"​​​​,​


​icons: [​​​​"checkbox_0.gif"​​​​, ​​​​"checkbox_1.gif"​​​​, ​​​​"checkbox_2.gif"​​​​],​


​showcheck: ​​​​false​​​​, ​​​​//是否显示选择            ​


​oncheckboxclick: ​​​​false​​​​, ​​​​//当checkstate状态变化时所触发的事件,但是不会触发因级联选择而引起的变化​


​onnodeclick: ​​​​false​​​​,​


​cascadecheck: ​​​​true​​​​,​


​data: ​​​​null​​​​,​


​clicktoggle: ​​​​true​​​​, ​​​​//点击节点展开和收缩子节点​


​theme: ​​​​"bbit-tree-arrows"​​ ​​//bbit-tree-lines ,bbit-tree-no-lines,bbit-tree-arrows​


​}​


 


​$.extend(dfop, settings);​


​var​​ ​​treenodes = dfop.data;​


​var​​ ​​me = $(​​​​this​​​​);​


​var​​ ​​id = me.attr(​​​​"id"​​​​);​


​if​​ ​​(id == ​​​​null​​ ​​|| id == ​​​​""​​​​) {​


​id = ​​​​"bbtree"​​ ​​+ ​​​​new​​ ​​Date().getTime();​


​me.attr(​​​​"id"​​​​, id);​


​}​


 


​var​​ ​​html = [];​


​buildtree(dfop.data, html);​


​me.addClass(​​​​"bbit-tree"​​​​).html(html.join(​​​​""​​​​));​


​InitEvent(me);​


​html = ​​​​null​​​​;​


​//预加载图片​


​if​​ ​​(dfop.showcheck) {​


​for​​ ​​(​​​​var​​ ​​i = 0; i < 3; i++) {​


​var​​ ​​im = ​​​​new​​ ​​Image(16,16);​


​im.src = dfop.cbiconpath + dfop.icons[i];​


​}​


​}​


​//region ​


​function​​ ​​buildtree(data, ht) {​


​ht.push(​​​​"<div class='bbit-tree-bwrap'>"​​​​); ​​​​// Wrap ;​


​ht.push(​​​​"<div class='bbit-tree-body'>"​​​​); ​​​​// body ;​


​ht.push(​​​​"<ul class='bbit-tree-root "​​​​, dfop.theme, ​​​​"'>"​​​​); ​​​​//root​


​var​​ ​​l = data.length;​


​for​​ ​​(​​​​var​​ ​​i = 0; i < l; i++) {​


​buildnode(data[i], ht, 0, i, i == l - 1);​


​}​


​ht.push(​​​​"</ul>"​​​​); ​​​​// root and;​


​ht.push(​​​​"</div>"​​​​); ​​​​// body end;​


​ht.push(​​​​"</div>"​​​​); ​​​​// Wrap end;​


​}​


​//endregion​


​function​​ ​​buildnode(nd, ht, deep, path, isend) {​


​ht.push(​​​​"<li class='bbit-tree-node'>"​​​​);​


​ht.push(​​​​"<div id='"​​​​, id, ​​​​"_"​​​​, nd.id, ​​​​"' tpath='"​​​​, path, ​​​​"' unselectable='on'"​​​​);​


​var​​ ​​cs = [];​


​cs.push(​​​​"bbit-tree-node-el"​​​​);​


​if​​ ​​(nd.hasChildren) {​


​cs.push(nd.isexpand ? ​​​​"bbit-tree-node-expanded"​​ ​​: ​​​​"bbit-tree-node-collapsed"​​​​);​


​}​


​else​​ ​​{​


​cs.push(​​​​"bbit-tree-node-leaf"​​​​);​


​}​


​if​​ ​​(nd.classes) { cs.push(nd.classes); }​


 


​ht.push(​​​​" class='"​​​​, cs.join(​​​​" "​​​​), ​​​​"'>"​​​​);​


​//span indent​


​ht.push(​​​​"<span class='bbit-tree-node-indent'>"​​​​);​


​if​​ ​​(deep == 1) {​


​ht.push(​​​​"<img class='bbit-tree-icon' src='../Themes/Shared/images/s.gif'/>"​​​​);​


​}​


​else​​ ​​if​​ ​​(deep > 1) {​


​ht.push(​​​​"<img class='bbit-tree-icon' src='../Themes/Shared/images/s.gif'/>"​​​​);​


​for​​ ​​(​​​​var​​ ​​j = 1; j < deep; j++) {​


​ht.push(​​​​"<img class='bbit-tree-elbow-line' src='../Themes/Shared/images/s.gif'/>"​​​​);​


​}​


​}​


​ht.push(​​​​"</span>"​​​​);​


​//img​


​cs.length = 0;​


​if​​ ​​(nd.hasChildren) {​


​if​​ ​​(nd.isexpand) {​


​cs.push(isend ? ​​​​"bbit-tree-elbow-end-minus"​​ ​​: ​​​​"bbit-tree-elbow-minus"​​​​);​


​}​


​else​​ ​​{​


​cs.push(isend ? ​​​​"bbit-tree-elbow-end-plus"​​ ​​: ​​​​"bbit-tree-elbow-plus"​​​​);​


​}​


​}​


​else​​ ​​{​


​cs.push(isend ? ​​​​"bbit-tree-elbow-end"​​ ​​: ​​​​"bbit-tree-elbow"​​​​);​


​}​


​ht.push(​​​​"<img class='bbit-tree-ec-icon "​​​​, cs.join(​​​​" "​​​​), ​​​​"' src='../Themes/Shared/images/s.gif'/>"​​​​);​


​ht.push(​​​​"<img class='bbit-tree-node-icon' src='../Themes/Shared/images/s.gif'/>"​​​​);​


​//checkbox​


​if​​ ​​(dfop.showcheck && nd.showcheck) {​


​if​​ ​​(nd.checkstate == ​​​​null​​ ​​|| nd.checkstate == undefined) {​


​nd.checkstate = 0;​


​}​


​ht.push(​​​​"<img  id='"​​​​, id, ​​​​"_"​​​​, nd.id, ​​​​"_cb' class='bbit-tree-node-cb' src='"​​​​, dfop.cbiconpath, dfop.icons[nd.checkstate], ​​​​"'/>"​​​​);​


​}​


​//a​


​ht.push(​​​​"<a hideFocus class='bbit-tree-node-anchor' tabIndex=1 href='javascript:void(0);'>"​​​​);​


​ht.push(​​​​"<span unselectable='on'>"​​​​, nd.text, ​​​​"</span>"​​​​);​


​ht.push(​​​​"</a>"​​​​);​


​ht.push(​​​​"</div>"​​​​);​


​//Child​


​if​​ ​​(nd.hasChildren) {​


​if​​ ​​(nd.isexpand) {​


​ht.push(​​​​"<ul  class='bbit-tree-node-ct'  style='z-index: 0; position: static; visibility: visible; top: auto; left: auto;'>"​​​​);​


​if​​ ​​(nd.ChildNodes) {​


​var​​ ​​l = nd.ChildNodes.length;​


​for​​ ​​(​​​​var​​ ​​k = 0; k < l; k++) {​


​nd.ChildNodes[k].parent = nd;​


​buildnode(nd.ChildNodes[k], ht, deep + 1, path + ​​​​"."​​ ​​+ k, k == l - 1);​


​}​


​}​


​ht.push(​​​​"</ul>"​​​​);​


​}​


​else​​ ​​{​


​ht.push(​​​​"<ul style='display:none;'></ul>"​​​​);​


​}​


​}​


​ht.push(​​​​"</li>"​​​​);​


​nd.render = ​​​​true​​​​;​


​}​


​function​​ ​​getItem(path) {​


​var​​ ​​ap = path.split(​​​​"."​​​​);​


​var​​ ​​t = treenodes;​


​for​​ ​​(​​​​var​​ ​​i = 0; i < ap.length; i++) {​


​if​​ ​​(i == 0) {​


​t = t[ap[i]];​


​}​


​else​​ ​​{​


​t = t.ChildNodes[ap[i]];​


​}​


​}​


​return​​ ​​t;​


​}​


​function​​ ​​check(item, state, type) {​


​var​​ ​​pstate = item.checkstate;​


​if​​ ​​(type == 1) {​


​item.checkstate = state;​


​}​


​else​​ ​​{​​​​// 上溯​


​var​​ ​​cs = item.ChildNodes;​


​var​​ ​​l = cs.length;​


​var​​ ​​ch = ​​​​true​​​​;​


​for​​ ​​(​​​​var​​ ​​i = 0; i < l; i++) {​


​if​​ ​​((state == 1 && cs[i].checkstate != 1) || state == 0 && cs[i].checkstate != 0) {​


​ch = ​​​​false​​​​;​


​break​​​​;​


​}​


​}​


​if​​ ​​(ch) {​


​item.checkstate = state;​


​}​


​else​​ ​​{​


​item.checkstate = 2;​


​}​


​}​


​//change show           ​


​if​​ ​​(item.render && pstate != item.checkstate) {​


​var​​ ​​et = $(​​​​"#"​​ ​​+ id + ​​​​"_"​​ ​​+ item.id + ​​​​"_cb"​​​​);​


​if​​ ​​(et.length == 1) {​


​et.attr(​​​​"src"​​​​, dfop.cbiconpath + dfop.icons[item.checkstate]);​


​}​


​}​


​}​


​//遍历子节点​


​function​​ ​​cascade(fn, item, args) {​


​if​​ ​​(fn(item, args, 1) != ​​​​false​​​​) {​


​if​​ ​​(item.ChildNodes != ​​​​null​​ ​​&& item.ChildNodes.length > 0) {​


​var​​ ​​cs = item.ChildNodes;​


​for​​ ​​(​​​​var​​ ​​i = 0, len = cs.length; i < len; i++) {​


​cascade(fn, cs[i], args);​


​}​


​}​


​}​


​}​


​//冒泡的祖先​


​function​​ ​​bubble(fn, item, args) {​


​var​​ ​​p = item.parent;​


​while​​ ​​(p) {​


​if​​ ​​(fn(p, args, 0) === ​​​​false​​​​) {​


​break​​​​;​


​}​


​p = p.parent;​


​}​


​}​


​function​​ ​​nodeclick(e) {​


​var​​ ​​path = $(​​​​this​​​​).attr(​​​​"tpath"​​​​);​


​var​​ ​​et = e.target || e.srcElement;​


​var​​ ​​item = getItem(path);​


 


​//debugger;​


​if​​ ​​(et.tagName == ​​​​"IMG"​​​​) {​


​// +号需要展开​


​if​​ ​​($(et).hasClass(​​​​"bbit-tree-elbow-plus"​​​​) || $(et).hasClass(​​​​"bbit-tree-elbow-end-plus"​​​​)) {​


​var​​ ​​ul = $(​​​​this​​​​).next(); ​​​​//"bbit-tree-node-ct"​


​if​​ ​​(ul.hasClass(​​​​"bbit-tree-node-ct"​​​​)) {​


​ul.show();​


​}​


​else​​ ​​{​


​var​​ ​​deep = path.split(​​​​"."​​​​).length;​


​if​​ ​​(item.complete) {​


​item.ChildNodes != ​​​​null​​ ​​&& asnybuild(item.ChildNodes, deep, path, ul, item);​


​}​


​else​​ ​​{​


​$(​​​​this​​​​).addClass(​​​​"bbit-tree-node-loading"​​​​);​


​asnyloadc(ul, item, ​​​​function​​​​(data) {​


​item.complete = ​​​​true​​​​;​


​item.ChildNodes = data;​


​asnybuild(data, deep, path, ul, item);​


​});​


​}​


​}​


​if​​ ​​($(et).hasClass(​​​​"bbit-tree-elbow-plus"​​​​)) {​


​$(et).swapClass(​​​​"bbit-tree-elbow-plus"​​​​, ​​​​"bbit-tree-elbow-minus"​​​​);​


​}​


​else​​ ​​{​


​$(et).swapClass(​​​​"bbit-tree-elbow-end-plus"​​​​, ​​​​"bbit-tree-elbow-end-minus"​​​​);​


​}​


​$(​​​​this​​​​).swapClass(​​​​"bbit-tree-node-collapsed"​​​​, ​​​​"bbit-tree-node-expanded"​​​​);​


​}​


​else​​ ​​if​​ ​​($(et).hasClass(​​​​"bbit-tree-elbow-minus"​​​​) || $(et).hasClass(​​​​"bbit-tree-elbow-end-minus"​​​​)) {  ​​​​//- 号需要收缩                    ​


​$(​​​​this​​​​).next().hide();​


​if​​ ​​($(et).hasClass(​​​​"bbit-tree-elbow-minus"​​​​)) {​


​$(et).swapClass(​​​​"bbit-tree-elbow-minus"​​​​, ​​​​"bbit-tree-elbow-plus"​​​​);​


​}​


​else​​ ​​{​


​$(et).swapClass(​​​​"bbit-tree-elbow-end-minus"​​​​, ​​​​"bbit-tree-elbow-end-plus"​​​​);​


​}​


​$(​​​​this​​​​).swapClass(​​​​"bbit-tree-node-expanded"​​​​, ​​​​"bbit-tree-node-collapsed"​​​​);​


​}​


​else​​ ​​if​​ ​​($(et).hasClass(​​​​"bbit-tree-node-cb"​​​​)) ​​​​// 点击了Checkbox​


​{​


​var​​ ​​s = item.checkstate != 1 ? 1 : 0;​


​var​​ ​​r = ​​​​true​​​​;​


​if​​ ​​(dfop.oncheckboxclick) {​


​r = dfop.oncheckboxclick.call(et, item, s);​


​}​


​if​​ ​​(r != ​​​​false​​​​) {​


​if​​ ​​(dfop.cascadecheck) {​


​//遍历​


​cascade(check, item, s);​


​//上溯​


​bubble(check, item, s);​


​}​


​else​​ ​​{​


​check(item, s, 1);​


​}​


​}​


​}​


​}​


​else​​ ​​{​


​if​​ ​​(dfop.citem) {​


​$(​​​​"#"​​ ​​+ id + ​​​​"_"​​ ​​+ dfop.citem.id).removeClass(​​​​"bbit-tree-selected"​​​​);​


​}​


​dfop.citem = item;​


​$(​​​​this​​​​).addClass(​​​​"bbit-tree-selected"​​​​);​


​if​​ ​​(dfop.onnodeclick) {​


​dfop.onnodeclick.call(​​​​this​​​​, item);​


​}​


​}​


​}​


​function​​ ​​asnybuild(nodes, deep, path, ul, pnode) {​


​var​​ ​​l = nodes.length;​


​if​​ ​​(l > 0) {​


​var​​ ​​ht = [];​


​for​​ ​​(​​​​var​​ ​​i = 0; i < l; i++) {​


​nodes[i].parent = pnode;​


​buildnode(nodes[i], ht, deep, path + ​​​​"."​​ ​​+ i, i == l - 1);​


​}​


​ul.html(ht.join(​​​​""​​​​));​


​ht = ​​​​null​​​​;​


​InitEvent(ul);​


​}​


​ul.addClass(​​​​"bbit-tree-node-ct"​​​​).css({ ​​​​"z-index"​​​​: 0, position: ​​​​"static"​​​​, visibility: ​​​​"visible"​​​​, top: ​​​​"auto"​​​​, left: ​​​​"auto"​​​​, display: ​​​​""​​ ​​});​


​ul.prev().removeClass(​​​​"bbit-tree-node-loading"​​​​);​


​}​


​function​​ ​​asnyloadc(pul, pnode, callback) {​


​if​​ ​​(dfop.url) {​


​var​​ ​​param = builparam(pnode);​


​$.ajax({​


​type: dfop.method,​


​url: dfop.url,​


​data: param,​


​dataType: dfop.datatype,​


​success: callback,​


​error: ​​​​function​​​​(e) { alert(​​​​"error occur!"​​​​); }​


​});​


​}​


​}​


​function​​ ​​builparam(node) {​


​var​​ ​​p = [{ name: ​​​​"id"​​​​, value: encodeURIComponent(node.id) }​


​, { name: ​​​​"text"​​​​, value: encodeURIComponent(node.text) }​


​, { name: ​​​​"value"​​​​, value: encodeURIComponent(node.value) }​


​, { name: ​​​​"checkstate"​​​​, value: node.checkstate}];​


​return​​ ​​p;​


​}​


​function​​ ​​InitEvent(parent) {​


​var​​ ​​nodes = $(​​​​"li.bbit-tree-node>div"​​​​, parent);​


​nodes.each(​​​​function​​​​(e) {​


​$(​​​​this​​​​).hover(​​​​function​​​​() {​


​$(​​​​this​​​​).addClass(​​​​"bbit-tree-node-over"​​​​);​


​}, ​​​​function​​​​() {​


​$(​​​​this​​​​).removeClass(​​​​"bbit-tree-node-over"​​​​);​


​})​


​.click(nodeclick)​


​.find(​​​​"img.bbit-tree-ec-icon"​​​​).each(​​​​function​​​​(e) {​


​if​​ ​​(!$(​​​​this​​​​).hasClass(​​​​"bbit-tree-elbow"​​​​)) {​


​$(​​​​this​​​​).hover(​​​​function​​​​() {​


​$(​​​​this​​​​).parent().addClass(​​​​"bbit-tree-ec-over"​​​​);​


​}, ​​​​function​​​​() {​


​$(​​​​this​​​​).parent().removeClass(​​​​"bbit-tree-ec-over"​​​​);​


​});​


​}​


​});​


​});​


​}​


​function​​ ​​getck(items, c, fn) {​


​for​​ ​​(​​​​var​​ ​​i = 0, l = items.length; i < l; i++) {​


​items[i].checkstate == 1 && c.push(fn(items[i]));​


​if​​ ​​(items[i].ChildNodes != ​​​​null​​ ​​&& items[i].ChildNodes.length > 0) {​


​getck(items[i].ChildNodes, c, fn);​


​}​


​}​


​}​


​me[0].t = {​


​getSelectedNodes: ​​​​function​​​​() {​


​var​​ ​​s = [];​


​getck(treenodes, s, ​​​​function​​​​(item) { ​​​​return​​ ​​item });​


​return​​ ​​s;​


​},​


​getSelectedValues: ​​​​function​​​​() {​


​var​​ ​​s = [];​


​getck(treenodes, s, ​​​​function​​​​(item) { ​​​​return​​ ​​item.value });​


​return​​ ​​s;​


​},​


​getCurrentItem: ​​​​function​​​​() {​


​return​​ ​​dfop.citem;​


​}​


​};​


​return​​ ​​me;​


​}​


​//获取所有选中的节点的Value数组​


​$.fn.getTSVs = ​​​​function​​​​() {​


​if​​ ​​(​​​​this​​​​[0].t) {​


​return​​ ​​this​​​​[0].t.getSelectedValues();​


​}​


​return​​ ​​null​​​​;​


​}​


​//获取所有选中的节点的Item数组​


​$.fn.getTSNs = ​​​​function​​​​() {​


​if​​ ​​(​​​​this​​​​[0].t) {​


​return​​ ​​this​​​​[0].t.getSelectedNodes();​


​}​


​return​​ ​​null​​​​;​


​}​


​$.fn.getTCT = ​​​​function​​​​() {​


​if​​ ​​(​​​​this​​​​[0].t) {​


​return​​ ​​this​​​​[0].t.getCurrentItem();​


​}​


​return​​ ​​null​​​​;​


​}​


 


​})(jQuery);​



 

第一步:自然是所有Jquery的控件的第一步都是搭这个架子,兼容JQuery和$避免闭包,避免和其他类库冲突,接受一个参数(是个对象)




​?​



1


2


3


4


5


6




​;(​​​​function​​​​($) {​


​//也可以使用$.fn.extend(treeview:function(setting){}) ​


​$.fn.treeview = ​​​​function​​​​(settings) {​


​}​


 


​})(jQuery);​


那第二步:给控件加一些参数默认参数,同时能调用方法$.extend让最终调用时的参数覆盖默认的(如果没有则使用默认)



​?​



1


2


3


4


5


6


7


8


9


10


11


12


13


14


15




​var​​ ​​dfop ={​


​method: ​​​​"POST"​​​​,​​​​//默认采用POST提交数据​


​datatype: ​​​​"json"​​​​,​​​​//数据类型是json​


​url: ​​​​false​​​​,​​​​//异步请求的url​


​cbiconpath: ​​​​"/images/icons/"​​​​,​​​​//checkbox icon的目录位置​


​icons: [​​​​"checkbox_0.gif"​​​​, ​​​​"checkbox_1.gif"​​​​, ​​​​"checkbox_2.gif"​​​​],​​​​//checkbxo三态的图片​


​showcheck: ​​​​false​​​​, ​​​​//是否显示checkbox          ​


​oncheckboxclick: ​​​​false​​​​, ​​​​//点击checkbox时触发的事件​


​onnodeclick: ​​​​false​​​​,​​​​//点击node触发的时间​


​cascadecheck: ​​​​true​​​​,​​​​//是否启用级联​


​data: ​​​​null​​​​,​​​​//初始化数据             ​


​theme: ​​​​"bbit-tree-arrows"​​ ​​//三种风格备选bbit-tree-lines ,bbit-tree-no-lines,bbit-tree-arrows​


​}​


​//用传进来的参数覆盖默认,没传则保留​


​$.extend(dfop, settings);​


第三步:生成默认数据的HTML(根据我们的分析节点的Dom结构,数据的数据结构,生成节点那是非常的简单),,添加到当前容器中。最后是注册事件这里有一个非常重要的地方,即懒加载(没有展开的节点HTML是不生成的),这就要求我们在树内部要维护一套数据(开销很小),对于性能的提升那是相当的明显。另外一个重要的地方,就是使用一次生成所有展开节点的HTML并通过innerHTML属性来生成Dom,而不是通过append操作,因为直接操作innerHTML比通过dom原生的方法要快上N倍(节点越多,N越大),切记切记!



​?​



1


2


3


4


5


6


7


8


9


10


11


12


13




​var​​ ​​treenodes = dfop.data; ​​​​//内部的数据,其实直接用 dfop.data也可以​


​var​​ ​​me = $(​​​​this​​​​);​


​var​​ ​​id = me.attr(​​​​"id"​​​​);​


​if​​ ​​(id == ​​​​null​​ ​​|| id == ​​​​""​​​​) {​


​id = ​​​​"bbtree"​​ ​​+ ​​​​new​​ ​​Date().getTime();​


​me.attr(​​​​"id"​​​​, id);​


​}​​​​//全局唯一的ID​


 


​var​​ ​​html = [];​


​buildtree(dfop.data, html);​​​​//生成展开节点的HTML,push到数组中​


​me.addClass(​​​​"bbit-tree"​​​​).html(html.join(​​​​""​​​​));​


​InitEvent(me);​​​​//初始化事件​


​html = ​​​​null​​​​;​


在节点生成过程中,同时可生产节点的Path(节点路径),方便检索



​?​



1


2


3


4


5


6


7


8


9


10


11


12


13


14


15


16




​if​​ ​​(nd.hasChildren) { ​​​​//存在子节点​


​if​​ ​​(nd.isexpand) {​​​​//同时节点已经展开则输出子节点​


​ht.push(​​​​"<ul  class='bbit-tree-node-ct'  style='z-index: 0; position: static; visibility: visible; top: auto; left: auto;'>"​​​​);​


​if​​ ​​(nd.ChildNodes) {​


​var​​ ​​l = nd.ChildNodes.length;​


​for​​ ​​(​​​​var​​ ​​k = 0; k < l; k++) {​​​​//递归调用并生产节点的路径​


​nd.ChildNodes[k].parent = nd;​


​buildnode(nd.ChildNodes[k], ht, deep + 1, path + ​​​​"."​​ ​​+ k, k == l - 1);​


​}​


​}​


​ht.push(​​​​"</ul>"​​​​);​


​}​


​else​​ ​​{ ​​​​//否则是待输出状态​


​ht.push(​​​​"<ul style='display:none;'></ul>"​​​​);​


​}​


​}​


注册事件,接受参数parent,即从某一父节点开始附加事件(因为做了个hover效果,所以事件是在每个节点上,如果取消该效果,事件可直接附加Tree上通过Event的srcElement来分发可略提升性能)



​?​



1


2


3


4


5


6


7


8


9


10


11


12


13


14


15


16


17


18


19


20




​function​​ ​​InitEvent(parent) {​


​var​​ ​​nodes = $(​​​​"li.bbit-tree-node>div"​​​​, parent);​


​nodes.each(​​​​function​​​​(e) {​


​$(​​​​this​​​​).hover(​​​​function​​​​() {​


​$(​​​​this​​​​).addClass(​​​​"bbit-tree-node-over"​​​​); ​​​​//鼠标浮动节点的样式变化​


​}, ​​​​function​​​​() {​


​$(​​​​this​​​​).removeClass(​​​​"bbit-tree-node-over"​​​​);​


​})​


​.click(nodeclick)​​​​//node的onclick事件,这个是重点哦​


​.find(​​​​"img.bbit-tree-ec-icon"​​​​).each(​​​​function​​​​(e) { ​​​​//arrow的hover事件,为了实现vista那个风格的​


​if​​ ​​(!$(​​​​this​​​​).hasClass(​​​​"bbit-tree-elbow"​​​​)) {​


​$(​​​​this​​​​).hover(​​​​function​​​​() {​


​$(​​​​this​​​​).parent().addClass(​​​​"bbit-tree-ec-over"​​​​);​


​}, ​​​​function​​​​() {​


​$(​​​​this​​​​).parent().removeClass(​​​​"bbit-tree-ec-over"​​​​);​


​});​


​}​


​});​


​});​


​}​


这里最主要的还是node的click事件,因为他要处理的事情很多,如树的展开收缩(如果子节点不存在,但是hasChildren为真,同时complete属性不为真则需要异步加载子节点,如子节点存在,但是没有Render那么就要Render),点击checkbox要出发级联的事件和oncheckbox事件,点击其他则触发配置条件的nodeonclick事件,这一切都通过前面event的源元素的class来区分点击的对象



​?​



1


2


3


4


5


6


7


8


9


10


11


12


13


14


15


16


17


18


19


20


21


22


23


24


25


26


27


28


29


30


31


32


33


34


35


36


37


38


39


40


41


42


43


44


45


46


47


48


49


50


51


52


53


54


55


56


57


58


59


60


61


62


63


64


65


66


67


68


69


70


71


72


73


74


75




​function​​ ​​nodeclick(e) {​


​var​​ ​​path = $(​​​​this​​​​).attr(​​​​"tpath"​​​​);​​​​//获取节点路径​


​var​​ ​​et = e.target || e.srcElement;​​​​//获取事件源​


​var​​ ​​item = getItem(path);​​​​//根据path获取节点的数据​


​//debugger;​


​if​​ ​​(et.tagName == ​​​​"IMG"​​​​) {​


​// +号需要展开,处理加减号​


​if​​ ​​($(et).hasClass(​​​​"bbit-tree-elbow-plus"​​​​) || $(et).hasClass(​​​​"bbit-tree-elbow-end-plus"​​​​)) {​


​var​​ ​​ul = $(​​​​this​​​​).next(); ​​​​//"bbit-tree-node-ct"​


​if​​ ​​(ul.hasClass(​​​​"bbit-tree-node-ct"​​​​)) {​


​ul.show();​


​}​


​else​​ ​​{​


​var​​ ​​deep = path.split(​​​​"."​​​​).length;​


​if​​ ​​(item.complete) {​


​item.ChildNodes != ​​​​null​​ ​​&& asnybuild(item.ChildNodes, deep, path, ul, item);​


​}​


​else​​ ​​{​


​$(​​​​this​​​​).addClass(​​​​"bbit-tree-node-loading"​​​​);​


​asnyloadc(ul, item, ​​​​function​​​​(data) {​


​item.complete = ​​​​true​​​​;​


​item.ChildNodes = data;​


​asnybuild(data, deep, path, ul, item);​


​});​


​}​


​}​


​if​​ ​​($(et).hasClass(​​​​"bbit-tree-elbow-plus"​​​​)) {​


​$(et).swapClass(​​​​"bbit-tree-elbow-plus"​​​​, ​​​​"bbit-tree-elbow-minus"​​​​);​


​}​


​else​​ ​​{​


​$(et).swapClass(​​​​"bbit-tree-elbow-end-plus"​​​​, ​​​​"bbit-tree-elbow-end-minus"​​​​);​


​}​


​$(​​​​this​​​​).swapClass(​​​​"bbit-tree-node-collapsed"​​​​, ​​​​"bbit-tree-node-expanded"​​​​);​


​}​


​else​​ ​​if​​ ​​($(et).hasClass(​​​​"bbit-tree-elbow-minus"​​​​) || $(et).hasClass(​​​​"bbit-tree-elbow-end-minus"​​​​)) {  ​​​​//- 号需要收缩                    ​


​$(​​​​this​​​​).next().hide();​


​if​​ ​​($(et).hasClass(​​​​"bbit-tree-elbow-minus"​​​​)) {​


​$(et).swapClass(​​​​"bbit-tree-elbow-minus"​​​​, ​​​​"bbit-tree-elbow-plus"​​​​);​


​}​


​else​​ ​​{​


​$(et).swapClass(​​​​"bbit-tree-elbow-end-minus"​​​​, ​​​​"bbit-tree-elbow-end-plus"​​​​);​


​}​


​$(​​​​this​​​​).swapClass(​​​​"bbit-tree-node-expanded"​​​​, ​​​​"bbit-tree-node-collapsed"​​​​);​


​}​


​else​​ ​​if​​ ​​($(et).hasClass(​​​​"bbit-tree-node-cb"​​​​)) ​​​​// 点击了Checkbox​


​{​


​var​​ ​​s = item.checkstate != 1 ? 1 : 0;​


​var​​ ​​r = ​​​​true​​​​;​


​if​​ ​​(dfop.oncheckboxclick) { ​​​​//触发配置的函数​


​r = dfop.oncheckboxclick.call(et, item, s);​


​}​


​if​​ ​​(r != ​​​​false​​​​) {​​​​//如果返回值不为false,即checkbxo变化有效​


​if​​ ​​(dfop.cascadecheck) {​​​​//允许触发级联​


​//遍历​


​cascade(check, item, s);​​​​//则向下关联​


​//上溯​


​bubble(check, item, s); ​​​​//向上关联​


​}​


​else​​ ​​{​


​check(item, s, 1);​​​​//否则只管自己​


​}​


​}​


​}​


​}​


​else​​ ​​{​​​​//点击到了其他地方​


​if​​ ​​(dfop.citem) { ​​​​//上一个当前节点​


​$(​​​​"#"​​ ​​+ id + ​​​​"_"​​ ​​+ dfop.citem.id).removeClass(​​​​"bbit-tree-selected"​​​​);​


​}​


​dfop.citem = item;​​​​//这次的当前节点​


​$(​​​​this​​​​).addClass(​​​​"bbit-tree-selected"​​​​);​


​if​​ ​​(dfop.onnodeclick) {​


​dfop.onnodeclick.call(​​​​this​​​​, item);​


​}​


​}​


​}​



展开节点,异步请求的部分代码应该不是很复杂就不细诉了,关键来讲一下级联

级联有两个问题要处理,第一个是遍历子节点,第二个是上溯到祖节点,因为我们的数据结构这两个操作都显得非常简单




​?​



1


2


3


4


5


6


7


8


9


10


11


12


13


14


15


16


17


18


19


20


21




​//遍历子节点​


​function​​ ​​cascade(fn, item, args) {​


​if​​ ​​(fn(item, args, 1) != ​​​​false​​​​) {​


​if​​ ​​(item.ChildNodes != ​​​​null​​ ​​&& item.ChildNodes.length > 0) {​


​var​​ ​​cs = item.ChildNodes;​


​for​​ ​​(​​​​var​​ ​​i = 0, len = cs.length; i < len; i++) {​


​cascade(fn, cs[i], args);​


​}​


​}​


​}​


​}​


​//冒泡的祖先​


​function​​ ​​bubble(fn, item, args) {​


​var​​ ​​p = item.parent;​


​while​​ ​​(p) {​


​if​​ ​​(fn(p, args, 0) === ​​​​false​​​​) {​


​break​​​​;​


​}​


​p = p.parent;​


​}​


​}​



找到节点的同时都会触发check这个回调函数,来判断当前节点的状态,详细请看下面代码中的注释部分应该是比较清晰,描写了这个过程




​?​



1


2


3


4


5


6


7


8


9


10


11


12


13


14


15


16


17


18


19


20


21


22


23


24


25


26


27


28


29


30




​function​​ ​​check(item, state, type) {​


​var​​ ​​pstate = item.checkstate; ​​​​//当前状态​


​if​​ ​​(type == 1) {​


​item.checkstate = state; ​​​​//如果是遍历子节点,父是什么子就是什么​


​}​


​else​​ ​​{​​​​// 上溯 ,这个就复杂一些了​


​var​​ ​​cs = item.ChildNodes; ​​​​//获取当前节点的所有子节点​


​var​​ ​​l = cs.length; ​


​var​​ ​​ch = ​​​​true​​​​; ​​​​//是否不是中间状态 半选​


​for​​ ​​(​​​​var​​ ​​i = 0; i < l; i++) {​


​if​​ ​​((state == 1 && cs[i].checkstate != 1) || state == 0 && cs[i].checkstate != 0) {​


​ch = ​​​​false​​​​;​


​break​​​​;​​​​//他的子节点只要有一个没选中,那么他就是半选​


​}​


​}​


​if​​ ​​(ch) {​


​item.checkstate = state;​​​​//不是半选,则子节点是什么他就是什么​


​}​


​else​​ ​​{​


​item.checkstate = 2; ​​​​//半选​


​}​


​}​


​//change show 如果节点已输出,而其前后状态不一样,则变化checkbxo的显示         ​


​if​​ ​​(item.render && pstate != item.checkstate) {​


​var​​ ​​et = $(​​​​"#"​​ ​​+ id + ​​​​"_"​​ ​​+ item.id + ​​​​"_cb"​​​​);​


​if​​ ​​(et.length == 1) {​


​et.attr(​​​​"src"​​​​, dfop.cbiconpath + dfop.icons[item.checkstate]);​


​}​


​}​


​}​



至此我们树的主体功能已经完全实现了。其他就是公开一些方法等,大家可详见代码,示例中公开了两个一个当前选中的所有节点,另外一个当前的节点。 

大家可以通过以下网址查看文中的示例,selected拼错了,大家海涵! windows azure部署还是麻烦懒得修改了3500+节点一次加载,大家可以点击根节点的全选来看看速度

 ​​http://jscs.cloudapp.net/ControlsSample/BigTreeSample​​ 

异步加载,按需加载的情况也是非常常用的,使用的是SQL Azure服务器在美国ing,所以可能异步有点慢,本地数据源那是瞬间的

​http://jscs.cloudapp.net/ControlsSample/TreeAsnySample​

FAQ:

1:如何设置每个节点不同的图标?

回答:

其实不用扩展,本身就支持,只是没有说明而已,我们来看一下这个代码吧?在BuildNode方法中有这么一句?      

if (nd.classes) { cs.push(nd.classes); }


在节点的数据结构中可以设置属性classes ,该属性将作为节点特殊的Css Class 添加到节点上。那么利用这点,就可以设置节点的图标了

打造jQuery的高性能TreeView_ico_27

然后就是编写一个Style 即可

打造jQuery的高性能TreeView_html_28

最后来看下效果吧?

打造jQuery的高性能TreeView_jquery_29