This commit is contained in:
Ilya Kantor 2014-10-26 22:10:13 +03:00
parent 06f61d8ce8
commit f301cb744d
2271 changed files with 103162 additions and 0 deletions

View file

@ -0,0 +1,76 @@
# Получение данных
Данные регионов можно хранить в разном виде.
Наиболее естественное представление дерева -- в виде вложенного объекта: свойство `children` содержит поддеревья.
Выглядит это так:
```js
var regions = [
{
title: 'Россия',
id: 1,
children: [
{
title: 'Центр',
id: 2,
children: [ ... поддеревья ... ]
},
...
}
}
...
]
```
У такого вложенного объекта есть важный недостаток: сложно перейти напрямую к узлу по ID. Нужно "прыгать" по дереву.
Поэтому может быть более удобен другой вариант:
```js
var regions = [
{
title: 'Россия',
id: 1,
children: [ 2 ]
},
{
title: 'Центр',
id: 2,
children: [ ... ]
},
...
]
```
..То есть, массив содержит все узлы дерева, и каждый узел хранит в `children` список `id` детей.
Но и это не совсем удобно. Ведь хочется по ID получить данные. Значит, нужно хранить не массив, а объект вида `id => { title: .., id: .., children: [... ] }`.
<ul>
<li>Свойство `children` может отсутствовать, если детей нет.</li>
<li>`id` есть и в ключе объекта и в данных узла. Так удобнее.</li>
<li>Список внешних узлов дерева содержится в корневом узле без имени, с `id = 0`.</li>
</ul>
**Выберите наиболее симпатичную структуру и получите её из исходного документа.**
# Данные
Скрипт для получения данных в последнем формате, описанном выше: [fetch.js](/files/tutorial/widgets/checkbox-tree/fetch.js).
Результат в файле (после `JSON.stringify`): [regions.js](/files/tutorial/widgets/checkbox-tree/regions.js).
# Исправления
Желательно сделать следующие исправления:
<ul>
<li>Переделать вёрстку. В частности, заменить текстовые + - на оформление при помощи CSS.</li>
<li>Оформить всё в виде виджета с шаблоном.</li>
<li>Использовать ленивый рендеринг! Не рисовать всё дерево сразу (зачем рисовать то, чего не видно?), а дорисовывать при открытии.</li>
# Решение
[edit src="solution"/]

View file

@ -0,0 +1 @@
{"name":"checkbox-tree","plunk":"XexJRV6rO0w15179jqGC"}

View file

@ -0,0 +1,23 @@
var result = {
0: { children: [] }
};
$('div[id^="region"]').each(function() {
el = $(this);
var id = el.attr('id').slice(6);
result[id] = {
title: el.children('label').html(),
children: [],
id: id
};
var parent = el.parent().closest('div[id^="region"]');
if (parent.length) {
var pid = parent.attr('id').slice(6);
} else {
pid = 0;
}
result[pid].children.push(+id);
});

View file

