Новый взгляд на вложенные списки

В этой статье мы рассмотрим, как с помощью CSS и jQuery можно сделать списки с вложенными элементами более удобными для просмотра и использования. Такая структура хорошо подходит для руководств, документации и файловых менеджеров.
Перейдём к примерам.
Первый пример – простой список с вложенными элементами.
Второй – всё тот же список, но с использованием jQuery плагина showTree. Плагин полностью работоспоcобен в IE 7+, Firefox, Opera, Safari и Chrome.
Именно его мы сегодня и будем изучать.

jQuery

Рассмотрим подробнее код плагина:
(function($){
	$.fn.showTree = function(o){
		var o = $.extend({
			closeFolders: false, // по умолчанию список раскрыт целиком
			classTree: null // дополнительный класс для списка
		}, o);
		return $(this).each(function(){ // проходим по всем спискам
			var tree = $(this); // присваиваем переменной tree содержание текущего списка 
			tree.addClass('jquery-tree').children('li:first').prepend('<div class="first"></div>'); // добавляем класс по умолчанию и скрываем верхнюю линию в первом элементе списка
	       	        tree.children('li:last').addClass('last'); // для последнего элемента в списке добавляем класс last 
			tree.find('ul').each(function(){ // проходим по всем вложенным спискам
				$(this).children('li:last').addClass('last'); // для последнего элемента в каждом из них добавляем класс last
			}).prev('a').addClass('folder'); // добавляем класс folder каждой ссылке, за которой идёт вложенный список

			if (o.classTree) tree.addClass(o.classTree); // если есть пользовательский класс, добавляем его 
			tree.find('a.folder').before('<span class="show"></span>'); // добавляем кнопку показа/скрытия содержимого папки
			tree.find('span').click(openTree); // каждой кнопке по клику на неё ставим функцию openTree
			if (o.closeFolders) { // если содержимое списков должно быть скрыто
				tree.find('ul').hide(); // скрываем его
			} else {
				tree.find('.folder').addClass('open').prev('span').addClass('minus'); // иначе показываем что папки открыты
			}
        	});
		function openTree(){ // функция, показывающая/скрывающая содержимое папки
			var button = $(this); // присваиваем переменной button значение нажатой кнопки
		   	var link = button.next(); // находим ссылку
			var files = link.next('ul'); // находим вложенные файлы
			if (link.hasClass('open')) { // если папка открыта
				files.hide(); // скрываем файлы
			} else {
				files.show(); // иначе показываем
			}
			link.toggleClass('open'); // переключаем классы у кнопки и папки 
			button.toggleClass('minus'); // если класса нет, добавляем, иначе - убираем
			return false; 
		}
	};
})(jQuery) 

Зачем нужны классы first и last?

Они несут чисто декоративную функцию – скрывают линии там, где они они не нужны.
Сравните:

Как происходит вызов функции?

$('.tree').showTree(); // простой вызов без дополнительных параметров
$('.tree').showTree({closeFolders: true}); // все вложенные папки скрыты 
$('.tree').showTree({closeFolders: true, ,classTree:'classic'}); // все вложенные папки скрыты и используется пользовательский класс 
Вот пример вызова последней функции, созданный из оглавления руководства по PHP:

CSS

Основная часть CSS:
.jquery-tree ul {
	padding:0; 
	margin:0;
}
.jquery-tree li {
	list-style:none; 
	padding-left:8px;
	margin-left:8px;
	border-left:1px dotted #838383;
	background:url("img/line.gif") no-repeat 0px 9px; /* добавляем горизонтальную линию около каждого элемента */
}
.jquery-tree a {
	text-decoration:none; 
	padding-left:18px;
	background:url("img/file.png") no-repeat 1px 2px; /* картинка для обычного элемента */
}
.jquery-tree a.folder {
	background:url("img/folder.png") no-repeat 1px 2px; /* картинка для закрытой папки */
}
.jquery-tree .last {
	border:none;
	padding-left:9px;
	background:url("img/line_last.gif") 0px top no-repeat; /* закрывающий элемент */
}
.jquery-tree .first { /* прикрываем верхнюю часть линии первого элемента */
	margin-left:-9px;
	width:2px;
	height:8px;
	float:left;
	background-color:#FFF;
}
.jquery-tree .show  { 
	margin-left:-16px;
	width:16px;
	height:16px;
	background:url("img/plus.png") left 2px no-repeat; /* картинка для кнопки показа */
	float:left;
	display:block;
	border:0;
	padding:0;
}
.jquery-tree .show.minus {
	background-image:url("img/minus.png"); /* картинка для кнопки скрытия */
}
.jquery-tree a.open  {
	background-image:url("img/folder_open.png"); /* картинка для открытой папки */
}

Дополнительные стили для элементов

Также вы можете задавать свои стили для элементов:
.jquery-tree a.photo {
	background-image:url("img/photo.png");
}
.jquery-tree a.text {
	background-image:url("img/text.png");
}

HTML:
<ul id="tree_two">
	<li><a href="/">Главный документ</a></li>
	<li><a href="/">Папка</a>
	<ul>
		<li><a href="/">Папка с изображениями</a>
		<ul>
			<li><a href="/" class="photo">Изображение</a></li>
			<li><a href="/" class="photo">Изображение</a></li>
			<li><a href="/" class="photo">Изображение</a></li>
		</ul></li>
		<li><a href="/" class="text">Текстовый документ</a></li>
	</ul></li>
</ul>
Как это выглядит:

CSS-хаки

