Drag and Drop with JavaScript
Drag and Drop with JavaScript (使用JavaScript拖放)
One of the greatest interface solutions of JavaScript is known as “Drag and Drop”. In this chapter, we are going to find out what it is mainly used for, and why it should be an essential part of your work. (JavaScript最伟大的界面解决方案之一被称为“拖放”。 在本章中,我们将了解它主要用于什么,以及为什么它应该成为您工作的重要组成部分。)
Dragging and dropping something is a clear and straightforward way of doing multiple things: from copying and moving your documents to ordering ( for example, dropping an item into a cart).
Modern HTML standard includes a specific section with events like dragstart, dragend,and more. The handiest thing about these events is that they can help you solve simple tasks much easier. For example, with the help of them, you can handle the drag and drop of “external” files into your browser, in a way that you can take any file in the OS file manager and drop it into the browser window, thus giving JavaScript access to its contents. (现代HTML标准包含一个特定部分,其中包含拖动开始、拖动结束等事件。这些事件最方便的地方在于,它们可以帮助您更轻松地解决简单的任务。例如,在他们的帮助下,您可以将“外部”文件拖放到浏览器中,这样您就可以在操作系统文件管理器中获取任何文件并将其放入浏览器窗口,从而使JavaScript能够访问其内容。)
However, there are limitations to native Drag events. For example, they don’t allow you to limit dragging by a certain area. Another limitation is that you can’t make it “vertical” or “horizontal” only. Other drag and drop tasks can’t be implemented by that API either. (但是,本机拖动事件存在限制。例如,它们不允许您将拖动限制在某个区域。另一个限制是,您不能仅将其设置为“垂直”或“水平”。该API也无法实现其他拖放任务。)
Now, let’s see how to perform drag and drop with mouse events. (现在,让我们看看如何使用鼠标事件执行拖放。)
The Algorithm of Drag and Drop
The Algorithm of Drag and Drop (拖放算法)
The principal drag and drop algorithm looks as follows:
On the mousedown, you need to arrange the element for moving, if it is necessary ( for example, you can create its copy). (-在mousedown上,如果有必要,您需要安排移动元素(例如,您可以创建其副本)。)
On the mousemove, you should move it by changing the left/top, as well as position:absolute.
On the mouseup, implement the overall actions connected to a finished drag and drop. (-在鼠标上,实现与完成的拖放相关的整体操作。)
Now, you know the basics. Let’s get to the examples. (现在,您已经知道了基本知识。让我们来看示例。)
For instance, a drag and drop algorithm of a text will look like this:
<!DOCTYPE html>
<html>
<head>
<title>Title of the Document</title>
</head>
<body>
<p id='text'>Drag the text</p>
<script>
text.onmousedown = function(event) { // start the process
// get ready to move: make an absolute and top z-index
text.style.position = 'absolute';
text.style.zIndex = 1000;
// move it from any existing parents directly to the body
(//将其从任何现有的父母直接移动到身体)
// to position it relative to the body
(//将其相对于身体定位)
document.body.append(text);
// and put this absolutely positioned text under the pointer
(//并将此绝对定位的文本放在指针下)
moveAt(event.pageX, event.pageY);
// centers the text on the coordinates (pageX, pageY)
(//将文本居中在坐标(pageX, pageY)上)
function moveAt(pageX, pageY) {
text.style.left = pageX - text.offsetWidth / 2 + 'px';
text.style.top = pageY - text.offsetHeight / 2 + 'px';
}
function onMouseMove(event) {
moveAt(event.pageX, event.pageY);
}
// move the text on mousemove
(//在mousemove上移动文本)
document.addEventListener('mousemove', onMouseMove);
// drop the text, remove unneeded handlers
(//丢弃文本,删除不需要的处理程序)
document.onmouseup = function() {
document.removeEventListener('mousemove', onMouseMove);
text.onmouseup = null;
};
};
</script>
</body>
</html>
After running the code above, you can notice a strange thing: at the start of the process, the text “forks”, and you begin dragging its clone. Such behavior shows up because the browser contains its own drag and drop for different elements such as images that run automatically and conflicting with yours.
So, for disabling it, you need to use the following code:
text.ondragstart = function () {
return false;
};
There is another important aspect to note: you track mousemove on the document and not the text. But, mousemove triggers frequently, but not for each pixel. Hence, after the move, your pointer might jump from the text anywhere in the heart of the document., and outside of the window, as well.
Correct Positioning
Correct Positioning (正确定位)
As you noticed in the example above, the text always moves in a way that its center is under the pointer, like this:
text.style.left = pageX - text.offsetWidth / 2 + 'px';
tex.style.top = pageY - text.offsetHeight / 2 + 'px';
However, there is a side-effect here. For initiating the drag and drop, you should mousedown anywhere you want, on the text. (但是,这里有一个副作用。要启动拖放操作,应将鼠标放在文本上任意位置。)
For example, if you begin to drag by the edge of the text, the pointer should be kept over the edge throughout the dragging. (例如,如果开始拖动文本边缘,则应在整个拖动过程中将指针保持在边缘上。)
You can also update the algorithm following the steps below:
// onmousedown
let shiftX = event.clientX - text.getBoundingClientRect().left;
let shiftY = event.clientY - text.getBoundingClientRect().top;
So, the final and better positioning is demonstrated below:
<!DOCTYPE html>
<html>
<head>
<title>Title of the Document</title>
<style>
#text {
cursor: pointer;
cursor: pointer;
width: 40px;
height: 40px;
}
</style>
</head>
<body>
<p id='text'>Drag the text</p>
<script>
text.onmousedown = function(event) {
let shiftX = event.clientX - text.getBoundingClientRect()
(let shiftX = event.clientX - text.getBoundingClientRect ())
.left;
let shiftY = event.clientY - text.getBoundingClientRect()
(let shiftY = event.clientY - text.getBoundingClientRect ())
.top;
text.style.position = 'absolute';
text.style.zIndex = 1000;
document.body.append(text);
moveAt(event.pageX, event.pageY);
// move the text along the coordinates (pageX, pageY)
(//沿坐标移动文本(pageX, pageY))
// taking into account the initial shifts
(//考虑到初始班次)
function moveAt(pageX, pageY) {
text.style.left = pageX - shiftX + 'px';
text.style.top = pageY - shiftY + 'px';
}
function onMouseMove(event) {
moveAt(event.pageX, event.pageY);
}
// move the text to the mousemove
(//将文本移动到鼠标移动)
document.addEventListener('mousemove', onMouseMove);
// drop the text, remove unneeded handlers
(//丢弃文本,删除不需要的处理程序)
text.onmouseup = function() {
document.removeEventListener('mousemove', onMouseMove);
text.onmouseup = null;
};
};
text.ondragstart = function() {
return false;
};
</script>
</body>
</html>
The d distinction between the past cases and this one is obvious: in the previous example, the text jumps under the pointer.
Potential Drop Targets (Droppables)
Potential Drop Targets (Droppables) (潜在跌落目标(可跌落物))
In the examples above, the text was dropped anywhere to stay. But, in practice, it is usually necessary to take an element and drop it into another (for example, a file into a folder). (在上面的示例中,文本被放置在任何地方。但是,在实践中,通常需要获取一个元素并将其放入另一个元素(例如,将文件放入文件夹)。)
In order to implement drag and drop functionality in JavaScript, we need to define the potential drop targets, also known as droppables. These are the elements on the page where the user can drop the dragged element. (为了在JavaScript中实现拖放功能,我们需要定义潜在的拖放目标,也称为droppables。这些是页面上的元素,用户可以在其中拖放拖动的元素。)
Here’s the HTML part:
<div id="draggable1" class="draggable" draggable="true">
Drag me!
</div>
<div id="droppable1" class="droppable">
Drop here!
</div>
This is the HTML code for our draggable and droppable elements. We have created a div element with id attribute set to “draggable1” and a class name of “draggable”. We have also set the draggable attribute to “true”, which makes the element draggable. (这是我们可拖放元素的HTML代码。我们创建了一个div元素,其id属性设置为“draggable1” ,类名为“draggable”。我们还将draggable属性设置为"true" ,这使得元素可拖动。)
We have also created a div element with id attribute set to “droppable1” and a class name of “droppable”. This will serve as the drop target for our draggable element. (我们还创建了一个div元素,其中id属性设置为“droppable1” ,类名为“droppable”。这将作为我们的可拖动元素的拖放目标。)
And here’s the CSS part:
.draggable {
width: 100px;
height: 50px;
background-color: lightblue;
border: 1px solid black;
padding: 10px;
}
.droppable {
width: 200px;
height: 100px;
background-color: white;
border: 1px solid black;
padding: 10px;
}
This is the CSS code that styles our draggable and droppable elements. We have set the width, height, background color, border, and padding for our elements. You can customize these styles as per your needs. (这是为我们的可拖放元素设置样式的CSS代码。我们为元素设置了宽度、高度、背景颜色、边框和填充。您可以根据需要自定义这些样式。)
And finally let’s take a look at the JavaScript code that make this all happen:
var draggable = document.getElementById("draggable1");
var droppable = document.getElementById("droppable1");
draggable.addEventListener("dragstart", function(event) {
event.dataTransfer.setData("text", event.target.id);
});
droppable.addEventListener("dragover", function(event) {
event.preventDefault();
});
droppable.addEventListener("drop", function(event) {
event.preventDefault();
var data = event.dataTransfer.getData("text");
var draggableElement = document.getElementById(data);
droppable.appendChild(draggableElement);
});
We first get references to our draggable and droppable elements using their id attributes. (我们首先使用id属性获取对可拖放元素的引用。)
We then add an event listener to our draggable element for the dragstart event. Inside this event listener, we set the dataTransfer property of the event object to the id of the draggable element using the setData() method. This will allow us to access the id of the draggable element when it is dropped onto the droppable element. (然后,我们为dragstart事件的可拖动元素添加一个事件侦听器。在此事件侦听器中,我们使用setData ()方法将事件对象的dataTransfer属性设置为可拖动元素的id。这将允许我们在拖放到可拖放元素上时访问可拖放元素的ID。)
We then add an event listener to our droppable element for the dragover event. Inside this event listener, we prevent the default action of the event using the preventDefault() method. This is required for the drop event to work correctly. (然后,我们为dragover事件的可丢弃元素添加一个事件侦听器。在此事件侦听器中,我们使用preventDefault ()方法阻止事件的默认操作。这是丢弃事件正常工作所必需的。)
Finally, we add an event listener to our droppable element for the drop event. Inside this event listener, we prevent the default action of the event using the preventDefault() method. We then get the id of the draggable element from the dataTransfer property of the event object using the getData() method. (最后,我们为DROP事件的可DROPPABLE元素添加了一个事件侦听器。在此事件侦听器中,我们使用preventDefault ()方法阻止事件的默认操作。然后,我们使用getData ()方法从事件对象的dataTransfer属性获取可拖动元素的ID。)
We then get a reference to the draggable element using its id and append it to the droppable element using the appendChild() method. This will move the draggable element inside the droppable element when it is dropped onto it. (然后,我们使用其id获取对draggable元素的引用,并使用appendChild ()方法将其追加到droppable元素。当可拖放元素被拖放到可拖放元素上时,它会将可拖放元素移动到可拖放元素内部。)
Now that we have each part, let’s take the final step and put it all together and see the result:
<!DOCTYPE html>
<html>
<head>
<title>Title of the Document</title>
<style>
#text {
cursor: pointer;
cursor: pointer;
width: 40px;
height: 40px;
}
.droppable {
background-color: white;
border: 1px solid black;
padding: 10px;
}
.droppable.drag-over {
background-color: lightblue;
}
.droppable.dropped {
background-color: lightgreen;
}
</style>
</head>
<body>
<div id="draggable1" class="draggable" draggable="true">
Drag me!
(拖动我)
</div>
<div id="droppable1" class="droppable">
Drop here!
(放到此处!)
</div>
<script>
var draggables = document.getElementsByClassName("draggable");
for (var i = 0; i < draggables.length; i++) {
draggables[i].addEventListener("dragstart", function (event) {
event.dataTransfer.setData("text", event.target.id);
});
}
var droppables = document.getElementsByClassName("droppable");
for (var i = 0; i < droppables.length; i++) {
droppables[i].addEventListener("dragover", function (event) {
event.preventDefault();
event.target.classList.add("drag-over");
});
droppables[i].addEventListener("dragleave", function (event) {
event.target.classList.remove("drag-over");
});
droppables[i].addEventListener("drop", function (event) {
var id = event.dataTransfer.getData("text");
var draggableElement = document.getElementById(id);
event.target.appendChild(draggableElement);
event.target.classList.remove("drag-over");
event.target.classList.add("dropped");
setTimeout(function () {
event.target.classList.remove("dropped");
}, 1000);
});
}
</script>
</body>
</html>
That’s it! This code should now allow you to drag the “Drag me!” element onto the “Drop here!” element and drop it inside it. (就是这样!此代码现在应允许您将“Drag me!”元素拖动到“Drop here!”元素上并将其放入其中。)
Summary
Summary (概要)
In this chapter, we considered the basic algorithm for drag and drop. The interface of drag and drop allows applications to apply the drag and drop features on different browsers. The user can select a draggable element with the mouse, drag it to a droppable element, dropping it by releasing the mouse button. (在本章中,我们考虑了拖放的基本算法。 拖放界面允许应用程序在不同的浏览器上应用拖放功能。 用户可以使用鼠标选择一个可拖动的元素,将其拖动到可放置的元素,通过释放鼠标按钮将其放置。)
For websites, extensions and so on, there is an option of customizing, which elements can become draggable, the type of feedback they produce. Also, there are frameworks building architecture on it: for example, DragZone, Droppable, Draggable, and other classes. Most of them act similarly to what was described above.