拖放一个元素并放下另一个元素

Posted

技术标签:

【中文标题】拖放一个元素并放下另一个元素【英文标题】:Drag one Element and drop other Element 【发布时间】:2020-11-09 13:38:07 【问题描述】:

我正在尝试使用动态创建的容器和可放置的项目来实现拖放

    点击按钮后可放置部分将动态创建 会有带有静态文本的元素的无序列表,例如电子邮件字段、电话号码字段、多个选项等。 我想根据静态字段类型(电子邮件、电话、多个复选框)动态创建新元素 需要在放置时插入新元素我已经创建了一个部分工作的代码,但不知何故这不能正常工作我怀疑我需要对 dataTransfer 对象做些什么。

这是部分工作,我能够动态创建元素并将其附加到 dom,但是在拖动时无法按预期工作。任何帮助将不胜感激。

let draggables = document.querySelectorAll('.draggable');
let containers = document.querySelectorAll('.container');
let mainContainer = document.getElementById('main-container');
let afterElement;
let elementToInsert;
let count = 0;

let addsec = document.getElementById('addsection');

    addsec.addEventListener('click',function(e)
        let container = document.createElement('div');
            container.classList.add('container');
        let section = document.createElement('h1');
            section.innerhtml = `Section $count++`;
            container.appendChild(section);
        mainContainer.appendChild(container)
        init();
    )

    draggables.forEach(draggable => 
        draggable.addEventListener('dragstart', (e) => 
            e.stopPropagation();
          
        console.log("drag start",e,draggable);
        if(draggable && draggable.getAttribute('name'))
            let element = draggable.getAttribute('name');
            switch(element)
                case 'text-field':
                    elementToInsert = document.createElement('input');
                    
                    elementToInsert.classList.add('draggable');
                    elementToInsert.setAttribute('draggable',true);
                    elementToInsert.setAttribute('placeholder','Text');
                    elementToInsert.setAttribute('disabled',true);
                    
                    break;
                case 'email-field':
                        elementToInsert = document.createElement('input');
                        
                        elementToInsert.classList.add('draggable');
                        elementToInsert.setAttribute('draggable',true);
                        elementToInsert.setAttribute('placeholder','Email');
                        elementToInsert.setAttribute('disabled',true);
                        break;
                case 'phone-field':
                    elementToInsert = document.createElement('input');
                    
                    elementToInsert.classList.add('draggable');
                    elementToInsert.setAttribute('draggable',true);
                    elementToInsert.setAttribute('placeholder','Phone');
                    elementToInsert.setAttribute('disabled',true);
                    break;
                default:
                    elementToInsert = draggable;
                    break;
            
        else
            elementToInsert = draggable; 
        

        elementToInsert.addEventListener('dragstart',function(ev)
            ev.dataTransfer.setData('elementid',ev.target.id);
        )
        elementToInsert.setAttribute('id',`field-$Date.now()`);
        draggable.classList.add('dragging')
        )
    
        draggable.addEventListener('dragend', () => 
            draggable.classList.remove('dragging')
        )
    )

   
    function init()
        draggables = document.querySelectorAll('.draggable');
        containers = document.querySelectorAll('.container');
        console.log("draggables",draggables,containers);
        
        containers.forEach(container => 
            container.addEventListener('dragover', e => 
                e.preventDefault()
                let data = e.dataTransfer.getData("elementid");
                console.log("eee",data);
                afterElement = getDragAfterElement(container, e.clientY);
                if (afterElement == null) 
                
                container.appendChild(elementToInsert)
                 else 
                container.insertBefore(elementToInsert, afterElement)
                
            )
            container.addEventListener('drop', e => 
                e.preventDefault()
                let data = e.dataTransfer.getData("elementid");
                // console.log("ff",data);
                draggables = document.querySelectorAll('.draggable');
                // init();
            )
            container.addEventListener('dragleave', e => 
                e.preventDefault();
            )
            container.addEventListener('dragenter', e => 
                e.preventDefault();
            )
        )


    

    function getDragAfterElement(container, y) 
    const draggableElements = [...container.querySelectorAll('.draggable:not(.dragging)')]

    return draggableElements.reduce((closest, child) => 
        const box = child.getBoundingClientRect()
        const offset = y - box.top - box.height / 2
        if (offset < 0 && offset > closest.offset) 
        return  offset: offset, element: child 
         else 
        return closest
        
    ,  offset: Number.NEGATIVE_INFINITY ).element
    
