Javascript HTML在多个无序列表之间拖放

Javascript HTML在多个无序列表之间拖放,javascript,html,dom,Javascript,Html,Dom,我有一个相对简单的web应用程序,我正在创建一个书架,我希望用户能够在书架之间拖放书籍。当我把一本书放在一个书架上现有书籍的“右边”时,它的工作原理和我想要的完全一样。拖放的书被添加到书架上 它不起作用的地方是当我把一本书放在现有的书上时。当我这样做的时候,这本书就从现有的书架上移除了——但是它完全消失了,而不是像我所希望的那样被添加到目标书架上。我不清楚为什么会这样 请看这里: (如果相关的话,我正在使用Firefox) 相关源代码如下(原始html): 注意:这最初是通过github中的je

我有一个相对简单的web应用程序,我正在创建一个书架,我希望用户能够在书架之间拖放书籍。当我把一本书放在一个书架上现有书籍的“右边”时,它的工作原理和我想要的完全一样。拖放的书被添加到书架上

它不起作用的地方是当我把一本书放在现有的书上时。当我这样做的时候,这本书就从现有的书架上移除了——但是它完全消失了,而不是像我所希望的那样被添加到目标书架上。我不清楚为什么会这样

请看这里:

(如果相关的话,我正在使用Firefox)

相关源代码如下(原始html):

注意:这最初是通过github中的jekyll生成的,但由于这个问题不是以jekyll为中心的,所以我不包括源代码细节

关于如何实现拖放,我主要遵循以下指南:

下面是相关的javascript代码。“删除”功能旨在将相关列表项删除到新列表中

     <script>
        
        function allowDrop(ev) {
           ev.preventDefault();
        }
        
        function drag(ev) {
           ev.dataTransfer.setData("text", ev.target.id);
        }
        
        function drop(ev) {
          ev.preventDefault();
          var data = ev.dataTransfer.getData("text");
          ev.target.appendChild(document.getElementById(data).parentNode);
        }
        
     </script>

功能allowDrop(ev){
ev.preventDefault();
}
功能阻力(ev){
ev.dataTransfer.setData(“文本”,ev.target.id);
}
功能下降(ev){
ev.preventDefault();
var data=ev.dataTransfer.getData(“文本”);
ev.target.appendChild(document.getElementById(data.parentNode));
}
每个书架本身被包装在一个div中,其中包含一个无序列表(ul),列表项(li)代表每本书。以下是第一个书架上的一个片段:

     <div ondrop="drop(event)" ondragover="allowDrop(event)"  class="shelf read">
        <ul  class="books clearfix">
           <li id="The_Clean_Coder" class="book">
              <img id="img_The_Clean_Coder" draggable="true" ondragstart="drag(event)"  class="front" src="/bookshelf-data/assets/img/the_clean_coder.jpg" alt="The Clean Coder">
              <div class="back" onclick="" id="read_The_Clean_Coder">
                 <div class="p0">
                    <span class="score"><span id="rating_The_Clean_Coder"></span></span>
                    <script>
                       var span = document.getElementById("rating_The_Clean_Coder");
                       var rating = 4.3;
                       var starWidth = (span.offsetWidth - paddingLeft - (5*gapSize))/5.0;
                       var gaps = Math.floor(rating);
                       var width = (rating * starWidth) + (gaps * gapSize);
                       span.style.width = `${width}px`;
                       span.title=4.3 +" out of 5";
                    </script>
                    <dl class="p10">
                       <dt>Title:</dt>
                       <dd>The Clean Coder</dd>
                       <dt>Author:</dt>
                       <dd>Robert C. Martin</dd>
                       <dt>Released:</dt>
                       <dd>May 23, 2011</dd>
                    </dl>
                    <p class="description">
                       Programmers who endure and succeed amidst swirling uncertainty and nonstop pressure share a common attribute - They care deeply about...
                    </p>
                    <a href="https://www.amazon.com/Clean-Coder-Conduct-Professional-Programmers/dp/0137081073/ref=as_li_ss_tl?crid=9YJ9X71LWCXD&keywords=clean+architecture&qid=1564690296&s=gateway&sprefix=clean+arh,aps,155&sr=8-4&linkCode=ll1&tag=mainstringarg-20&linkId=cd98bbe94682a01daca2dcb8852cf52e&language=en_US" target="_blank">Amazon Link</a>
                 </div>
              </div>
           </li>

  • var span=document.getElementById(“对干净的编码器进行评级”); 风险值评级=4.3; var starWidth=(span.offsetWidth-paddingLeft-(5*间隙))/5.0; var缺口=数学下限(额定值); 风险值宽度=(评级*起始宽度)+(缺口*缺口); span.style.width=`${width}px`; span.标题=4.3+“共5个”; 标题: 干净的编码器 作者: 罗伯特·C·马丁 发布: 2011年5月23日

    在不断变化的不确定性和不间断的压力中坚持并取得成功的程序员有一个共同的特点——他们非常关心。。。


