拖放一个元素并放下另一个元素
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 可拖动元素并将其放入另一个列表的问题