jQuery Sortable - выберите и перетащите несколько элементов списка

У меня есть дизайн, где у меня есть список «доступных ящиков», пользователи берут ящики, перетаскивая их из списка «доступные ящики» в свой список «Мои ящики».

Пользователи чаще всего принимают несколько блоков за раз (максимум 20), после того, как они закончили с полями, они перетаскивают их обратно в список «доступных ящиков», чтобы вернуть их.

Сортировка jQuery позволяет перетаскивать по одному блоку за раз, что с точки зрения пользователя нежелательно. Я не смог придумать простое решение проблемы.

Возможно, мне придется придумать совершенно другой метод пользовательского интерфейса, но сначала у кого-нибудь есть какие-либо предложения о том, как это можно сделать?

Спасибо!

 Aaron Blenkush08 мар. 2013 г., 20:34
Смотрите мой ответ для рабочего решения, Принятый ответ и ответ @Shanimal - хорошие старты, но у них были некоторые ошибки, которые нужно было устранить.

Ответы на вопрос(5)

Решение Вопроса

У меня не работает это с помощью sortable, но я использовал draggable & droppable. Я не знаю, охватил ли я всю необходимую вам функциональность, но это должно быть хорошим началом (демо здесь):

HTML

<div class="demo">
    <p>Available Boxes (click to select multiple boxes)</p>    
    <ul id="draggable">
        <li>Box #1</li>
        <li>Box #2</li>
        <li>Box #3</li>
        <li>Box #4</li>
    </ul>

    <p>My Boxes</p>
    <ul id="droppable">
    </ul>

</div>

скрипт

$(document).ready(function(){

    var selectedClass = 'ui-state-highlight',
        clickDelay = 600,     // click time (milliseconds)
        lastClick, diffClick; // timestamps

    $("#draggable li")
        // Script to deferentiate a click from a mousedown for drag event
        .bind('mousedown mouseup', function(e){
            if (e.type=="mousedown") {
                lastClick = e.timeStamp; // get mousedown time
            } else {
                diffClick = e.timeStamp - lastClick;
                if ( diffClick < clickDelay ) {
                    // add selected class to group draggable objects
                    $(this).toggleClass(selectedClass);
                }
            }
        })
        .draggable({
            revertDuration: 10, // grouped items animate separately, so leave this number low
            containment: '.demo',
            start: function(e, ui) {
                ui.helper.addClass(selectedClass);
            },
            stop: function(e, ui) {
                // reset group positions
                $('.' + selectedClass).css({ top:0, left:0 });
            },
            drag: function(e, ui) {
                // set selected group position to main dragged object
                // this works because the position is relative to the starting position
                $('.' + selectedClass).css({
                    top : ui.position.top,
                    left: ui.position.left
                });
            }
        });

    $("#droppable, #draggable")
        .sortable()
        .droppable({
            drop: function(e, ui) {
                $('.' + selectedClass)
                 .appendTo($(this))
                 .add(ui.draggable) // ui.draggable is appended by the script, so add it after
                 .removeClass(selectedClass)
                 .css({ top:0, left:0 });
            }
        });

});
 Alp02 июл. 2012 г., 18:30
Это немного случайно, но перетаскивание элемента без выбора не выполняет перетаскивание. Выбор двух элементов и их перетаскивание приводит к перетаскиванию только одного элемента на цель. В основном, некоторые элементы остаются в исходной области, а не перетаскиваются на цель. Chromium 18, Linux 64 Bit.

Решение Аарона Бленкуша имеет серьезную ошибку: удаление и добавление элементов в сортируемую структуру разрывов списка; Обновление может помочь, но если другие функции обрабатывают листинг, для их обновления необходим триггер для всех, и все это становится слишком сложным.

Проанализировав некоторые решения в stackoverflow, я подытожил мое в следующем:

Не используйте помощник - используйте функцию запуска, потому что он уже имеет ui.item, который является помощником по умолчанию.

    start: function(event, ui){
        // only essential functionality below

        // get your own dragged items, which do not include ui.item;
        // the example shows my custom select which selects the elements
        // with ".selected" class
        var dragged = ui.item.siblings(arr["nested_item"]).children('.tRow.tSelected').parent(arr["nested_item"]);

        // clone the dragged items
        var dragged_cloned = dragged.clone();

        // add special class for easier pick-up at update part
        dragged_cloned.each(function(){$(this).addClass('drag_clone');});

        // record dragged items as data to the ui.item
        ui.item.data('dragged', dragged);

        // hide dragged from the main list
        dragged.hide();

        // attached cloned items to the ui.item - which is also ui.helper
        dragged_cloned.appendTo(ui.item);
        },