body 
    margin: 0;
  
  
  h1
    color:#fff;
  
  .container 
    background-color: #333;
    padding: 1rem;
    margin: 1rem;
    width: 200px;
  
  
  .draggable 
    padding: 1rem;
    background-color: white;
    border: 1px solid black;
    cursor: move;
  
  
  .draggable.dragging 
    opacity: .5;
  
  
#main-container
    display: flex;


  input
      display: block;
      width:83%;
      height: 0px;
  

  .main
      display: flex;
  
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
 <link rel="stylesheet" type="text/css" href="index.css" >
 
</head>
<body>

    <div class="main">
        <div>
            <div class="draggable" name="text-field" draggable="true">Text Field</div>
            <div class="draggable" name="email-field" draggable="true">Email Field</div>
            <div class="draggable" name="phone-field" draggable="true">Phone Field</div>
        </div>
        <div id="main-container">
            <!-- <div class="container">
                <h1>Section 1</h1>
              <p class="draggable" draggable="true">1</p>
              <p class="draggable" draggable="true">2</p>
            </div>
            <div class="container">
              <h1>Section 2</h1>
              <p class="draggable" draggable="true">3</p>
              <p class="draggable" draggable="true">4</p>
            </div> -->
        </div>
    </div>

  <button id="addsection">Add section</button>
  <script src="index.js"></script>



</body>
</html>

【问题讨论】:

【参考方案1】:

最后我不确定这是正确的方法,但它可以满足我的要求

