UPDATE:回答网友提出的设置节点的自定义图片的问题,同时欢迎你们提问,我尽可能在第一时间回复,详见最后 2009-11-03javascript
项目中常常会遇到树形数据的展示,包括导航,选择等功能,因此树控件在大多项目中都是必须的。那一个实用的树应该具有什么功能呢?css
根据个人项目实践状况,主要是几个关键点:html
1:支持静态的树,即一次性将所有数据加载到客户端。java
2:异步树,即一次只加载一级或若干级节点,子节点能够异步加载数据。node
3:Checkbox树(多是静态树也多是异步树),用于选择(如选择组织机构,选择数据字典项)等,最好是可以支持节点级联(这个是难点)jquery
4:可以承载大数据量,并性能表现优异web
5:可以在主流浏览器中运行良好ajax
那我要打造的TreeView就是为了实现这个5个主要指标的。json
先来看下效果图windows
上图是中国行政区域的数据树,总共得节点是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的节点结构
Jquery.TreeView IE6 下 展开第三级即出现错位
而我采用的也是第二种方式,可是缩进采用了填空的方式,即缩进的位置用空白的图片填充来避免Jquery.treeview的问题
个人树节点结构
肯定了节点的HTML咱们就能够来写CSS了。有了效果图,有个节点结构接着就编写CSS了
下面是CSS的完整代码
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的样式
css中所用到的全部图片
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了
先上个完整代码,再逐一分析
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);
|
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);
|
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
;
|
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>"
);
}
}
|
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"
);
});
}
});
});
}
|
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 添加到节点上。那么利用这点,就能够设置节点的图标了
而后就是编写一个Style 便可
最后来看下效果吧?