Сортировка элементов

150

Взаимодействие Sortable позволяет изменять порядок расположения элементов в наборе путем их перетаскивания из одного места в другое. Чтобы применить взаимодействие Sortable, следует выбрать элемент, содержащий отдельные объекты, которые вы хотите отсортировать, и вызвать метод sortable(). Соответствующий простой пример приведен ниже:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>jQuery UI</title>
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
    <script src="//ajax.aspnetcdn.com/ajax/jquery.ui/1.10.3/jquery-ui.min.js"></script>
    <link rel="stylesheet" href="http://ajax.aspnetcdn.com/ajax/jquery.ui/1.10.3/themes/sunny/jquery-ui.css">
    <style type="text/css">
        div.sortable { width: 100px; background-color: lightgrey; font-size: large;
            float: left; margin: 6px; text-align: center; border: medium solid black;
            padding: 10px;}
    </style>
    <script type="text/javascript">
$(function() {
	
     $('#sortContainer').sortable();  

});
    </script> 
</head>
<body>
    <div id="sortContainer">
        <div id="item1" class="sortable ui-state-error">Элемент 1</div>
        <div id="item2" class="sortable ui-state-error">Элемент 2</div>
        <div id="item3" class="sortable ui-state-error">Элемент 3</div>
    </div>
</body>
</html></code>

Запустить пример

В этом примере мы создаем ряд элементов div и назначаем им класс sortable. Для создания взаимодействия мы выбираем родительский элемент div (атрибут id которого равен sortContainer) и вызываем метод sortable(). В результате мы получаем возможность менять порядок расположения трех элементов div путем их перетаскивания в новые позиции. Этот процесс проиллюстрирован на рисунке:

Сортировка элементов путем их перетаскивания

Здесь для демонстрации взаимодействия Sortable элемент под названием "Элемент 2" перетаскивается вправо в окне браузера. Как только он минует элемент под названием "Элемент 3", элементы переставляются и располагаются в новом порядке. В данном случае элемент перемещался на одну позицию, но ничто не мешает перетаскивать элементы сразу на несколько позиций.

Определение порядка сортируемых элементов

В некоторых случаях требуется определить, в каком порядке располагаются элементы после их перетаскивания пользователем. Для получения этой информации можно вызвать метод toArray, который возвращает JavaScript-массив, содержащий значения атрибута id сортируемых элементов.

Ниже приведен пример вывода на консоль текущего порядка элементов после щелчка на кнопке:

...
$(function() {
	
     $('#sortContainer').sortable();
	 
    $('<div id=buttonDiv><button>Получить порядок</button></div>').appendTo('body');
	
    $('button').button().click(function() {
        var order = $('#sortContainer').sortable("toArray");
        for (var i = 0; i < order.length; i++) {
            console.log("Position: " + i + " ID: " + order[i]);
        }
    })

});
...

В результате щелчка на кнопке вызывается метод toArray, и содержимое результирующего массива выводится на консоль:

Добавление кнопки для вывода на консоль позиций элементов в списке

Настройка взаимодействия Sortable

Взаимодействие Sortable в значительной мере зависит от взаимодействия Draggable, описанного в предыдущей статье. Это означает, что все опции взаимодействия Draggable (такие, как axis или tolerance) с тем же эффектом могут применяться и для настройки взаимодействия Sortable. В связи с этим я не буду вновь подробно описывать все настройки и остановлюсь лишь на тех из них, которые свойственны лишь взаимодействию Sortable и чаще всего используются. Их перечень приведен в таблице ниже, а подробные описания содержатся в следующих разделах:

Свойства взаимодействия Sortable
Свойство Описание
connectWith Определяет другой сортируемый элемент-контейнер, с которым должна быть установлена связь, обеспечивающая возможность взаимного перемещения элементов между контейнерами. Значение по умолчанию — false; ему соответствует отсутствие таких связей
dropOnEmpty Если эта опция равна false, то элементы не могут быть перемещены в связанный сортируемый контейнер, когда он пуст. Значение по умолчанию - true
items Определяет селектор, устанавливающий, какие элементы будут сортируемыми. Значение по умолчанию "> *", оно соответствует выбору всех потомков элемента, для которого был вызван метод sortable()
placeholder Определяет класс, который будет назначен элементу, созданному для заполнения позиции, занимаемой сортируемым элементом до его перемещения в новое расположение

Связывание сортируемых контейнеров между собой

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

Определив значения свойства connectWith для обоих элементов, можно сделать эту связь двухсторонней, как показано в примере ниже:

<!DOCTYPE html>
...
    <style type="text/css">
        div.sortable { width: 100px; background-color: lightgrey; font-size: large;
            margin: 4px; text-align: center; border: medium solid black; padding: 10px;}
        #fruitContainer {position: absolute; right:50px}
        #flowerContainer {position: absolute; left:50px}
        div.flower {background-color: lightgreen}
    </style>
    <script type="text/javascript">
$(function() {
    $('#fruitContainer').sortable({
        connectWith: '#flowerContainer'
    });
    $('#flowerContainer').sortable({
        connectWith: '#fruitContainer'
    });
});
    </script>
</head>
<body>
    <div id="fruitContainer" class="sortContainer">
        <div id="fruit_1" class="sortable fruit">Яблоко</div>
        <div id="fruit_2" class="sortable fruit">Апельсин</div>
        <div id="fruit_3" class="sortable fruit">Банан</div>
        <div id="fruit_4" class="sortable fruit">Груша</div>
    </div>
    <div id="flowerContainer" class="sortContainer">
        <div id="flower_1" class="sortable flower">Астра</div>
        <div id="flower_2" class="sortable flower">Пион</div>
        <div id="flower_3" class="sortable flower">Лилия</div>
        <div id="flower_4" class="sortable flower">Орхидея</div>        
    </div> 
</body>
</html>

Запустить пример

В этом примере создаются две группы элементов, и для контейнерного элемента каждой группы вызывается метод sortable(). Для связывания групп между собой используется опция connectWith. Результат представлен на рисунке:

Перемещение элементов между связанными сортируемыми контейнерами

Связывание перемещаемого элемента с сортируемым контейнером

Также имеется возможность связать между собой перемещаемый и сортируемый элементы. Для этого следует использовать в перемещаемом элементе опцию connectToSortable, указав в качестве ее значения селектор, выбирающий сортируемый контейнерный элемент, с которым вы хотите установить связь. В примере ниже показано, как это можно сделать:

<!DOCTYPE html>
...
    <script type="text/javascript">
$(function() {
	
     $('#fruit_1').draggable({
        connectToSortable: '#flowerContainer',
        helper: "clone"
    });
    $('#flowerContainer').sortable();
	
});
    </script>
</head>
<body>
    <div id="fruitContainer" class="sortContainer">
        <div id="fruit_1" class="sortable fruit">Яблоко</div>
    </div>
    <div id="flowerContainer" class="sortContainer">
        <div id="flower_1" class="sortable flower">Астра</div>
        <div id="flower_2" class="sortable flower">Пион</div>
        <div id="flower_3" class="sortable flower">Лилия</div>
        <div id="flower_4" class="sortable flower">Орхидея</div>        
    </div> 
</body>
</html>

Запустить пример

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

Связывание перемещаемых и сортируемых элементов

Это работает безукоризненно в тех случаях, когда значением свойства helper перемещаемого элемента является clone. При других значениях этого свойства также получаются правильные результаты, но при этом выводятся сообщения об ошибках.

Выбор сортируемых элементов

Можно выбирать по своему усмотрению, какие из элементов, содержащихся в контейнере, могут участвовать в сортировке. Для этого используется опция items, принимающая в качестве значения селектор элементов, которые вы хотите сделать сортируемыми. Элементы, которые не соответствуют селекторам, не могут переставляться в сортируемом контейнере. Соответствующий пример приведен ниже:

...
$(function() {
	
    $('div.flower:odd').css("background-color", "salmon")
    
    $('#flowerContainer').sortable({
        items: '.flower:even'
    });
	
});
...

Запустить пример

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

Работая с опцией items, вы должны знать об одной особенности. Элемент, не соответствующий селектору, нельзя перетащить в новую позицию, если только перед этим он не был вытеснен со своей позиции другим элементом. Так, например, в приведенном примере если элемент "Астры" смещается в другую позицию и при этом принудительно смещает элемент "Пионы", будучи один раз вытесненным из своей начальной позиции, элемент "Пионы" приобретает способность к перемещению и сортировке, как если бы он соответствовал селектору, определяемому опцией items.

Стилевое оформление опустевшей позиции

В результате перетаскивания элемента в другое место он оставляет после себя незаполненную позицию. Опция placeholder позволяет назначить пустой позиции некий класс CSS. Эту возможность удобно использовать для визуального выделения того места в документе, которое готово принять элемент. Пример использования опции placeholder приведен ниже:

<!DOCTYPE html>
...
    <style type="text/css">
        div.sortable { width: 100px; background-color: lightgrey; font-size: large;
            margin: 4px; text-align: center; border: medium solid black; padding: 10px;}
        #flowerContainer {position: absolute; left:50px}
        div.flower {background-color: lightgreen}
        .emptySpace {border: medium dotted red; height: 36px; margin: 4px}
    </style>
    <script type="text/javascript">
$(function() {
	
    $('#flowerContainer').sortable({
        placeholder: 'emptySpace'
    });
	
});
    </script>
</head>
<body>
    <div id="flowerContainer" class="sortContainer">
        <div id="flower_1" class="sortable flower">Астра</div>
        <div id="flower_2" class="sortable flower">Пион</div>
        <div id="flower_3" class="sortable flower">Лилия</div>
        <div id="flower_4" class="sortable flower">Орхидея</div>        
    </div> 
</body>
</html>

Запустить пример

В этом примере я определил класс emptySpace, задающий высоту и размер полей, а также пунктирную границу красного цвета для элементов, к которым он применяется. Этот класс задается в качестве значения опции placeholder, и, как показано на рисунке, когда элемент перетаскивается, оставленному им пустому пространству присваивается класс emptySpace:

Использование параметра placeholder

Методы взаимодействия Sortable

Для взаимодействия Sortable определены все стандартные методы jQuery UI плюс несколько дополнительных, являющихся специфическими для работы с сортируемыми элементами. Эти методы перечислены в таблице ниже:

Методы взаимодействия Sortable
Метод Описание
sortable("toArray") Возвращает массив, содержащий упорядоченный список значений атрибута id (см. приведенный ранее пример использования этого метода)
sortable("refresh") Обновляет состояние кеша взаимодействия Sortable
sortable("cancel") Отменяет результат применения последней операции сортировки

Отмена результата последней сортировки

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

<!DOCTYPE html>
...
    <style type="text/css">
        div.sortable { width: 100px; background-color: lightgrey; font-size: large;
            margin: 4px; text-align: center; border: medium solid black; padding: 10px;}
    </style>
    <script type="text/javascript">
$(function() {
	
    $('#error').dialog({autoOpen: false, modal: true, title: "Ошибка!"})
        
    $('#flowerContainer').sortable({
          update: function() {
              var sortedItems = $('#flowerContainer').sortable("toArray");          
              if (sortedItems[0] != "item_1") {
                  $('#error').dialog("open")
                  $('#flowerContainer').sortable("cancel")                  
              }
          }
    });
	
});
    </script>
</head>
<body>
    <div id="error">Ha первом месте должен быть "Король"</div>
    <div id="flowerContainer" class="sortContainer">
        <div id="item_1" class="sortable ">Король</div>
        <div id="item_2" class="sortable ">Дама</div>
        <div id="item_3" class="sortable ">Валет</div>
        <div id="item_4" class="sortable">10</div>      
    </div> 
