Открыть список
Как стать автором
Обновить

Многоуровневое дерево с маркерами, сохраняющее состояние (HTML, CSS, jQuery, Cookies)

Разработка веб-сайтов
Продолжается развитие темы о многоуровневом дереве с маркерами. Многоуровневое дерево с сохранением состояния узлов
Теперь дерево выросло и окрепло, стало взрослее и помнит выбранный узел и состояние кажого узла в отдельности.
Страницу можно перезагружать, а дерево все равно будет помнить все что вы открыли и выбрали!


Для сохранания состояния использованы Cookies и jquery.cookies.js плагин. Работать с cookies очень просто.

Подготовка Html


Дерево будет работать, если выполнить базовое условие — оформить html в таком виде:
<div id="multi-derevo">
<h4><a href="#">Заголовок</a></h4>
<ul>
<li><span><a href="#1">1. Ветка</a></span>
<ul>
<li><span><a href="#11">1.1. Ветка</a></span>
<ul>
<li><span><a href="#111">1.1.1. Листик</a></span></li>
<li><span><a href="#112">1.1.2. Цветок </a></span></li>
<li><span><a href="#113">1.1.3. Цветок </a></span></li>
</ul>
</li>
</ul>
</li>
<li><span><a href="#2">2. Ветка</a></span></li>
</ul>
</div><!-- /multi-derevo -->


Для сосздания подуровня достатночно вставить после заголовка узла вложенный список такой же структуры.

В принципе глубина вложения не ограничена, но так как все элементы загружаются сразу вы рискуете попасть на трафик и долгое ожидание, так что будте бдительны.

Обновленный скрипт дерева


Все пояснения в комментариях кода.
/*
(c) 2009 r3code.habrahabr.ru
По вопросам модификации под задачу обращайтесь

Скрипт: Построение дерева по готовому HTML списку.
r3code.habrahabr.ru/blog/59823

Выделяем узлы имющие поддеревья и добавляем у ним метку.
Определяет поведение узлов дерева при клике на них.
- Изменяет состояние маркера раскрытия (открыт/закрыт).
- Узлы содержащие в себе другие узлы, по клику разворачиваются
или сворачиваются, в зависимости от текущего состояния.
- При переходе с одного узла на другой снимается выделение (.current)
и пеходит на выбранный узел.
- Определяет последний узел с поддеревом и скрывает соединительную
линию до следующего узла этого уровня.
- Сохранаяет состояние узлов (откр./закр.) и выбранный узел в cookies.
- При установленных cookies состояние узлов восстанавливается при загрузке.

19/05/2009
*/
//=================================================================================

$(document).ready(function () {
/* Расставляем маркеры на узлах, имющих внутри себя поддерево.
Выбираем элементы 'li' которые имеют вложенные 'ul', ставим для них
маркер, т.е. находим в этом 'li' вложенный тег 'a'
и в него дописываем маркер '<em class="marker"></em>'.
a:first используется, чтобы узлам ниже 1го уровня вложенности
маркеры не добавлялись повторно.
*/
var root = $('#multi-derevo');
// уникальные идентификаторы всем узлам, сквозная нумерация (Nested set)
$('li', root).each(function (index) {
this.id = 'n' + index;
});
$('li:has("ul")', root).find('a:first').prepend('<em class="marker"></em>');

// выбрать текущий узел
var current_id = $.cookie('current_node');
if(current_id) $('#'+current_id).find('a:first').toggleClass('current');

// вешаем событие на клик по ссылке
//-----------------------------------
$('li span', root).click(function () {
// снимаем выделение предыдущего узла
$('a.current', root).removeClass('current');
var a = $('a:first',this.parentNode);
a.toggleClass('current');
var current_id = a.parent('li').attr('id');
//alert(a.parents('li').get(0).tagName+"#"+a.parents('li').attr('id'));
setCookie('current_node',a.parents('li').attr('id') || null);
// Выделяем выбранный узел
toggleNode(this.parentNode);
});
//postLoad(); // функция раскрытия по текущему url
openNodes(); // открыть по данным cookie
})

