Как то попалась мне вот такая задачка. Нужно было сделать двух-уровневые фильтры для акций, потратил я конечно целый денёк на это дело и вот хочу поделиться с вами, что у меня получилось.
Структура таблицы весьма простая: id, name, parent, position. На выходе у нас должен получиться HTML код, имея следующую структуру:
<ul>
<li>...</li>
...
<li>
<ul>
<li></li>
...
</ul>
</li>
...
</ul>
Что мы тут видим, а видим ты тут два списка, где один вложен в другой. Этот код я добавил, для простоты понимания будущей структуры, с которой нам предстоит работать.
И так пристутим к самому интересному. Для того, чтобы перетаскивать списки, вверх и вниз, добавим в каждый тег li один элемент с классом sort. Для фильтров первого уровня добавим к этому элементу ещё один класс lvl1, а для второго уровня lvl2. Для возможности перетаскивания из одного уровня в другой, необходимо добавить ещё один элемент с классом drag. В итоге у нас должно получиться:
<ul class="tags-list">
<li class="droppable draggable">
<span class="sort lvl1"></span>
<span class="drag"></span>
<span class="name">Европа</span>
</li>
<li class="droppable draggable">
<span class="sort lvl1"></span>
<span class="drag disable"></span>
<span class="name">Москва</span>
<ul class="tags-list1">
<li class="droppable draggable">
<span class="sort lvl2"></span>
<span class="drag left"></span>
<span class="name">Подмосковье</span>
</li>
</ul>
</li>
<li class="droppable draggable">
<span class="sort lvl1"></span>
<span class="drag"></span>
<span class="name">Италия</span>
</li>
</ul>
Добавляем функцию инициализации сортировки для одного списка, исключая вложенные. Тем самым у нас получиться, что сортировка будет накладываться на каждый список.
function sort(ul, handle) {
$(ul).sortable({
items:'li',
axis:'y',
handle: handle,
cursor: 'move',
update: function(event,ui) {
//Send data to server
}
});
}
Инициализируем все доступные списки после загрузки страницы:
$(document).ready(function(){
//Инициализируем сортировку внутри списка
sort('.tags-list', '.sort.lvl1');
sort('.tags-list1', '.sort.lvl2');
});
Начинаем работу с перетаскиванием элементов между списками. Создадим функцию, которая будет добавлять перетаскиваемый элемент из первого уровня списка, во второй:
//Доабвление во второй уровень
function appendToRight(obj, droppable) {
if( $(obj).parents(".tags-list1").length ) {
obj = $(obj).parents(".tags-list1").parent();
}
//Если внутри списка нет, создаём его
if( $(obj).find("ul").length == 0 ) {
$(obj).append("");
$(obj).find(".drag").addClass("disable");
}
var parent = droppable.parent();
droppable.find('.drag').addClass("left");
$(obj).find("ul").append(droppable);
if( parent.find('li').length == 0 ) {
parent.parent().find(".drag").removeClass("disable");
parent.remove();
}
$(obj).find("ul .sort").removeClass("lvl1").addClass('lvl2');
$(obj).removeClass("ui-state-active");
//Инициализируем сортировку внутри списка
sort($(obj).find('ul'), '.sort.lvl2');
}
Возникает ситуация, как же нам перетащить элемент из второго списка, в первый. Для этого создаим новый блок, вне главного списка (сверху или снизу):
<ul class="tags-root">
<li class="droppable root">..</li>
</ul>
И добавим функцию, которая будет обрабатывать дроп на корневой элемент. Что делает данная фукнция, переносит в конец списка элемент и проверяет остались ли ещё элементы в предыдущем списке, если нет, то удаляет список.
//Добавление в корень
function appendToRoot(droppable) {
var parent = droppable.parent();
droppable.find('.drag.left').removeClass("left");
$(droppable).find(".sort").removeClass("lvl2").addClass('lvl1');
$('.tags-list').append(droppable);
if( parent.find('li').length == 0 ) {
parent.parent().find(".drag").removeClass("disable");
parent.remove();
}
}
Для этого пропишем действия нажатия и отпускания кнопки мыши, в которых мы будем добавлять / удалять специальный класс drag-drop, по которому мы будем определять действие, что юзер хочет перетащить элемент в другой список. Тут же инициализируем фукнции draggable и droppable. Вот, что у нас получилось:
$(".drag").mousedown(function(){
$(this).parents("li").addClass("drag-drop");
});
$(".drag").mouseup(function(){
var self = this;
setTimeout(function(){
$(self).parents("li").removeClass("drag-drop");
}, 1)
});
$(".tags-list li.draggable").draggable({
handle: ".drag",
revert: true,
start: function() {
if( $(this).find("ul").length ) {
return false;
}
}
});
$('.droppable').droppable({
activeClass: "ui-state-hover",
hoverClass: "ui-state-active",
accept: ".drag-drop",
drop: function( event, ui ) {
setTimeout(function(){
$('ul, li').removeClass("drag-drop ui-state-hover ui-state-active");
},1);
if( $(this).hasClass("root") ) {
appendToRoot(ui.draggable);
} else {
appendToRight(this, ui.draggable);
}
}
});
Вот такой способ. Возможно не совсем идеальный, и возможно кто-то напишет лучше, но почему то я не нашёл в просторах инета...
P.Nixx, 16.10.2012, 16:59