</body>
</html>

Запустить пример

В этом примере метод cancel вызывается в том случае, если в новом порядке расположения элементов, созданном пользователем, элемент "Король" не находится на первом месте. Для уведомления пользователя о возникших проблемах используется виджет Dialog. Изменениям, затрагивающим порядок расположения других элементов, разрешается вступить в силу.

События взаимодействия Sortable

Взаимодействие Sortable поддерживает все события, определенные для взаимодействия Draggable, которые были описаны в предыдущей статье. Кроме того, взаимодействие Sortable поддерживает ряд собственных событий, перечень которых приведен в таблице ниже:

События взаимодействия Sortable
Событие Описание
change Происходит при изменении позиции элемента в результате сортировки, выполненной пользователем
receive Происходит при перемещении элемента в данный сортируемый элемент-контейнер из другого связанного сортируемого элемента-контейнера
remove Происходит при перемещении элемента из данного сортируемого элемента-контейнера в другой связанный сортируемый элемент-контейнер
sort Происходит при каждом перемещении мыши в процессе сортировки
update Происходит при завершении перемещения элемента пользователем при условии, что порядок элементов был изменен

При наступлении каждого из этих событий jQuery UI предоставляет дополнительную информацию посредством передаваемого обработчику события в качестве аргумента объекта ui, свойства которого перечислены в таблице ниже:

Свойства объекта ui взаимодействия Sortable
Свойство Описание
helper Возвращает вспомогательный элемент
position Возвращает информацию о текущем местоположении вспомогательного элемента в виде объекта со свойствами top и left
item Возвращает объект jQuery, содержащий перемещаемый элемент
placeholder Возвращает объект jQuery, представляющий позицию, с которой был перемещен или куда будет перемещен сортируемый элемент
sender Возвращает объект jQuery, содержащий связанный сортируемый контейнерный элемент, в котором ранее находился перемещенный элемент (в отсутствие связанных сортируемых контейнеров значение этого свойства равно null)

Пример использования объекта ui вместе с событиями sort и change приведен ниже:

Запустить пример

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>jQuery UI</title>
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
    <script src="//ajax.aspnetcdn.com/ajax/jquery.ui/1.10.3/jquery-ui.min.js"></script>
    <link rel="stylesheet" href="http://ajax.aspnetcdn.com/ajax/jquery.ui/1.10.3/themes/sunny/jquery-ui.css">
    <style type="text/css">
        div.sortable {width: 100px; background-color: lightgrey; font-size: large;
            margin: 4px; text-align: center; border: medium solid black; padding: 10px;}
        #flowerContainer {position: absolute; left:10px}
        #info {position: absolute; right: 10px; border: medium solid black; padding: 4px}
		div.flower {background-color: lightgreen}
    </style>
    <script type="text/javascript">
$(function() {
	
    $('#flowerContainer').sortable({
        sort: function(event, ui) {
            $('#itemId').text(ui.item.attr("id"))
        },
        change: function(event, ui) {
            $('#pos').text($('#flowerContainer *').index(ui.placeholder))
        }
    });
	
});
    </script>
</head>
<body>
    <div id="flowerContainer" class="sortContainer">
        <div id="flower_1" class="sortable flower">Астра</div>
        <div id="flower_2" class="sortable flower">Пион</div>
        <div id="flower_3" class="sortable flower">Лилия</div>
        <div id="flower_4" class="sortable flower">Орхидея</div>        
    </div>
    <div id="info" class="ui-widget">
        <div>ID элемента: <span id="itemId">не определено</span></div>
        <div>Позиция: <span id="pos">не определено</span></div>
    </div>
</body>
</html>

Здесь события используются для отображения информации о выполняемой операции сортировки. Функция—обработчик события sort считывает значение свойства ui.item и получает значение атрибута id перемещаемого элемента. Обработчик события change считывает значение свойства ui.placeholder и использует метод index для вычисления позиции заместителя элемента среди сортируемых элементов.

Пройди тесты