function CustomDragAndDrop()
    this.draggables = undefined;
    this.containers = undefined;
    this.placeholder = undefined;
    this.elementToInsert = undefined;
    this.afterElement = undefined;
    this.sectionCount = 0;
    this.mainContainer = undefined;
    this.existingElement = undefined;

    this.addSection = function()
        let container = document.createElement('div');
            container.classList.add('container');
        let section = document.createElement('h1');
            section.innerHTML = `Section $this.sectionCount++`;
            container.appendChild(section);
            console.log("this main container",this.mainContainer);
        this.mainContainer.appendChild(container);
        this.addEventListenersForContainer(container);
        this.updateContainers();
    
    this.updateContainers = function()
        this.containers = document.querySelectorAll('.container');
    
    this.updateDraggables = function()
        this.draggables = document.querySelectorAll('.draggable');
    
    this.addEventListeners = function(name)
        console.log("adding event listeners",this.containers,this.draggables);
        if(name === 'containers' && this.containers)
            this.containers.forEach(container => 
                container.addEventListener('dragover',e => this.onDragHover(e,container,false));
                container.addEventListener('drop',e => this.onDragDrop(e),false);
                container.addEventListener('dragleave',e => this.onDragLeave(e),false);
                container.addEventListener('dragenter',e => this.onDragEnter(e),false);
            );
        else if('draggables' && this.draggables)
            this.draggables.forEach(draggable => 
                draggable.addEventListener('dragstart',e => this.onDragStart(e,draggable),false);
                draggable.addEventListener('dragend', e => this.onDragEnd(e,draggable),false);
            );
        else if(this.draggables && this.containers)
            this.containers.forEach(container => 
                container.addEventListener('dragover',e => this.onDragHover(e,container),false);
                container.addEventListener('drop', e => this.onDragDrop(e),false);
                container.addEventListener('dragleave',e => this.onDragLeave(e),false);
                container.addEventListener('dragenter',e => this.onDragEnter(e),false);
            );
            this.draggables.forEach(draggable => 
                draggable.addEventListener('dragstart',e => this.onDragStart(e),false);
                draggable.addEventListener('dragend', e => this.onDragEnd(e),false);
            );
        
        console.log("draggables",this.draggables);
        console.log("this.containers",this.containers);
    
    this.removeEventListeners = function(name)
        if(name === 'container' && this.containers)
            this.containers.forEach(container => 
                container.removeEventListener('dragover',e => this.onDragHover(e,container),false);
                container.removeEventListener('drop', e => this.onDragDrop(e),false);
                container.removeEventListener('dragleave',e => this.onDragLeave(e),false);
                container.removeEventListener('dragenter',e => this.onDragEnter(e),false);
            );
        else if(name === 'draggables' && this.draggables)
            this.draggables.forEach(draggable => 
                draggable.removeEventListener('dragstart',e => this.onDragStart(e,draggable),false);
                draggable.removeEventListener('dragend', e => this.onDragEnd(e,draggable),false);
            );
        else if(this.draggables && this.containers)
            this.containers.forEach(container => 
                container.removeEventListener('dragover',e => this.onDragHover(e,container),false);
                container.removeEventListener('drop',e => this.onDragDrop(e),false);
                container.removeEventListener('dragleave',e => this.onDragLeave(e),false);
                container.removeEventListener('dragenter',e => this.onDragEnter(e),false);
            );
            this.draggables.forEach(draggable => 
                draggable.removeEventListener('dragstart',e => this.onDragStart(e),false);
                draggable.removeEventListener('dragend',e => this.onDragEnd(e),false);
            );
        
    
    this.createPlaceHolder = function()
        let placeholder = document.createElement('div');
        placeholder.style.height = '50px';
        placeholder.style.borderRadius = '5px';
        placeholder.style.backgroundColor = '#eee';
        placeholder.style.margin = '10px 0';
        this.placeholder = placeholder;
    
    this.getDragAfterElement = function(container, y)
    const draggableElements = [...container.querySelectorAll('.draggable:not(.dragging)')]
    return draggableElements.reduce((closest, child) => 
        const box = child.getBoundingClientRect()
        const offset = y - box.top - box.height / 2
        if (offset < 0 && offset > closest.offset) 
        return  offset: offset, element: child 
         else 
        return closest
        
    ,  offset: Number.NEGATIVE_INFINITY ).element
    

    this.onDragStart = function(e,draggable)
        e.stopPropagation();
        let elementToInsert;
        console.log("drag start",draggable);
        // e.dataTransfer.setData('elementid',e.target.id);
        if(draggable && draggable.getAttribute('name'))
            let element = draggable.getAttribute('name');
            
            switch(element)
                case 'text-field':
                    elementToInsert = document.createElement('input');
                    elementToInsert.classList.add('draggable');
                    elementToInsert.setAttribute('draggable',true);
                    elementToInsert.setAttribute('placeholder','Text');
                    elementToInsert.setAttribute('disabled',true);
                    elementToInsert.setAttribute('id',`field-$Date.now()`);
                    this.elementToInsert = elementToInsert;
                    this.existingElement = false;
                    break;
                case 'email-field':
                        elementToInsert = document.createElement('input');
                        elementToInsert.classList.add('draggable');
                        elementToInsert.setAttribute('draggable',true);
                        elementToInsert.setAttribute('placeholder','Email');
                        elementToInsert.setAttribute('disabled',true);
                        elementToInsert.setAttribute('id',`field-$Date.now()`);
                        this.elementToInsert = elementToInsert;
                        this.existingElement = false;
                        break;
                case 'phone-field':
                    elementToInsert = document.createElement('input');
                    
                    elementToInsert.classList.add('draggable');
                    elementToInsert.setAttribute('draggable',true);
                    elementToInsert.setAttribute('placeholder','Phone');
                    elementToInsert.setAttribute('disabled',true);
                    elementToInsert.setAttribute('id',`field-$Date.now()`);
                    this.elementToInsert = elementToInsert;
                    this.existingElement = false;
                    break;
                default:
                    this.elementToInsert = draggable;
                    this.existingElement = true;
                    break;
            
        else
            this.existingElement = true;
            this.elementToInsert = draggable; 
        
        this.placeholder.setAttribute("id",`placeholder-$Date.now()`);
        draggable.classList.add('dragging');
    
    this.onDragEnd = function(e,draggable)
        draggable.classList.remove('dragging');
        console.log("drag end",this.elementToInsert);
        if(!this.existingElement)
            this.addEventListenerForDraggableItem(this.elementToInsert);
            this.updateDraggables('draggables');
        else
            console.log("existing ele",this.elementToInsert);
            this.elementToInsert.classList.remove('dragging');
        
    
    this.addEventListenerForDraggableItem = function(element)
        console.log("ele",element);
        element.addEventListener('dragstart',e => this.onDragStart(e,element));
        element.addEventListener('dragend',e => this.onDragEnd(e,element));
    

    this.addEventListenersForContainer = function(container)
        container.addEventListener('dragover',e => this.onDragHover(e,container,false));
        container.addEventListener('drop',e => this.onDragDrop(e),false);
        container.addEventListener('dragleave',e => this.onDragLeave(e),false);
        container.addEventListener('dragenter',e => this.onDragEnter(e),false);
    

    this.onDragHover = function(e,container)
        e.preventDefault();
        this.afterElement = this.getDragAfterElement(container, e.clientY);
        if (this.afterElement == null) 
            container.appendChild(this.placeholder)
         else 
            container.insertBefore(this.placeholder, this.afterElement)
        
    
    this.onDragEnter = function(e)
        e.preventDefault();
    
    this.onDragLeave = function(e)
        e.preventDefault();
        console.log("on drag leave");
    
    this.onDragDrop = function(e)
        e.preventDefault()
        // let data = e.dataTransfer.getData("elementid");
        this.placeholder.replaceWith(this.elementToInsert);
    

    this.init = function()
        try
            this.mainContainer = document.getElementById('main-container');
            this.updateContainers();
            this.updateDraggables();
            this.addEventListeners();
            this.createPlaceHolder();
        catch(e)
            console.log(e);
        
    


