首页 > 解决方案 > 类似于 Trello 的 Javascript 拖放悬停临时 Div 占位符?

问题描述

我有一个基本的拖放式 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 类似的效果。这会在拖动悬停效果上创建与类<div>相同级别的另一个元素drag-card,并相应地重新定位自身。

在此处输入图像描述

我知道我必须使用事件侦听器,但仅此而已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项目来源的元素

在此处输入图像描述

标签: javascripthtmlcssdrag-and-dropdraggable

解决方案


使用 jquery 和 jquery UI,我不久前做了类似的事情。我没有创建“制作新卡”功能,而是从“启动板”开始并创建了两个可放置的区域,可以将卡附加到这些区域并在它们之间切换 - 类似于您所拥有的。我记得使用“相交”是让它按我想要的方式工作的一个转折点——能够在列表中上下移动元素(因此它们不一定会移回它们的起源位置)。也许它可以成为您的起点?这是小提琴(jquery是旧的..建议更新到新版本)

希望这可以帮助。

编辑:我对您的代码进行了一些小调整,以添加轮廓并在移动时更改光标。根据对另一个问题的评论,添加边框是创建视觉“轮廓”效果的最有效方法。有一种更长的方法可以创建“可排序”效果,该效果在我发现的这个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>


推荐阅读