При необходимости можете добавить CSS-хак для IE:
<!--[if IE]><style>
.jquery-tree .last {background-position:0px -1px;}
.jquery-tree .first {height:9px;}
</style><![endif]-->
Если кто-то подскажет, как решить проблему с border:dotted в Opera и Chrome, я буду очень благодарна.

HTML

Сам HTML довольно прост – в нём используются лишь вложенные списки, созданные с помощью тегов li и ul.
<ul class="tree">
	<li><a href="/">Главный документ</a></li>
	<li><a href="/">Родительская папка</a>
	<ul>
		<li><a href="/">Вложенный документ</a></li>
		<li><a href="/">Вложенная папка</a>
		<ul>
			<li><a href="/">Важный документ</a></li>
			<li><a href="/">Ещё один документ</a></li>
			<li><a href="/">Самый важный документ</a></li>
			<li><a href="/">Ненужные документы</a>
			<ul>
				<li><a href="/">Первый не нужный документ</a></li>
				<li><a href="/">Второй</a></li>
				<li><a href="/">Третий</a></li>
			</ul></li>
		</ul></li>
		<li><a href="/">Очередной документ</a></li>
	</ul></li>
    <li><a href="/">Вторая родительская папка</a>
	<ul>
		<li><a href="/">Вложенная папка</a>
		<ul>
			<li><a href="/">Ещё одна папка</a>
			<ul>
				<li><a href="/">Спрятанный документ</a></li>
			</ul></li>
			<li><a href="/">Ещё один документ</a></li>
		</ul></li>
	</ul></li>
</ul>


Разбор очередного плагина закончен.
Успехов в работе с jQuery :)

Если вам что-то не понятно – задавайте вопросы в комментариях.




Комментарии

9 июля 2010, 21:40 (Ответить)
А как сделать чтобы при нажатии на заголовок папки, папка раскрывалась?

tree.find('span').click(openTree); // каждой кнопке по клику на неё ставим функцию openTree
tree.find('a.folder').click(openTree);

что-то не работает :)
10 июля 2010, 11:37 (Ответить)
, не работает, потому что функция openTree изначально работает с кнопкой открытия/закрытия, а не с заголовком.
Чтобы это исправить, нужно заменить содержимое функции openTree:
function openTree(){
	if ($(this).hasClass('folder')) { // если это заголовок
		var link = $(this); // текущий объект это заголовок
		var button = link.prev(); // кнопка - предыдущая
	}  else { // если это кнопка
		var button = $(this); // кнопка - текущий объект
		var link = button.next(); // заголовок - следующий
	}
	var files = link.next('ul');
	if (link.hasClass('open')) {
		files.hide();
	} else {
		files.show();
	}
	link.toggleClass('open');
	button.toggleClass('minus');
	return false;
}
12 июля 2010, 23:44 (Ответить)
Очень полезная статья, был на ней давно и не жалею что сохранил ссылку :) Мне она сейчас очень поможет :)
15 июля 2010, 0:57 (Ответить)
Не могу сделать так, чтобы нажимая на текст в заголовке открывалась папочка с вложеным ul... Читаю верхн.. статью, не получаетса..... Можно подробднее что делать заменив функцию в showtree.jquery.js ?
15 июля 2010, 10:38 (Ответить)
, после того как вы замените функцию openTree на приведенную в комментариях,
найдите в коде строку
tree.find('a.folder').before('<span class="show"></span>');

и замените её на
tree.find('a.folder').before('<span class="show"></span>').click(openTree);
15 июля 2010, 21:20 (Ответить)
Помогло :) Спасибо :)
11 октября 2010, 15:56 (Ответить)
Привет, а плагин поддерживается? а то других таких же симпатичных найти не могу)))

та же проблема с заголовками...
11 октября 2010, 17:03 (Ответить)
кстати, может папку img переименовать в ShowTreeImg?
14 октября 2010, 21:23 (Ответить)
Конечно, вы можете это сделать, если вам так удобнее :)
14 октября 2010, 21:22 (Ответить)
Артур, для решения проблемы с заголовками можно использовать код, приведённый мной чуть выше в комментариях.
26 октября 2010, 22:01 (Ответить)
Очень хороший материал! Скажите, а можно сделать так, чтобы при клике на документ (переход по ссылке) структура раскрытого дерева сохранялась, а кликнутый элемент выделился?
26 октября 2010, 22:23 (Ответить)
ds, изначально я хотела реализовать такой функционал, но потом подумала, что большинству пользователей это не понадобится.
Поэтому, если вам необходима функция сохранения состояния дерева, советую присмотреться к следующим скриптам:
www.linkexchanger.su/2008/49.html
habrahabr.ru/blogs/webdev/59823/
site-96.ru/blog/document287/
(все описания скриптов - на русском.)
26 октября 2010, 23:19 (Ответить)
Спасибо!
2 декабря 2010, 11:39 (Ответить)
Как реализовать добывление/удаление элементов в таком скрипте?
2 декабря 2010, 16:50 (Ответить)
Если добавлять/удалять динамически (т. е. уже после загрузки страницы) - то никак. Такую возможность я не предусмотрела.
18 августа 2011, 16:30 (Ответить)
Динамика реализуется с помощью live(), насколько я помню
20 ноября 2011, 14:50 (Ответить)
только вот ссылки не работают никакие изза return false..
5 мая, 20:45 (Ответить)
Добрый день... пробывал поменять код чтобы корректно отрабатывало на дополнительные теги не получилось... как можно поправить?
Например так уже не работает скрипт...
Тест
Тест Или так
5 мая, 20:46 (Ответить)
Тест

Добавить комментарий