let customDragnDrop = new CustomDragAndDrop();
customDragnDrop.init();
body 
    margin: 0;
  
  
  h1
    color:#fff;
  
  .container 
    background-color: #333;
    padding: 1rem;
    margin: 1rem;
    width: 200px;
  
  
  .draggable 
    padding: 1rem;
    background-color: white;
    border: 1px solid black;
    cursor: move;
  
  
  .draggable.dragging 
    opacity: .5;
  
  
#main-container
    display: flex;


  input
      display: block;
      width:83%;
      height: 0px;
  

  .main
      display: flex;
  
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
 <link rel="stylesheet" type="text/css" href="index.css" >
 
</head>
<body>

    <div class="main">
        <div>
            <div class="draggable" id="text" name="text-field" draggable="true">Text Field</div>
            <div class="draggable" id="email" name="email-field" draggable="true">Email Field</div>
            <div class="draggable" id="phone" name="phone-field" draggable="true">Phone Field</div>
        </div>
        <div id="main-container">
            <!-- <div class="container">
                <h1>Section 1</h1>
              <p class="draggable" draggable="true">1</p>
              <p class="draggable" draggable="true">2</p>
            </div>
            <div class="container">
              <h1>Section 2</h1>
              <p class="draggable" draggable="true">3</p>
              <p class="draggable" draggable="true">4</p>
            </div> -->
        </div>
    </div>

  <button onclick="customDragnDrop.addSection()">Add section</button>
  <script src="index.js"></script>



</body>
</html>

【讨论】:

以上是关于拖放一个元素并放下另一个元素的主要内容,如果未能解决你的问题,请参考以下文章

从一个列表中拖放 jQuery UI 可拖动元素并将其放入另一个列表的问题

使用 jQuery UI 拖放:更改拖放的元素

使用 cdkDropList 时元素样式不适用(角度 cdk 拖放)

通过拖放将自定义元素的对象引用传递给另一个自定义元素

web页面拖放效果的实现

web页面拖放效果的实现