@ -0,0 +1,75 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<link href="tree.css" rel="stylesheet">
<script src="http://code.jquery.com/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/lodash.js/2.4.1/lodash.min.js"></script>
<script src="regions.js"></script>
<script src="tree.js"></script>
</head>
<body>
<script id="tree-template" type="text/template">
<ul>
<% $(children).each(function() {
var nodeData = data[this];
var hasChildren = nodeData.children && nodeData.children.length;
%>
<li data-id="<%=nodeData.id%>" class="<%=hasChildren ? 'tree-closed' : ''%>">
<% if (hasChildren) { %> <span class="tree-toggler"></span> <% } %>
<input type="checkbox" value="<%=nodeData.id%>">
<%=nodeData.title%>
</li>
<% }); %>
</ul>
</script>
<script>
// требование - как можно меньше DOM-элементов
// работа с данными из 1000 узлов
/* пример структуры данных
var data = [
{
children: [1,2,3]
},
{
title: 'Россия',
id: 1,
children: [4,5]
},
{
title: 'Украина',
id: 2
},
{
title: 'Беларусь',
id: 3
},
{
title: 'Север',
id: 4
},
{
title: 'Юг',
id: 5
}
];
*/
var tree = new Tree({
template: _.template($('#tree-template').html().trim()),
data: regions
});
tree.getElement().appendTo('body');
</script>
</body>
</html>

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,37 @@
.tree, .tree ul {
list-style: none;
margin: 0;
padding: 2px;
}
.tree .tree-open ul {
display: block;
}
.tree .tree-closed ul {
display: none;
}
.tree-toggler {
color: blue;
cursor: pointer;
float: left;
margin-left: -16px;
width: 1em;
height: 1em;
}
.tree-open .tree-toggler {
background: url(http://js.cx/tree/minus.gif) no-repeat center;
}
.tree-closed .tree-toggler {
background: url(http://js.cx/tree/plus.gif) no-repeat center;
}
.tree li {
line-height: 1em;
padding-left: 16px;
}

View file

@ -0,0 +1,74 @@
function Tree(options) {
var self = this;
var template = options.template;
var data = options.data;
this.getElement = function() {
if (!this.elem) render();
return this.elem;
}
function onTogglerClick(e) {
self.toggle( $(e.currentTarget.parentNode) );
}
function onCheckboxChange(e) {
var checked = e.target.checked;
var id = e.target.value;
var node = $(e.currentTarget.parentNode);
if (checked) {
self.open(node);
}
node.find('input').prop('checked', checked);
}
this.toggle = function(node) {
ensureChildrenRendered(node);
node.toggleClass("tree-open tree-closed");
};
this.open = function(node) {
ensureChildrenRendered(node);
node.addClass("tree-open").removeClass("tree-closed");
}
this.close = function(node) {
ensureChildrenRendered(node);
node.addClass("tree-closed").removeClass("tree-open");
}
function ensureChildrenRendered(node) {
var ul = node.children('ul');
if (!ul.length) {
renderChildren( node.data('id') ).appendTo(node);
if (node.children('input').is(':checked')) {
node.find('input').prop('checked', true);
}
}
}
function renderChildren(id) {
return $(template({
children: data[id].children,
data: data
}));
}
function render() {
self.elem = renderChildren(0)
.addClass('tree');
self.elem.on('click', '.tree-toggler', onTogglerClick);
self.elem.on('change', 'input', onCheckboxChange);
}
}

View file

@ -0,0 +1,33 @@
# Дерево с чекбоксами
[importance 5]
Вы получили проект, в котором есть дерево с чекбоксами.
К сожалению, оно "кривое" -- некрасивое, плохо закодированное и с ошибками. Ваша задача -- сделать "как надо".
Образец:
[iframe src="task" height=400 link edit border=1]
Особенности работы:
<ul>
<li>При изначальном рисовании все элементы закрыты и не отмечены.</li>
<li>Установка отметки в чекбоксе раскрывает узел и отмечает все элементы под ним.</li>
<li>Снятие отметки из чекбокса -- снимает отметки из всех элементов под ним.</li>
</ul>
Требования к реализации:
<ul>
<li>Все данные для дерева (регионы) нужно хранить в JS. Как именно -- определяете вы сами. Можно в одном объекте, можно -- ещё как-то.</li>
<li>Программист, который делал этот код, сейчас недоступен. Ваша задача -- восстановить данные регионов из дерева примера.</li>
<li>Можно считать, что название региона всегда занимает только 1 строку.</li>
<li>Дерево должно легко работать с 1000 узлов (в примере их около 450).</li>
<li>Используйте минимум DOM-элементов.</li>
</ul>
Исходный документ содержит "образец" дерева.
[edit task src="task"/]
P.S. При получении данных из дерева-образца вам поможет `JSON.stringify(объект)`. Он превратит объект в строку, которую можно положить в файл.

View file

@ -0,0 +1 @@
{"name":"checkbox-tree-src","plunk":"Hdhnty2bvLMMM0VH9jjn"}

View file

@ -0,0 +1 @@
{"name":"checkbox-tree-src","plunk":"Hdhnty2bvLMMM0VH9jjn"}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long