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

如何让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>

关键修改说明

  1. 砍掉批量创建容器的操作:删掉了drag事件里的prepareEmptyDropZones()调用,彻底避免拖拽一开始就给所有元素生成容器的行为。
  2. 按需动态生成容器:在over事件中,只针对当前悬停的.js-structure-item检查并创建子容器,只在用户可能放置的位置生成DOM元素。
  3. 实时同步Dragula容器列表:每次创建新容器或清理空容器后,调用resetContainers()更新Dragula可识别的容器,确保新容器能正常接收拖拽元素。
  4. 优化视觉体验:只有空的.js-structure-parent才显示虚线边框,默认状态下不会有冗余的边框干扰页面布局。

这样修改后,哪怕有几百个元素,拖拽时也只会在用户悬停的位置生成容器,不会导致页面DOM膨胀,性能和体验都会大幅提升~

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 08:08:20