jQuery Sortable - 选择并拖动多个列表项

Posted

技术标签:

【中文标题】jQuery Sortable - 选择并拖动多个列表项【英文标题】:jQuery Sortable - Select and Drag Multiple List Items 【发布时间】:2011-04-16 01:00:58 【问题描述】:

我有一个设计,其中有一个“可用盒子”列表,用户通过将盒子从“可用盒子”列表拖动到他们的“我的盒子”列表来获取盒子。

用户经常一次拿走多个盒子(最多 20 个),一旦他们用完盒子,他们会将它们拖回“可用盒子”列表以返回它们。

jQuery sortable 允许我一次拖动一个框,从用户的角度来看这是不可取的。我一直无法想出一个简单的解决方案。

我可能不得不想出一个完全不同的 UI 方法,但首先有人对如何实现这一点有任何建议吗?

谢谢!

【问题讨论】:

See my answer for a working solution。接受的答案和@Shanimal 的答案是好的开始,但他们有一些需要解决的错误。 【参考方案1】:

工作解决方案

tl;dr:Refer to this Fiddle for a working answer。


我到处寻找解决将多个选定项目从可排序项拖动到连接的可排序项问题的解决方案,这些答案是我能找到的最佳答案。

不过……

接受的答案有问题,@Shanimal's answer 很接近,但还不够完整。我采用了@Shaanimal 的代码并在其上进行构建。

我修好了:

disappearing list item bug that @Ryan mentioned, 清理了 html 中的语法错误(缺少结束标记和嵌套的 <li/>s)。

我补充说:

适当的 Ctrl + 单击(或 Cmd + 单击,如果在 Mac 上)支持选择多个项目。点击而不按住Ctrl键将导致该项目被选中,而同一列表中的其他项目被取消选择。这与jQuery UI Selectable() widget 的点击行为相同,不同之处在于Selectable() 在鼠标拖动时有一个选取框。

Fiddle

HTML:

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

JavaScript(带有 jQ​​uery 和 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 小部件非常相似。一个 UI 提示是确保您为可拖动项或可选择项设置了 delay 参数,这样您就可以单击以选择多个项目而无需启动拖动。然后构建一个 看起来 像所有选定元素放在一起的帮助器(创建一个新元素,克隆选定的项目,然后附加它们),但 确保 离开原始项目完好无损(否则它会破坏功能 - 我无法确切说明原因,但它涉及许多令人沮丧的 DOM 异常)。

我还添加了 Shift + Click 功能,使其功能更像原生桌面应用程序。我可能需要创建一个博客,以便更详细地阐述这一点:-)

【讨论】:

更好...但是 Ryan 提到的错误仍然是可重现的...我拖了 2 个元素...然后又回来了,因为我决定我不想移动它们,除非我放置它们与发生 DOM 3 异常的位置相同 @RoopakVenkatakrishnan:经过改造,现在已修复。 @Mrigesh,我查看了 Fiddle 以获得这个答案,并记得我如何访问 stop 回调中的拖动元素。我没有在这里解释它,而是对具有 cmets 的 Fiddle 进行了更新,以便您了解如何操作。 (提示:它使用jQuery.data() 方法将元素附加到Sortable 在其回调之间传递的拖动的item。在启动排序时,您必须将所选项目附加到item。我在@ 上做了987654339@回调。)jsfiddle.net/hQnWG/614 @AaronBlenkush 感谢您的努力。为实现它而脱帽致敬。我会尝试你提供的这个解决方案并让你知道:) 对于将 【参考方案2】:

我没有使用 sortable 进行这项工作,但我确实使用了 draggable 和 droppable。我不知道我是否涵盖了您需要的所有功能,但这应该是一个好的开始 (demo here):

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 );
            
        );

);

【讨论】:

有点随机,但是拖动一个项目而不选择它不会执行拖动。选择两个项目并拖动它们只会将一个元素拖动到目标。大多数情况下,一些元素保留在源区域中,而不是被拖到目标区域。铬 18,Linux 64 位。【参考方案3】:

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>

【讨论】:

谢谢!这非常简单,正是我想要的。我在您的 JSFiddle 示例中注意到的一件事是,有一个奇怪的错误,如果您选择一个项目然后将其放在中心周围,它会消失。 我发现 jquery-sortable 在您移动时会添加一个额外的 li 元素作为项目所在位置的占位符。当您调用 info.item.siblings(".selected") 时,它会被收集。您可能还必须添加 .not(".ui-sortable-placeholder") 以排除该列表项。这似乎为我修复了这个错误。 这段代码对我来说非常有用。我刚刚在stop 函数中添加了一行,使其从所有选定项目中删除.selected 类。 @Shanimal Chrome v25.0.1364.152 m 仅当您选择多个并将其放在您从中选择它们的同一个列表中而不是在您从中选择它们的完全相同的位置时才会出现问题(或者有时在两个列表之间)。选择2并立即开始拖放 @Shaanimal:我认为问题出在拖动元素内附加的兄弟元素。我已经通过使用 jQuery.data() 来存储兄弟姐妹解决了这个问题,然后在 drop 之后使用它在接收列表中重建它们。拖动“助手”是一个新的空白&lt;li/&gt;,并附加了移动的元素。 jsfiddle.net/hQnWG/480【参考方案4】:

为此有一个 jQuery UI 插件:https://github.com/shvetsgroup/jquery.multisortable

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

$('ul.sortable').multisortable();

【讨论】:

是否支持connectWithdraggable items 怎么样?【参考方案5】:

Aaron Blenkush 的解决方案有一个重大缺陷:在可排序列表中删除和添加项目会破坏结构; refresh 可以提供帮助,但如果其他函数处理列表,则需要触发所有这些函数来刷新,这一切都变得过于复杂。

在***分析了一些解决方案后,我总结了以下几点:

不要使用helper - 使用start函数,因为它已经有ui.item,默认是helper。

    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();
        ,
    

停止功能可能需要更新功能的一些副本,但可能与更新分开,因为如果没有更改 - 请勿向服务器提交任何内容。

添加:保留拖动项目的顺序。

【讨论】:

以上是关于jQuery Sortable - 选择并拖动多个列表项的主要内容,如果未能解决你的问题,请参考以下文章

jquery sortable使用户能够在`n`秒后拖动元素

jquery-ui-sortable 的拖动事件

可拖动的 droppable 和 sortable

jQuery UI Sortable 将拖动对象的类添加到占位符以确定大小

jquery sortable的拖动方法示例详解

jquery-ui sortable - 拖动时隐藏原始列表隐藏被拖动项