Welcome toVigges Developer Community-Open, Learning,Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
156 views
in Technique[技术] by (71.8m points)

javascript - HTML Drag and Drop between multiple Unordered Lists

I have a relatively simple web app that I'm creating as a bookshelf and I want the user to be able to drag and drop books between the bookshelves. It works exactly as I want when I drop a book to the "right" side of the existing books in one of the bookshelves. The book that has been dragged and dropped is added to the shelf.

Where it isn't working is when I drop a book on top of an existing book. When I do that, the book is removed from the existing shelf -- but it disappears totally instead of being added to the targeted bookshelf as I desire. Its unclear to me why this is.

See here: https://mainstringargs.github.io/bookshelf-data/test.html

(I'm using Firefox, if that is relevant)

Relevant source code is here (raw html): https://github.com/mainstringargs/bookshelf-data/blob/master/test.html

Note: This was originally generated via jekyll in github, but since this problem is not jekyll centric, I'm not including that source code detail.

I largely followed this guide for how to implement drag and drop:

https://www.w3schools.com/html/html5_draganddrop.asp

The relevant javascript code is below. The 'drop' function is intended to drop the relevant list item to the new list.

     <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>

Each bookshelf is itself wrapped in a div containing a single unordered list (ul) with list items (li) representing each book. Here is a snippet from the first bookshelf:

     <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>

As you can see, I've added these calls to the bookshelf div: ondrop="drop(event)" ondragover="allowDrop(event)" and on each image, I've included draggable="true" ondragstart="drag(event)". This follows the pattern from the w3schools link, but there are cases where it does not seem to append the item to the unordered list.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

The problem is that when you drop the element at the end, it's considering the parent of that element that is being moved the ul :

enter image description here

But when you drop your image on top of another image, it's putting that image inside the dropped one, considering it it's parent :

enter image description here

You then get an image inside an image, so it wouldn't show and wouldn't work.

To fix this I would suggest using a 'closest' method, in order to append to the closest book shelf :

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);
}

You'd have a result like the following :

<link rel="stylesheet" href="https://mainstringargs.github.io/myshelf/assets/css/main.css">
<script>
  const paddingLeft = 15;
  const gapSize = 10;

  function allowDrop(ev) {
    ev.preventDefault();
  }

  function drag(ev) {
    ev.dataTransfer.setData("text", ev.target.id);
  }

  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.preventDefault();
    var data = ev.dataTransfer.getData("text");
    console.log(ev.target)
    //console.log(data);
    //console.log(document.getElementById(data));
    //console.log(document.getElementById(data).parentNode);
    //var parent = document.getElementById(data).parentNode
    ev.target.closest('ul').appendChild(document.getElementById(data).parentNode);
  }
