|
Задача. Разработать галерею с неограниченной вложенностью в категориях. Категории должны поддерживать локализацию (русский и английский языки). Должна быть панель администратора. Все что можно, реализовать с помощью Аякс. В самом начале я получил, штмл верстку галереи. Не идеально, но концепция понятна. За прототип дерева было взято цсс-дерево из техногрета Артемия Лебедева.. Недостатки того дерева: поддерживает только один уровень вложенности, в ИЕ плохо выглядит. Уже вырисовывается первая задача. Создание дерева Я подумываю об использовании дерева из библиотеки ext.js. Но решаю не внедрять библиотеку в этот проект. В проекте использую только prototype.js. Итак, создаем дерево с неограниченной вложенностью. Оно должно позволять добавлять, удалять и редактировать узлы. Задача ясна. Первое что решаю сделать — добавить в дерево поддержку ИЕ и научить неограниченной вложенности. Делаю это, используя просто штмл и цсс. До динамики еще время дойдет. В итоге получаю такой вид дерева. Из рисунка видно что начальный вариант несколько изменился. Штмл такого вида: 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 | <ul class="ul_maincategory">
<li class="">
<div>
<p>
<a class="sc" href="javascript:expand_or_collapse();"><span class="expanded_node" />
</a><a class="title" title="ARATA" href="javascript:open_item();">ARATA</a></p>
</div>
<ul class="ul_subcategory">
<li class="">
<div>
<p style="left: 1.89em;">
<a class="sc" href="javascript:expand_or_collapse();"><span class="expanded_node" />
</a><a class="title" title="Подкатегория1" href="javascript:open_item();">Подкатегория1</a></p>
</div>
<ul class="ul_subcategory" style="border-left: 2px solid white; margin-left: -1px;">
<li class="">
<div>
<p style="left: 1.89em;">
<a class="sc" href="javascript:expand_or_collapse();"><span class="expanded_node" />
</a><a class="title" title="Подкатегория1" href="javascript:open_item();">Подкатегория1</a></p>
</div>
<ul class="ul_subcategory" style="border-left: 2px solid white; margin-left: -1px;">
<li>
<div>
<p>
<a class="sc" href="javascript:expand_or_collapse();" /><a class="title" title="Подкатегория1"
href="javascript:open_item();">Подкатегория1</a></p>
</div>
</li>
</ul>
</li>
</ul>
</li></p>
</ul>
</li>
<li>
<div>
<p>
<a class="sc" href="javascript:expand_or_collapse();" /><a class="title" href="javascript:open_item();"
title="CATALANO">CATALANO</a></p>
</div>
</li>
<li>
<div>
<p>
<a class="sc" href="javascript:expand_or_collapse();" /><a class="title" href="javascript:open_item();"
title="MONILE">MONILE</a></p>
</div>
</li>
<li>
<div>
<p>
<a class="sc" href="javascript:expand_or_collapse();" /><a class="title" href="javascript:open_item();"
title="LENVAL">LENVAL</a></p>
</div>
</li>
<li>
<div>
<p>
<a class="sc" href="javascript:expand_or_collapse();" /><a class="title" href="javascript:open_item();"
title="ROBERTO GIANNOTTI">ROBERTO GIANNOTTI</a></p>
</div>
</li>
</ul>
|
Вырисовывается задача с помощью джаваскрипта создать тоже самое.
На вход нужно давать данные. Выбор формата очевиден.
JSON, лучше в JS нет!
Ручками создаем данные. С помощью этих данных будет отображено дерево категорий.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 | var
data={nodes:[{name:'ARATA',hint:'ARATA',url:'ARATA',expanded:false,
languages:[{language:'Русский',name:'ARATA',hint:'ARATA'},
{language:'English',name:'ARATA',hint:'ARATA'}],
categoryId:58,
childNodes:[{name:Кольца,hint:Кольца,url:'arata/rings',expanded:false,
languages:[{language:'Русский',name:Кольца,hint:Кольца},
{language:'English',name:'Rings',hint:'Rings'}],
categoryId:64}]},
{name:'CATALANO',hint:'CATALANO',url:'CATALANO',expanded:false,
languages:[{language:'Русский',name:'CATALANO',hint:'CATALANO'},
{language:'English',name:'CATALANO',hint:'CATALANO'}],categoryId:59},
{name:'MONILE',hint:'MONILE',url:'MONILE',expanded:false,
languages:[{language:'Русский',name:'MONILE',hint:'MONILE'},
{language:'English',name:'MONILE',hint:'MONILE'}],categoryId:60},
{name:'LENVAL',hint:'LENVAL',url:'LENVAL',expanded:false,
languages:[{language:'Русский',name:'LENVAL',hint:'LENVAL'},
{language:'English',name:'LENVAL',hint:'LENVAL'}],categoryId:62},
{name:'ROBERTO GIANNOTTI',hint:'ROBERTO GIANNOTTI',
url:'ROBERTO GIANNOTTI',expanded:false,
languages:[{language:'Русский',name:'ROBERTO GIANNOTTI',hint:'ROBERTO GIANNOTTI'},
{language:'English',name:'ROBERTO GIANNOTTI',hint:'ROBERTO GIANNOTTI'}],categoryId:63}]};
|
Итак, можно заняться наконец рендерингом дерева.
Рендеринг дерева
Задача: с помощью джаваскрипта отрендерить дерево. На вход будут поступать джейсон данные, на выходе получится штмл.
В объектной структуре особо нечего выдумывать, классы те же что и везде (класс дерева и класс узла):
Tree – содержит в себе ноды, рисует дерево итд.
TreeNode – реализация логики узла.
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 | var Tree = Class.create();
Tree.prototype = {
initialize:function(container,dataSource)
{
this.el = $(container);
this.data= dataSource;
this.el.className = "tree";
this.childNodes =new Array();
this.el.onclick = this.deselectAll.bindAsEventListener(this);
this.render();
this.selectedNode = this;
},
render:function()
{
this.nodesEl =createElement("ul","",this.el,"ul_maincategory");
this.createChilds();
this.renderChilds();
this.updateConnectors();
},
createChilds:function()
{
for(var i=0;i<this.data.nodes.length;i++)
{
this.appendNode(new TreeNode(this.data.nodes[i],this,this));
}
},
renderChilds:function()
{
for(var i=0;i<this.childNodes.length;i++)
{
this.childNodes[i].render();
}
},
appendNode:function(child)
{
this.childNodes.push(child);
},
appendNodeToSelected:function(config)
{
var selectedNode = this.selectedNode?this.selectedNode:this;
node = new TreeNode(config, this,selectedNode);
if(selectedNode){
selectedNode.appendNode(node);
selectedNode.updateNodesEl();
}
else {this.appendNode(node);}
node.render();
this.updateConnectors();
return node;
},
updateNodesEl:function()
{},
setSelectedNode:function(node)
{
if(this.selectedNode&&this.selectedNode!=this) this.selectedNode.deselectNode();
this.selectedNode = node;
if(this.onSelect) this.onSelect(node);
},
selectFirstNode:function()
{
this.childNodes[0].onClickHandler();
},
deselectAll:function()
{
this.setSelectedNode(this);
},
deleteSelectedNode:function()
{
if(!this.selectedNode) return;
this.selectedNode.deleteNode();
this.updateConnectors();
},
updateConnectors:function()
{
//update recursively childs
for(var i=0;i<this.childNodes.length;i++)
{
this.childNodes[i].updateConnectors();
}
}
}
|
Из полезного, метод удаления выбраного нода (deleteSelectedNode), добавление нода (appendNode), добавление нода в дети текущего узла (appendNodeToSelected).
Код нода не показываю тут, так как его много. В ноде реализованы методы редактирующие его отображение и данные.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 | setTitle:function(title)
{
this.titleEl.innerHTML = title;
this.data.name = title;
this.title = title;
},
setHint:function(hint)
{
this.titleEl.title = hint;
this.data.hint = hint;
this.hint = hint;
},
setUrl:function(url)
{
this.data.url = url;
},
|
А также код удаления:
| deleteNode:function()
{ this.parentNode.childNodes=deleteElementFromArray(this.parentNode.childNodes,this.getIndex());
destroyElement(this.el);
this.el = null;
if(this.parentNode.childNodes.length==0) {destroyElement(this.parentNode.nodesEl);
this.parentNode.nodesEl=null;};
if(this.parentNode!=this.tree)
this.parentNode.updateNodesEl();
this.tree.selectedNode = null;
},
| В завершении Задача выполнена, дерево отображается в ИЕ и ФФ; позволяет добавлять удалять и редактировать ноды; содержит удобное АПИ для работы. Пример посмотреть можно здесь. Скачать пример здесь.
|
|