Javascript 临时拖放悬停 Div 类似于 Trello 的占位符?

Javascript Drag and Drop Hover Temporary Div Placeholder similar to Trello?

我有一个类似 trello 的基本拖放式看板。您可以在不同的灰色框之间拖动任务。它使用 HTML 拖放 API 在此处找到 https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API

var dropTarget = document.querySelector(".drop-target");
var draggables = document.querySelectorAll(".drag-task");

// Tells the other side what data is being passed (e.g. the ID is targeted)
draggables.forEach(item => {
  item.addEventListener("dragstart", function(ev){
    ev.dataTransfer.setData("srcId", ev.target.id);
  });
})
// The end destination, prevent browsers default drag and drop (disabling breaks feature)
// because it's disabled by browsers by default
dropTarget.addEventListener('dragover', function(ev) {
  ev.preventDefault();
});
// End destination where item is dropped into
dropTarget.addEventListener('drop', function(ev) {
  ev.preventDefault();
  let target = ev.target;
  let droppable  = target.classList.contains('drag-box');
  let srcId = ev.dataTransfer.getData("srcId");

  if (droppable) {
    ev.target.appendChild(document.getElementById(srcId));
  }
});
/***********DRAGGABLE BACKGROUND ****************/
.drag-box {
  background-color: lightgray;
  float: right;
  width: 120px;
  min-height: 50px;
  padding-bottom: 30px;
  height: auto;
  margin: 30px;
}
.drag-task {
  background-color: white;
  margin: 15px;
}
.drop-active {
  border: 1px dashed red;
}
<div class="drop-target">
    <div class="drag-box">
      <div class="drag-card">
        <div draggable="true" id="task1" class="drag-task">Test Card 1</div>
      </div>
      <div class="drag-card">
        <div draggable="true" id="task2" class="drag-task">Test Card 2</div>
      </div>
      <div class="drag-card">
        <div draggable="true" id="task3" class="drag-task">Test Card 3</div>
      </div>
    </div>
    <div class="drag-box">
    </div>
    <div class="drag-box">
    </div>
  </div>

我想要实现的是类似于找到的这个 gif 的效果 here。这将创建另一个 <div> 元素,与 drag-card class 处于同一级别,具有拖动效果,并相应地重新定位。

我知道我必须使用 dragoverdragleave 事件侦听器,但就我所知。我在文件末尾添加了这段代码。我从未使用过拖动事件侦听器,所以这对我来说是新的。

var makeHoverElement= true;
dropTarget.addEventListener("dragover", function(ev){
  if(makeHoverElement){
    let newNode =document.createElement('div');
    newNode.className ='drop-active'
    ev.target.parentElement.prepend(newNode);
    makeHoverElement = false;
  }
});

dropTarget.addEventListener("dragleave", function(ev){
   // really I have no idea how to make this effect
});

到目前为止的结果并没有像我预期的那样。 Dragover 正在应用于 task 项目源自

的元素

使用jquery和jqueryUI,我刚才做了一些类似的事情。我没有创建 "make new card" 函数,我从 "launchpad" 开始并创建了两个可放置的区域,卡片可以附加到这些区域并在它们之间切换 - 类似于您拥有的。我记得使用 "intersect" 是让它按我想要的方式工作的转折点 - 能够在列表中上下移动元素(因此它们不一定会移回它们的起源位置)。也许它可以成为你的起点? 这是 fiddle(jquery 是旧的..建议更新到较新的版本)

希望这对您有所帮助。

编辑: 我对您的代码进行了一些小调整,以添加轮廓并在移动时更改光标。根据对 another question, adding a border is the most efficient way to create the visual 'outline' effect. There is a longer way to create the 'sortable' effect which is demoed in this codepen 的评论,我发现并简单解释了该函数是基于计算悬停位置的,如果拖动的元素在列表中的某个项目的中间,则会显示效果并且可以删除该项目在列表项之间。

希望这足够清楚了!

// Tells the other side what data is being passed (e.g. the ID is targeted)
var dropTarget = document.querySelector(".drop-target");
var draggables = document.querySelectorAll(".drag-task");

