You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

jQuery draggable connectToSortable 拖拽后保留原列表元素方法

MIDI工具跨列表拖拽克隆实现方案

场景说明

开发简易MIDI工具时,页面包含两个列表:

  • 左侧为.playList类的可排序列表,用于放置待使用的音符,支持接收从右侧拖拽过来的音符项
  • 右侧为可选音符库列表,存放所有可选用的音符

问题描述

拖拽右侧音符到左侧列表后,右侧原始音符项会被移除,无法重复添加同一个音符。此前尝试过两种修复方案均未生效:

  • 在draggable的stop事件中克隆元素,结果左侧出现2个重复音符,右侧原元素仍被移除
  • 在start事件中克隆元素,拖拽放置后原元素依旧被移除
    疑问:多数相关示例基于droppable实现,当前使用的是sortable组件,二者是否能实现相同的拖拽效果?

原有问题代码

$(".alloc").draggable(
    {
        connectToSortable: ".playList",
        helper: "clone",
        snap: true,
        refreshPositions: true,
        stack: ".notes",
        handle: ".note-head",
        start: function (_event, ui) {
            let li = ui.helper;
            li.clone().insertAfter(li);
        },
        stop: function (_event, ui) {
            let li = ui.helper;
            let ol = li.parent();
            li.removeClass("bank");
            li.addClass("note");
            let id = li.attr( "id");
            let value = li.attr( "data-value");
            let data = {
                _token: $('#token').val(),
                id: id,
                value: value
            };
            $.post( "/note/add", data )
                .done( function( result ) {
                    // Update if needed...
                });

        }
    }
);

$(".playList").sortable(
    {
        connectWith: ".notes",
        placeholder: "ui-state-highlight h-full",
        distance: 4,
        helper: "clone",
        receive: function (_event, ui) {
            ui.item.remove();
        }
    }
);

预期效果

拖拽放置后右侧原始音符列表保持不变,仅将元素克隆后添加到左侧可排序列表中。

实现方案

组件能力说明

sortable本身支持跨列表拖拽接收,完全可以实现和droppable一致的拖拽效果,不需要额外引入droppable逻辑。

问题根源

  • 手动在start/stop事件中添加的克隆逻辑,和jQuery UI自带的拖拽移动逻辑冲突,是导致重复项、原元素丢失的核心原因
  • connectToSortable的默认行为是将拖拽源元素移动到目标sortable列表,所以即使设置了helper: "clone",放置后源元素还是会被移走
  • 原有receive事件里直接写ui.item.remove()会把移过来的源元素直接删掉,没有做克隆替换

修正后代码

$(".alloc").draggable({
    connectToSortable: ".playList",
    helper: "clone", // 拖拽时使用克隆辅助元素,不触碰右侧原元素
    snap: true,
    refreshPositions: true,
    stack: ".notes",
    handle: ".note-head"
    // 移除start事件中多余的手动克隆逻辑
});

$(".playList").sortable({
    connectWith: ".notes",
    placeholder: "ui-state-highlight h-full",
    distance: 4,
    helper: "clone",
    receive: function (_event, ui) {
        // 只处理从右侧音符库拖入的元素
        if (ui.item.hasClass("alloc")) {
            // 克隆拖入的项,修改为左侧列表需要的样式类
            const newNote = ui.item.clone().removeClass("bank alloc").addClass("note");
            // 将处理好的克隆项插入到当前位置
            ui.item.after(newNote);
            // 移除sortable自动移过来的源元素,右侧原元素会自动保留在原位置
            ui.item.remove();
            
            // 执行新增音符的接口请求
            const id = newNote.attr("id");
            const value = newNote.attr("data-value");
            $.post("/note/add", {
                _token: $('#token').val(),
                id: id,
                value: value
            }).done(function(result) {
                // 按需写后续更新逻辑
            });
        }
    }
});

逻辑说明

  1. 保留draggable的helper: "clone"配置,拖拽全程移动的是临时生成的克隆辅助元素,不会影响右侧原始音符
  2. 移除所有手动在拖拽生命周期里写的克隆逻辑,避免和组件默认行为冲突
  3. 在sortable的receive回调里做拦截:当检测到是右侧音符库的元素拖入时,先克隆一份符合左侧列表要求的新元素插入,再把组件自动移过来的源元素删掉,这样右侧原始元素不会有任何改动,左侧只会新增克隆后的音符项。

内容的提问来源于stack exchange,提问作者ThurstonLevi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 03:57:20