如何让Dragula仅在悬停元素时显示拖拽放置位置?
解决Dragula拖拽时批量生成容器导致的页面膨胀问题
我完全懂你现在的痛点——当元素数量破百时,拖拽刚开始就给所有元素批量生成空容器,直接把页面DOM搞爆炸,卡顿到没法用。咱们换个思路就行:只在拖拽元素悬停到可放置的目标元素上时,才动态创建对应的容器,这样就不会一次性生成一大堆冗余DOM了。
下面是修改后的完整实现方案:
修改后的JavaScript代码
var drake; function setupDragula() { var containers = Array.prototype.slice.call(document.querySelectorAll(".js-structure-parent")); var opts = { allowNestedContainers: true, accepts: function(el, target, source, sibling) { // 防止拖拽的容器被放入自身内部 return !contains(el, target); } }; drake = dragula(containers, opts) .on('drag', function(el) { el.classList.remove('ex-moved'); // 移除拖拽开始时批量创建容器的操作,避免一次性生成大量DOM }) .on('drop', function(el, container, source) { el.classList.add('ex-moved'); removeEmptyDropZones(); }) .on('cancel', function(el, container, source) { removeEmptyDropZones(); }) .on('over', function(el, container) { container.classList.add('editing'); el.classList.add('el-over'); // 核心逻辑:仅在悬停到目标元素时,动态创建容器 const parentItem = container.closest('.js-structure-item'); if (parentItem && !parentItem.querySelector('.js-structure-parent')) { const emptyParent = document.createElement('div'); emptyParent.className = "js-structure-parent"; parentItem.appendChild(emptyParent); // 更新Dragula的容器列表,确保新创建的容器能被识别为可放置区域 resetContainers(); } }) .on('out', function(el, container) { container.classList.remove('editing'); el.classList.remove('el-over'); }); } function contains(a, b) { return a.contains ? a != b && a.contains(b) : !!(a.compareDocumentPosition(b) & 16); } function removeEmptyDropZones() { var dropZones = querySelectorAllArray(".js-structure-parent"); dropZones.forEach(function(dropZone) { if (dropZone.children.length == 0) { dropZone.remove(); } }); // 清理空容器后重新更新Dragula的容器列表 resetContainers(); } function resetContainers() { drake.containers = Array.prototype.slice.call(document.querySelectorAll(".js-structure-parent")); } function querySelectorAllArray(selector) { return Array.prototype.slice.call(document.querySelectorAll(selector)) } document.addEventListener("DOMContentLoaded", function(event) { setupDragula(); });
修改后的CSS代码
.js-structure-item { cursor: move; } .js-structure-item .container { margin-bottom: 10px; } /*parent*/ .js-structure-parent { padding: 0px 0px 0px 30px; } /* 仅空容器显示虚线边框,避免默认状态下的冗余视觉元素 */ .js-structure-parent:empty { min-height: 20px; border: 1px dashed #ccc; } .el-over { background-color: green; } .js-structure-item.empty { color: #666; }
HTML部分(保持原有结构不变)
<script src="https://cdnjs.cloudflare.com/ajax/libs/dragula/3.7.2/dragula.min.js"></script> <div class="js-structure-parent"> <div class="js-structure-item"> <div class="container"> File Folder </div> <div class="js-structure-parent"> <div class="js-structure-item"> <div class="container"> File 1 </div> </div> <div class="js-structure-item"> <div class="container"> File 2 </div> </div> <div class="js-structure-item"> <div class="container"> File 3 </div> </div> <div class="js-structure-item"> <div class="container"> File 4 </div> </div> </div> </div> <div class="js-structure-item"> <div class="container"> Image Folder </div> <div class="js-structure-parent"> <div class="js-structure-item"> <div class="container"> Image file 1 </div> </div> <div class="js-structure-item"> <div class="container"> Image file 2 </div> </div> <div class="js-structure-item"> <div class="container"> Image file 3 </div> </div> <div class="js-structure-item"> <div class="container"> Image file 4 </div> </div> </div> </div> <div class="js-structure-item"> <div class="container"> Document folder </div> <div class="js-structure-parent"> <div class="js-structure-item"> <div class="container"> Document file 1 </div> </div> <div class="js-structure-item"> <div class="container"> Document file 2 </div> </div> <div class="js-structure-item"> <div class="container"> Document file 3 </div> </div> <div class="js-structure-item"> <div class="container"> Document file 4 </div> </div> </div> </div> </div>
关键修改说明
- 砍掉批量创建容器的操作:删掉了
drag事件里的prepareEmptyDropZones()调用,彻底避免拖拽一开始就给所有元素生成容器的行为。 - 按需动态生成容器:在
over事件中,只针对当前悬停的.js-structure-item检查并创建子容器,只在用户可能放置的位置生成DOM元素。 - 实时同步Dragula容器列表:每次创建新容器或清理空容器后,调用
resetContainers()更新Dragula可识别的容器,确保新容器能正常接收拖拽元素。 - 优化视觉体验:只有空的
.js-structure-parent才显示虚线边框,默认状态下不会有冗余的边框干扰页面布局。
这样修改后,哪怕有几百个元素,拖拽时也只会在用户悬停的位置生成容器,不会导致页面DOM膨胀,性能和体验都会大幅提升~
内容的提问来源于stack exchange,提问作者jaread83
相关产品推荐
相关产品推荐