//---------------------------------------------------------------------------------
// Выделил функцию разворачивания дерева в отдельную
function toggleNode(Node) {// node= li
prepareLast(Node);
// анимация раскрытия узла и изменение состояния маркера
var ul=$('ul:first',Node);// Находим поддерево
if (ul.length) {// поддерево есть
ul.slideToggle(200); //свернуть или развернуть
// Меняем сосотояние маркера на закрыто/открыто
var em=$('em:first',Node);// this = 'li span'
// было em.hasClass('open')?em.removeClass('open'):em.addClass('open');
em.toggleClass('open');
saveTreeState();
}
}

// функция обработки последнего узла в уровне
function prepareLast(Node) {
/* если это последний узел уровня, то соединительную линию к следующему
рисовать не нужно */
$(Node).each(function(){
if (!$(this).next().length) {
/* берем корень разветвления <li>, в нем находим поддерево <ul>,
выбираем прямых потомков ul > li, назначаем им класс 'last' */
$(this).find('ul:first > li').addClass('last');
}
})
}
// функция разворачивания дерева до выбранной ранее ссылки
function postLoad(){
var url = window.location.toString();
var max = 0;
var a = null;
$('#multi-derevo li span a').each(function(){
// сравниваем адрес страницы и ссылку из атрибута
if(url.indexOf(this.href) >= 0 && this.href.length > max){
a = this;
max = this.href.length;
}
});
// если узел не виден, то разворачиваем дерево
if ($(a).is(':hidden') || $(a).parents(':hidden').length) {
var li = $(a).parents().filter('li');
prepareLast(li);
toggleNode(li);
}
// выделим выбранный узел
if (a) {
$(a).toggleClass('current');
}
else { // первый показ, выберем первую ссылку (можно убрать если не нужно)
$('#multi-derevo li span a:first').toggleClass('current');
}
}

// подготовка информации о сосотояниях узлов
function GetOpenedNodes(items){ // li:has('ul')
var str = [];
$(items).each(function() {
var res = $(this).attr('id');
var state = $('em:first',this).hasClass('open') ? 1 : '';
if(res && state){
str.push(res);
}
});
return str.join(',');
}

// сохранить полный список открытых узлов
function saveTreeState(){
var open_id = GetOpenedNodes($('#multi-derevo li:has("ul")')) || null;
setCookie("open_nodes", open_id);
return false;
}

// раскрытие узлов по указанному списку
function openNodes(){
// читаем куки и открываем узлы
var open_nodes = $.cookie("open_nodes");
if(open_nodes) {
var nodes = open_nodes.split(',');

if(nodes[0]){
for(var node in nodes){
nodes[node] = '#' + nodes[node];
}
var ids = nodes.join(',');
$(ids).each(function() {
toggleNode($(this));
});
}
}
return false;
}

// настройки хранить в Cookies 1 день
function setCookie(name, value){
var DAY = 24 * 60 * 60 * 1000;
var date = new Date();
date.setTime(date.getTime() + (1 * DAY)); // 1 день
$.cookie(name, value, {expires: date});
// alert("Cookie set: "+name+"="+value);
}



Мне конечно хотелось бы сделать рефакторинг кода с учетом возможностей jQuery, этот вариант написан так как я знаю. не откажусть от полезных мыслей по улучшению вида этого кода.

Скрипт в работе


Посмотреть работающий пример.

Пример 2. Маркеры вынесены левее, чтобы текст был выравнен по одной вертикальной линии на одном уровне. Изменен CSS и скрипт.

UPD 19.05.2009
Отказался от сохранения состояния дерева при unload — Opera не поддерживает. Теперь состояние дерева сохраняется при клике на узел. Пример и скрипт обновлен.

UPD 24.02.2012
Сделан еще один вариант оформления как просил sanch3z
habrahabr.ru/blogs/webdev/59823/#comment_1641197
Теги:многоуровневое деревоверсткаcsshtmlдеревомаркерыjquerytreejavascriptcookiespersistentпамять
Хабы: Разработка веб-сайтов
Всего голосов 38: ↑35.5 и ↓2.5 +33
Просмотры16.5K

Комментарии 48

Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

Похожие публикации

WordPress-разработчик / HTML-верстальщик
от 40 000 ₽FLUENTRUSSIAСанкт-ПетербургМожно удаленно
Fullstack Web Developer
до 120 000 ₽УмназияМосква
Веб-разработчик на Битрикс/Bitrix
от 100 000 до 150 000 ₽АНО "Московский спорт"МоскваМожно удаленно
Грамотный Frontend-dev
от 120 000 до 150 000 ₽Relictum ProМожно удаленно

Лучшие публикации за сутки