На части обновления:

update: function(event, ui){
    // only essential functionality below

    // attach dragged items after the ui.item and show them
    ui.item.after(ui.item.data("dragged").show());

    // remove cloned items
    ui.item.children(".drag_clone").remove();
    },

Для функции Stop может потребоваться некоторая копия функциональности обновления, но, скорее всего, она отделена от обновления, поскольку, если нет изменений, не отправляйте ничего на сервер.

Для добавления: сохранение порядка перетаскиваемых объектов.

JSFiddle:http://jsfiddle.net/hQnWG/

<style>
    ul {border:1px solid Black;width:200px;height:200px;display:inline-block;vertical-align:top}
    li {background-color:Azure;border-bottom:1px dotted Gray}   
    li.selected {background-color:GoldenRod}
</style>
<h1>Click items to select them</h1>
<ul>
    <li>One</li>
    <li>Two<li>
    <li>Three</li>
</ul><ul>
    <li>Four</li>
    <li>Five<li>
    <li>Six</li>
</ul>
<script>
    $("li").click(function(){
        $(this).toggleClass("selected");
    })
    $("ul").sortable({
        connectWith: "ul",
        start:function(e,info){
            // info.item.siblings(".selected").appendTo(info.item);
            info.item.siblings(".selected").not(".ui-sortable-placeholder").appendTo(info.item);

        },
        stop:function(e,info){
            info.item.after(info.item.find("li"))
        }
    })
</script>
 Aaron Blenkush08 мар. 2013 г., 23:55
@Shanimal: Я полагаю, что проблема была в элементах родного брата, добавленных внутрь перетаскиваемого элемента. Я исправил проблему с помощью jQuery.data () для хранения братьев и сестер, а затем использовал его после удаления, чтобы восстановить их в списке получения. Перетащите «помощник» новый пробел<li/> с добавленными перемещенными элементами.jsfiddle.net/hQnWG/480
 Shanimal08 мар. 2013 г., 21:03
Какой браузер вы используете? Я не могу воспроизвести проблему.codepen.io/anon/pen/lcof
 Shanimal08 мар. 2013 г., 20:03
@supertrue ты пробовал это?
 Roopak Venkatakrishnan08 мар. 2013 г., 21:46
@Shanimal Chrome v25.0.1364.152 m Проблема возникает только в том случае, если вы выберете более одного и поместите его в тот же список, из которого вы их выбрали, а не в то же место, где вы их выбрали (или иногда между обоими списками). выберите 2 и начните перетаскивать
 Ryan24 авг. 2012 г., 18:01
Я понял, что jquery-sortable добавляет дополнительный элемент li, когда вы перемещаетесь в качестве заполнителя того места, где находился элемент. Это собирается, когда вы вызываете info.item.siblings (". Selected"). Возможно, вам также придется добавить .not (". Ui-sortable-placeholder"), чтобы исключить этот элемент списка. Это, кажется, исправить ошибку для меня.
 Roopak Venkatakrishnan08 мар. 2013 г., 19:56
@ Райан Я знаю, что я открываю старый вопрос ... Но я пытался избавиться от ошибки, описанной выше ... и даже не похоже, что она мне не помогает ... возможно, новая версия jquery ... могла Вы говорите мне, что именно вы сделали?
 supertrue13 сент. 2012 г., 05:45
Этот код отлично работал для меня, как есть. Я просто добавил строку вstop функция, чтобы заставить его удалить.selected класс из всех выбранных предметов.
 Shanimal09 мар. 2013 г., 00:19
@ Аарон, хорошо выглядит
 Ryan24 авг. 2012 г., 17:44
Спасибо! Это супер просто и именно то, что я искал. Одна вещь, которую я заметил в вашем примере с JSFiddle, заключалась в том, что была странная ошибка, при которой, если вы выберете элемент, а затем уроните его вокруг центра, он исчезнет.
 Shanimal08 мар. 2013 г., 22:35
Я думаю, что он пытается добавить его к выбранной себе. Дай мне посмотреть, смогу ли я это исправить

Для этого есть плагин jQuery UI:https://github.com/shvetsgroup/jquery.multisortable

jsFiddle:http://jsfiddle.net/neochief/KWeMM/

$('ul.sortable').multisortable();
 seebiscuit06 окт. 2014 г., 20:08
Поддерживает ли этоconnectWith? Как насчетdraggable Предметы?
Рабочий раствор

ТЛ; др: Обратитесь к этой скрипке для рабочего ответа.

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

Тем не мение...

Принятый ответ глючит, и@ Shanimal's answer закрыто, но не совсем полный. Я взял код @ Shanimal и построил его.

