Search code examples
javascriptdrag-and-drop

How to detect if a dragged element is dropped outside of its parent?


I want to find out when an element is dragged and dropped outside of its parent:

element.addEventListener('dragend', function(event) {
  // check if element is dropped outside its parent
}, false)

Solution

  • This technique identifies whether or not the dragged element was dropped inside its parent by:

    1. storing dragParent element upon 'dragstart'
    2. adding 'drop' event event listener to window
    3. using drop event's event.target to determine drop target
    4. walking up event target's DOM tree to search for dragParent

    // the parent of the dragged element
    let dragParent = null;
    
    function handleDragstart(ev) {
      // record the dragged element's parent
      dragParent = ev.target.parentElement;
    
      // set a 'drop' listener to the window
      window.addEventListener('drop', handleDrop);
    }
    
    // handle a 'drop' event to the window
    function handleDrop(ev) {
      ev.preventDefault();
    
      // remove the 'drop' listener
      window.removeEventListener('drop', handleDrop);
    
      // search up target's DOM tree for dragParent
      for (let el = ev.target; el.tagName !== 'HTML'; el = el.parentElement) {
        if (el === dragParent) {
          alert('#p1 was dropped inside dragParent');
          return;
        }
      }
      alert('#p1 was dropped outside dragParent');
    }
    
    // these event handers are required to make an element into a drop zone
    function handleDragover(ev) {
      ev.preventDefault();
    }
    
    function handleDragend(ev) {}
    
    // convert some elements into drop zones
    // ref: https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API#define_a_drop_zone
    //
    function makeDropZone(el) {
      el.addEventListener('dragover', handleDragover);
      el.addEventListener('drop', handleDragend);
    }
    for (const elId of ['sibling', 'parent', 'stranger']) {
      const element = document.getElementById(elId);
      makeDropZone(element);
    }
    makeDropZone(document.body);
    
    // make `#p1` draggable
    document.getElementById('p1').addEventListener('dragstart', handleDragstart);
    body {
      padding: 0.4rem 0 2rem 0.4rem;
      height: calc(100% - 0.5rem);
      width: calc(100% - 2rem);
      border: 1px solid #888;
      font-family: sans-serif;
    }
    
    div {
      padding: 0.6rem;
    }
    
    #parent {
      background-color: #ddd;
      width: 90%;
      padding: 0.3rem;
    }
    
    #p1 {
      background-color: white;
      padding: 0.1rem;
      width: 30%;
    }
    
    #sibling {
      background-color: #bbb;
      width: 50%;
      height: 3rem;
    }
    
    #stranger {
      background-color: #ddd;
      margin: 0.5rem 0;
      width: 90%;
      height: 80px;
      padding: 0.3rem;
    }
    <body>
      body
      <h3>Drag <code>#p1</code> onto <code>#sibling</code>, <code>#parent</code>, or
        <code>body</code></h3>
      <div id="parent">
        #parent
        <p id="p1" draggable="true">
          #p1 (draggable)
        </p>
        <div id="sibling">
          #sibling
        </div>
      </div>
      <div id="stranger">
        #stranger
      </div>
    </body>