如您所见,我已将这些调用添加到书架div:ondrop=“drop(event)”ondragover=“allowDrop(event)”中,并且在每个图像上都包含draggable=“true”ondragstart=“drag(event)”。这遵循w3schools链接的模式,但在某些情况下,它似乎不会将项目附加到无序列表中。

问题是,当您将项目放在现有书籍的顶部时,事件的目标是现有书籍的
img
元素。当您在列表的右侧放置时,事件的目标是正常工作的
ul
元素

这个答案将解决您的问题:

请看这个小提琴:

或者,如果事件是
img
元素,则可以检查事件的目标并附加到其父节点,如下所示:

function drop(ev) {
    ev.preventDefault();
    var data = ev.dataTransfer.getData("text");
    var appendNode = ev.target;
    if(appendNode.nodeName == "IMG")
        appendNode = appendNode.parentNode.parentNode;
    appendNode.appendChild(document.getElementById(data).parentNode);
}

示例:

问题在于,当您在末尾放置元素时,它考虑的是正在移动的元素的父元素:

但当你将图像放在另一个图像上时,它会将该图像放在该图像中,考虑到它是父图像:

然后在图像中得到一个图像,这样它就不会显示也不会工作

为了解决这个问题,我建议使用“最近的”方法,以便附加到最近的书架上:

function closest(elem) {
  if (elem.className.indexOf(1)) {
    return elem;
  }
  var parent = elem.parentNode;
  if (parent.className.indexOf(1) != -1) {
    return parent;
  }
  return closest(parent);
}
function drop(ev) {
  ...
  ev.target.closest('ul').appendChild(document.getElementById(data).parentNode);
}
您将得到如下结果:


常数paddingLeft=15;
常数gapSize=10;
功能allowDrop(ev){
ev.preventDefault();
}
功能阻力(ev){
ev.dataTransfer.setData(“文本”,ev.target.id);
}
功能最近(elem){
if(elem.className.indexOf(1)){
返回元素;
}
var parent=elem.parentNode;
if(parent.className.indexOf(1)!=-1){
返回父母;
}
返回最近的(父级);
}
功能下降(ev){
ev.preventDefault();
var data=ev.dataTransfer.getData(“文本”);
控制台日志(ev.target)
//控制台日志(数据);
//log(document.getElementById(数据));
//log(document.getElementById(data.parentNode);
//var parent=document.getElementById(数据).parentNode
ev.target.closest('ul').appendChild(document.getElementById(data.parentNode));
}
我的架子
  • var span=document.getElementById(“rating_Clean_Architecture”); 风险值评级=4.3; var starWidth=(span.offsetWidth-paddingLeft-(5*间隙))/5.0; var缺口=数学下限(额定值); 风险值宽度=(评级*起始宽度)+(缺口*缺口); span.style.width=`${width}px`; span.title=4.3+“五取五”; 标题: 清洁建筑 作者: 罗伯特·C·马丁 发布: 2017年9月20日

    应用软件arc的通用规则

    <script>
        if (!Element.prototype.matches) {
            Element.prototype.matches =
            Element.prototype.msMatchesSelector ||
            Element.prototype.webkitMatchesSelector;
        }
    
        if (!Element.prototype.closest) {
            Element.prototype.closest = function(s) {
                var el = this;
    
                do {
                    if (Element.prototype.matches.call(el, s)) return el;
                    el = el.parentElement || el.parentNode;
                } while (el !== null && el.nodeType === 1);
                return null;
            };
        }
    </script>
    
    function drop(ev) {
        ev.preventDefault();
        var data = ev.dataTransfer.getData("text");
     
        ev.target.closest("ul").appendChild(document.getElementById(data).parentNode);
    }