Я исправил:

ошибка исчезающего элемента списка, о которой упоминал @Ryan,исправлены синтаксические ошибки в HTML (отсутствующие закрывающие теги и вложенные<li/>с).

Я добавил:

правильныйCtrl + клик (или жеCmd + клик если на Mac) поддержка выбора нескольких элементов. Нажатие кнопкибез Ctrl нажатие клавиши приведет к тому, что этот элемент будет выбран, а другие элементы в этом же списке будутотменен, Это то же поведение, что и при нажатииJQuery UISelectable() виджетразница в том, чтоSelectable() имеет шатер на музедраге.

скрипка

HTML:

<ul>
    <li>One</li>
    <li>Two</li>
    <li>Three</li>
</ul>
<ul>
    <li>Four</li>
    <li>Five</li>
    <li>Six</li>
</ul>

JavaScript (с помощью jQuery и jQuery UI):

$("ul").on('click', 'li', function (e) {
    if (e.ctrlKey || e.metaKey) {
        $(this).toggleClass("selected");
    } else {
        $(this).addClass("selected").siblings().removeClass('selected');
    }
}).sortable({
    connectWith: "ul",
    delay: 150, //Needed to prevent accidental drag when trying to select
    revert: 0,
    helper: function (e, item) {
        var helper = $('<li/>');
        if (!item.hasClass('selected')) {
            item.addClass('selected').siblings().removeClass('selected');
        }
        var elements = item.parent().children('.selected').clone();
        item.data('multidrag', elements).siblings('.selected').remove();
        return helper.append(elements);
    },
    stop: function (e, info) {
        info.item.after(info.item.data('multidrag')).remove();
    }

});

НОТА:

С тех пор, как я это опубликовал, я реализовал нечто схожее - подключение перетаскиваемых элементов списка к сортируемому с возможностью множественного выбора. Он настроен почти так же, так как виджеты пользовательского интерфейса jQuery очень похожи. Один совет от UI - убедиться, что у вас естьdelay набор параметров для перетаскиваемых или выбираемых элементов, так что вы можете щелкнуть, чтобы выбрать несколько элементов, не инициируя перетаскивание. Затем вы создаете помощник, которыйвыглядит как все выбранные элементы, собранные вместе (создать новый элемент, клонировать выбранные элементы и добавить их), ноудостовериться оставить оригинальный элемент нетронутым (иначе это портит функциональность - я не могу точно сказать, почему, но это включает в себя множество разочаровывающих исключений DOM).

Я также добавилShift + клик функциональность, так что он работает больше как родные настольные приложения. Возможно, мне придется начать блог, чтобы я мог объяснить это более подробно :-)

 Aaron Blenkush08 мар. 2013 г., 23:53
@RoopakVenkatakrishnan: После обновления это исправлено.
 Reuben Ward05 авг. 2016 г., 03:26
Определенно лучшее решение для пользователей, у которых есть элементы типа <select>, вложенные в их элементы списка. Отлично сработало для меня (и я пробовал другие библиотеки, которые были очень расстраивающими и имели проблемы) Хорошая работа, чувак, ценю это
 Roopak Venkatakrishnan11 мар. 2013 г., 20:54
да, это, кажется, работает блестяще!
 T J09 июн. 2014 г., 12:05
Одна из проблем заключается в том, что если я просто перетащу непоследовательные элементы и оставлю их в одном контейнере, при возврате порядок элементов будет изменен ...
 slaur406 сент. 2013 г., 13:24
Это решение приводит меня к нескольким ошибкам (потому что некоторые элементы удалены из списка, я думаю). Я обнаружил, что было более безопасно скрывать элементы и удалять их в функции остановки
 Mrigesh Raj Shrestha13 июн. 2013 г., 10:10
@aaron Потрясающие вещи; но у меня есть функция, которая обрабатывает некоторую синхронизацию в другой области DOM после завершения перетаскивания, то есть через событие остановки. И в то время мне нужны объекты недавно перетаскиваемого предмета. Я не смог получить этот перетаскиваемый стек. Можете ли вы помочь мне в этом?
 Aaron Blenkush30 окт. 2013 г., 19:47
@django Это выходит за рамки этого вопроса. Вы должны исследовать это и открыть новый вопрос, если вы не можете понять это. Обязательно укажите, что вы пробовали.forum.jquery.com/topic/how-to-cancel-drag-while-dragging
 T J10 июн. 2014 г., 07:43
@AaronBlenkush, так же как и другие ответы .. вы говорили, что принятый ответ ошибочен, другие ответы не совсем полны и т. Д., Поэтому я указал - и этот тоже.
 Aaron Blenkush17 июн. 2013 г., 18:29