</script>
<section id="bookshelf">
  <div class="user-details">
    <h1> My Shelf </h1>
  </div>
  <div class="shelf ondeck" ondrop="drop(event)" ondragover="allowDrop(event)">
    <ul class="books clearfix">
      <li id="Clean_Architecture" class="book">
        <img id="img_Clean_Architecture" draggable="true" ondragstart="drag(event)" class="front" src="https://mainstringargs.github.io/bookshelf-data/assets/img/clean_architecture.jpg" alt="Clean Architecture">
        <div class="back" onclick="" id="ondeck_Clean_Architecture">
          <div class="p0">
            <span class="score"><span id="rating_Clean_Architecture"></span></span>
            <script>
              var span = document.getElementById("rating_Clean_Architecture");
              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>Clean Architecture</dd>
              <dt>Author:</dt>
              <dd>Robert C. Martin</dd>
              <dt>Released:</dt>
              <dd>September 20, 2017</dd>
            </dl>
            <p class="description">
              By applying universal rules of software architecture, you can dramatically improve developer productivity throughout the life of any software system....
            </p>
            <a href="https://www.amazon.com/Clean-Architecture-Craftsmans-Software-Structure/dp/0134494164/ref=as_li_ss_tl?crid=9YJ9X71LWCXD&keywords=clean+architecture&qid=1564690296&s=gateway&sprefix=clean+arh,aps,155&sr=8-1&linkCode=ll1&tag=mainstringarg-20&linkId=d660a50d6cae9d31db54a46b1367c79e&language=en_US"
              target="_blank">Amazon Link</a>
          </div>
        </div>
      </li>
      <li id="Designing_Data-Intensive_Applications" class="book">
        <img id="img_Designing_Data-Intensive_Applications" draggable="true" ondragstart="drag(event)" class="front" src="https://mainstringargs.github.io/bookshelf-data/assets/img/designing_data_intensive_applications.jpg" alt="Designing Data-Intensive Applications">
        <div class="back" onclick="" id="ondeck_Designing_Data-Intensive_Applications">
          <div class="p0">
            <span class="score"><span id="rating_Designing_Data-Intensive_Applications"></span></span>
            <script>
              var span = document.getElementById("rating_Designing_Data-Intensive_Applications");
              var rating = 4.7;
              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.7 + " out of 5";
            </script>
            <dl class="p10">
              <dt>Title:</dt>
              <dd>Designing Data-Intensive Applications</dd>
              <dt>Author:</dt>
              <dd>Martin Kleppmann</dd>
              <dt>Released:</dt>
              <dd>April 2, 2017</dd>
            </dl>
            <p class="description">
              Data is at the center of many challenges in system design today. Difficult issues need to be figured out, such...
            </p>
            <a href="https://www.amazon.com/Designing-Data-Intensive-Applications-Reliable-Maintainable/dp/1449373321/ref=as_li_ss_tl?crid=2J0BUT3XUW5BX&keywords=streaming+systems&qid=1561728107&s=books&sprefix=Streaming+Systems,stripbooks,156&sr=1-2&linkCode=ll1&tag=mainstringarg-20&linkId=f07f59f79a517afbf479cd3642e61c3a&language=en_US"
              target="_blank">Amazon Link</a>
          </div>
        </div>
      </li>
      <li id="Streaming_Systems" class="book">
        <img id="img_Streaming_Systems" draggable="true" ondragstart="drag(event)" class="front" src="https://mainstringargs.github.io/bookshelf-data/assets/img/streaming_systems.jpg" alt="Streaming Systems">
        <div class="back" onclick="" id="ondeck_Streaming_Systems">
          <div class="p0">
            <span class="score"><span id="rating_Streaming_Systems"></span></span>
            <script>
              var span = document.getElementById("rating_Streaming_Systems");
              var rating = 4.5;
              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.5 + " out of 5";
            </script>
            <dl class="p10">
              <dt>Title:</dt>
              <dd>Streaming Systems</dd>
              <dt>Author:</dt>
              <dd>Tyler Akidau, Slava Chernyak & Reuven Lax</dd>
              <dt>Released:</dt>
              <dd>August 2, 2018</dd>
            </dl>
            <p class="description">
              Streaming data is a big deal in big data these days. As more and more businesses seek to tame the...
            </p>
            <a href="https://www.amazon.com/Streaming-Systems-Where-Large-Scale-Processing/dp/1491983876/ref=as_li_ss_tl?keywords=Streaming+Systems&qid=1564691718&s=gateway&sr=8-2&linkCode=ll1&tag=mainstringarg-20&linkId=3976bbc20ed7fc47509f68ea58a53b19&language=en_US"
              target="_blank">Amazon Link</a>
          </div>
        </div>
      </li>
      <li id="Clean_Code" class="book">
        <img id="img_Clean_Code" draggable="true" ondragstart="drag(event)" class="front" src="https://mainstringargs.github.io/bookshelf-data/assets/img/clean_code.jpg" alt="Clean Code">
        <div class="back" onclick="" id="ondeck_Clean_Code">
          <div class="p0">
            <span class="score"><span id="rating_Clean_Code"></span></span>
            <script>
              var span = document.getElementById("rating_Clean_Code");
              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>Clean Code</dd>
              <dt>Author:</dt>
              <dd>Robert C. Martin</dd>
              <dt>Released:</dt>
              <dd>August 11, 2008</dd>
            </dl>
            <p class="description">
              Best agile practices of cleaning code “on the fly” that will instill within you the values of a software craftsman...
            </p>
            <a href="https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882/ref=as_li_ss_tl?_encoding=UTF8&pd_rd_i=0132350882&pd_rd_r=779f34be-10b6-4da1-bee1-315e02822e8a&pd_rd_w=cbXpV&pd_rd_wg=7e0si&pf_rd_p=1c11b7ff-9ffb-4ba6-8036-be1b0afa79bb&pf_rd_r=5DNWQZ8VE6S37W6B11GZ&psc=1&refRID=5DNWQZ8VE6S37W6B11GZ&linkCode=ll1&tag=mainstringarg-20&linkId=c0c7bb9dde941f0956bbee5628952f98&language=en_US"
              target="_blank">Amazon Link</a>
          </div>
        </div>
      </li>
      <li id="Weapons_of_Math_Destruction" class="book">
        <img id="img_Weapons_of_Math_Destruction" draggable="true" ondragstart="drag(event)" class="front" src="https://mainstringargs.github.io/bookshelf-data/assets/img/weapons_of_math_destruction.jpg" alt="Weapons of Math Destruction">
        <div class="back" onclick="" id="ondeck_Weapons_of_Math_Destruction">
          <div class="p0">
            <span class="score"><span id="rating_Weapons_of_Math_Destruction"></span></span>
            <script>
              var span = document.getElementById("rating_Weapons_of_Math_Destruction");
              var rating = 4.1;
              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.1 + " out of 5";
            </script>
            <dl class="p10">
              <dt>Title:</dt>
              <dd>Weapons of Math Destruction</dd>
              <dt

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to Vigges Developer Community for programmer and developer-Open, Learning and Share
...