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) { // 按需写后续更新逻辑 }); } } });
逻辑说明
- 保留
draggable的helper: "clone"配置,拖拽全程移动的是临时生成的克隆辅助元素,不会影响右侧原始音符 - 移除所有手动在拖拽生命周期里写的克隆逻辑,避免和组件默认行为冲突
- 在sortable的
receive回调里做拦截:当检测到是右侧音符库的元素拖入时,先克隆一份符合左侧列表要求的新元素插入,再把组件自动移过来的源元素删掉,这样右侧原始元素不会有任何改动,左侧只会新增克隆后的音符项。
内容的提问来源于stack exchange,提问作者ThurstonLevi
相关产品推荐
相关产品推荐