@Mrigesh, я посмотрел на Fiddle для этого ответа и вспомнил, как я получаю доступ к перетаскиваемым элементам вstop Перезвоните. Вместо того чтобы объяснять это здесь, я сделал обновление для скрипки, в котором есть комментарии, чтобы вы могли видеть, как это сделать. (Подсказка: он используетjQuery.data() способ прикрепить элементы к перетаскиваемомуitem этот Sortable проходит между его обратными вызовами. Вы должны прикрепить выбранные элементы кitem когда сортировка начинается. Я сделал это наhelper Перезвоните.)jsfiddle.net/hQnWG/614
 Aaron Blenkush09 июн. 2014 г., 16:52
@TJ: Это переполнение стека, а не репо с открытым исходным кодом. Ответ должен быть отправной точкой, а не окончательным решением ;-)
 Mrigesh Raj Shrestha17 июн. 2013 г., 06:35
@AaronBlenkush, этот сегмент приложений, в котором существует этот сценарий, - настоящая странность. Поиграть это действительно тяжелые вещи. Abt api, я проверил все, но не смог получить коллекцию нескольких элементов, которые только что перетащили.
 Salman12 нояб. 2013 г., 21:49
@AaronBlenkush ваше решение работает отлично, но я хочу, чтобы одна модификация содержала элементы-клоны, а не удаляла их из списка источников. Можете ли вы помочь мне, как я могу этого достичь? Ваш код удалить элементы из списка источников.
 django30 окт. 2013 г., 18:58
Если мы выбрали несколько элементов, а затем решили не отбрасывать их (причина может быть в неправильном выборе), тогда можно будет нажать ecaspe во время перетаскивания, чтобы вернуть элементы обратно в исходное положение. есть идеи как это сделать?
 Aaron Blenkush24 сент. 2013 г., 23:20
@ slaur4 Это имеет смысл. Все равно дым и зеркала; что бы ни достигло конечного результата (надежного) взаимодействия с пользователем! Если вы чувствуете такую ​​склонность, опубликуйте свое улучшение как отдельный ответ для потомков :-)
 Aaron Blenkush16 мая 2013 г., 20:12
@ Гаутам: я не уверен, что вы имеете в виду; Вы можете нажать Ctrl + щелчок, чтобы выбрать все элементы списка одновременно. С тех пор я использовал это в приложении, и я добавил Ctrl + A для выбора всех и Shift + щелчок, чтобы выбрать диапазон элементов. Shift + щелчок немного сложнее, но Ctrl + A работает, если вы знаете, какой список имеет «фокус». Просто слушайте Ctrl + A, затем добавьтеselected класс для каждого элемента.
 Mrigesh Raj Shrestha18 июн. 2013 г., 13:48
@AaronBlenkush спасибо за такие усилия. Снимаю шляпу, чтобы это произошло. Я попробую это решение, которое вы дали, и дам вам знать :)
 Gautam16 мая 2013 г., 18:55
Привет, Аарон, Ваше решение работает отлично. Но в некоторых случаях мне нужно только разрешить пользователю выбирать весь список (все <li>) и помещать его в другие <ul>.
 Aaron Blenkush13 июн. 2013 г., 19:28
@Mrigesh: Вы проверили документацию по API для jQuery UI дляСортируемый а такжеПеретаскиваемый? В частности, посмотрите наsortableсобытия обратного вызова. Я бы порекомендовал создатьскрипка и исследуя значенияevent а такжеui объекты путем печати на консоль (console.dir(event);, например). Я редко запоминаю эти вещи - всякий раз, когда мне нужно «запомнить», я просто беру пару минут на то, что я описал выше, и точно вижу, что происходит, если документы API не сообщают мне сразу.
 Roopak Venkatakrishnan08 мар. 2013 г., 20:35
Это лучше ... но ошибка, о которой упоминал Райан, все еще воспроизводима ... я вытащил 2 элемента ... потом вернулся, потому что решил, что не хочу их перемещать, и если я не положу их в то же положение, как они были Возникает исключение DOM 3
 seebiscuit06 окт. 2014 г., 21:47
Отличная работа, Аарон. Мне было интересно, какой может быть чистая точка входа, без необходимости слишком много гадить с внутренностями, и, кажется, вы ее нашли. Я реализую ваш код для подключенногоперетаскиваемый, Как вы могли столкнуться,connectToSortable принимаетdraggable helper и передает егоsortable, Следовательноelements которые хранятся вhelper изdraggable затем теряются к тому времени, когда мы добираемся доsortable drop функция. Как ты справился с этим?

Ваш ответ на вопрос