// Tells the other side what data is being passed (e.g. the ID is targeted)
draggables.forEach(item => {
  item.addEventListener("dragstart", function(ev) {
    ev.dataTransfer.setData("srcId", ev.target.id);
  });
})
// The end destination, prevent browsers default drag and drop (disabling breaks feature)
// because it's disabled by browsers by default
dropTarget.addEventListener('dragover', function(ev) {
  ev.preventDefault();
});
// End destination where item is dropped into
dropTarget.addEventListener('drop', function(ev) {
  ev.preventDefault();
  let target = ev.target;
  let droppable = target.classList.contains('drag-box');
  let srcId = ev.dataTransfer.getData("srcId");

  if (droppable) {
    ev.target.appendChild(document.getElementById(srcId));
  }
});
.drag-box {
  background-color: lightgray;
  float: left;
  width: 120px;
  min-height: 80px; /*lengthened the height slightly*/
  padding-bottom: 30px;
  height: auto;
  margin: 30px;
  cursor: move; /*added the 'cross' cursor*/
}
.drag-task {
  background-color: white;
  margin: 10px;
  padding: 5px; /*added padding to make tiles bigger*/
  border:1px dashed #000000; /*set an outline*/
}

.drop-active {
  border: 1px dashed red;
  cursor: pointer; /*change the pointer back to the default cursor while moving between lists*/
}
<div class="drop-target">
  <div class="drag-box">
    <div class="drag-card">
      <div draggable="true" id="task1" class="drag-task">Test Card 1</div>
    </div>
    <div class="drag-card">
      <div draggable="true" id="task2" class="drag-task">Test Card 2</div>
    </div>
    <div class="drag-card">
      <div draggable="true" id="task3" class="drag-task">Test Card 3</div>
    </div>
  </div>
  
  <!-- added tiles to the 2nd list (and deleted 3rd box)-->
  <div class="drag-box">
    <div class="drag-card">
      <div draggable="true" id="orange" class="drag-task">Orange</div>
    </div>
    <div class="drag-card">
      <div draggable="true" id="apple" class="drag-task">Apple</div>
    </div>
    <div class="drag-card">
      <div draggable="true" id="pear" class="drag-task">Pear</div>
    </div>
  </div>

$("#launchPad").height($(window).height() - 20);
var dropSpace = $(window).width() - $("#launchPad").width();
$("#dropZone").width(dropSpace - 70);
$("#dropZone").height($("#launchPad").height());

$(".card").draggable({
    appendTo: "#launchPad",
    cursor: "move",
    helper: 'clone',
    revert: "invalid",

});

$("#launchPad").droppable({
    tolerance: "intersect",
    accept: ".card",
    activeClass: "ui-state-default",
    hoverClass: "ui-state-hover",
    drop: function(event, ui) {
        $("#launchPad").append($(ui.draggable));
    }
});

$(".stackDrop").droppable({
    tolerance: "intersect",
    accept: ".card",
    activeClass: "ui-state-default",
    hoverClass: "ui-state-hover",
    drop: function(event, ui) {        
        $(this).append($(ui.draggable));
    }
});
body { 
    margin: 0;
    background-color: #ffffcc;
}
#launchPad {
    width:170px;
    float:left;
    border: 1px solid #eaeaea;
    background-color: #f5f5f5;
}
#dropZone {
    float:right;
    border: 1px solid #eaeaea;
    background-color: #ffffcc;
}
.card { 
    width: 130px; 
    padding: 5px 10px;
    margin:5px;
    border:1px solid #ccc;
    background-color: #eaeaea;
}
.stack {
  display:inline-block;
   vertical-align:top;
    width: 180px;
    border: 1px solid #ccc;
    background-color: #f5f5f5;
    margin: 20px;
}
.stackHdr {
    background-color: #eaeaea;
    border: 1px solid #fff;
    padding: 5px 
}
.stackDrop {
    min-height:100px;
    padding: 15px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.6.4/jquery.min.js"></script>
<script src="http://code.jquery.com/ui/1.9.2/jquery-ui.js"></script>

<div id="launchPad">    
    <div class="card draggable" >
        apple
    </div> 
    <div class="card draggable">
        orange
    </div> 
    <div class="card draggable">
        banana
    </div> 
    <div class="card draggable">
        car
    </div> 
    <div class="card draggable">
        bus
    </div> 
</div>

<div id="dropZone">
    <div class="stack">
        <div class="stackHdr">
            Drop here 
        </div>
        <div class="stackDrop droppable">
            
        </div>
    </div>
    
    <div class="stack">
        <div class="stackHdr">
            Or here
        </div>
        <div class="stackDrop droppable">
            
        </div>
    </div>
</div>

问题出在ev.target.parentElement.prepend(newNode); 您的 ev.target 仍然是您从 拖动它的节点的子节点 。这就是虚线边框 div 被添加到 'old' 框中的原因。我建议在 'dragover' 函数中明确找到鼠标悬停的元素并将 newNode 添加到其中。例如,您可以通过 document.querySelector(":hover" ) select 或尝试在那里处理 'mouseover' 事件。

至于 'dragleave' 效果,我建议您使用 Node.cloneNode() 方法克隆 ev.target,然后使用 Node.insertBefore() 将克隆附加到 ev.target.parentElementMDN on .insertBefore()