JavaScript 拖放

概要:在本教程中,您将了解 JavaScript 拖放 API 以及如何使用它来实现一个简单的拖放应用程序。

JavaScript 拖放 API 简介

HTML5 正式引入了拖放规范。大多数现代 Web 浏览器都实现了基于 HTML5 规范的原生拖放功能。

默认情况下,只有图像和文本可以拖放。要拖放图像,您只需按住鼠标按钮,然后移动它。要拖放文本,您需要突出显示一些文本,并像拖放图像一样拖放它。

HTML5 规范指定几乎所有元素都可以拖放。要使元素可拖放,您需要将draggable 属性及其值为true 添加到其 HTML 标签中。例如

<div class="item" draggable="true"></div>Code language: HTML, XML (xml)

可拖放元素上的事件

当您拖放元素时,这些事件将按以下顺序触发

  • dragstart
  • drag
  • dragend

当您按住鼠标按钮并开始移动鼠标时,dragstart 事件将在您拖动的可拖放元素上触发。光标将更改为禁止拖放符号(带有斜线的圆圈),以指示您无法将元素拖放到自身上。

dragstart 事件触发后,只要您拖动元素,drag 事件就会反复触发。

dragend 事件在您停止拖动元素时触发。

所有事件的目标(e.target)都是正在拖动的元素。

默认情况下,浏览器不会更改拖动元素的外观。因此,您可以根据自己的喜好自定义其外观。

放置目标上的事件

当您将元素拖放到有效的放置目标上时,这些事件将按以下顺序触发

  • dragenter
  • dragover
  • dragleavedrop

当您将元素拖放到放置目标上时,dragenter 事件就会触发。

dragenter 事件触发后,只要您拖动元素在放置目标的边界内,dragover 事件就会反复触发。

当您将元素拖出放置目标的边界时,dragover 事件将停止触发,dragleave 事件将触发。

如果您将元素放置到目标上,则drop 事件将触发,而不是dragleave 事件。

dragenterdragoverdragleavedrop 事件的目标(e.target)是放置目标元素。

有效的放置目标

几乎所有元素都支持放置目标事件(dragenterdragoverdragleavedrop)。但是,它们默认不允许放置。

如果您将元素拖放到不允许放置的放置目标上,则drop 事件将不会触发。

要将元素变成有效的放置目标,您可以通过在相应的事件处理程序中调用event.preventDefault() 方法来覆盖dragenterdragover 事件的默认行为。(有关更多信息,请参阅示例部分)

使用 dataTransfer 对象传输数据

要传输拖放操作中的数据,您需要使用dataTransfer 对象。

dataTransfer 对象是事件的一个属性。它允许您将数据从拖动的元素传输到放置目标。

dataTransfer 对象有两种方法:setData()getData()

setData() 允许您将拖动操作的数据设置为指定的格式和数据

dataTransfer.setData(format, data)Code language: CSS (css)

格式可以是text/plaintext/uri-list。数据可以是表示要添加到拖动对象的字符串。

getData() 方法检索由 setData() 方法存储的拖动数据。

getData() 接受一个参数

dataTransfer(format)

格式可以是text/plaintext/uri-listgetData() 返回由 setData() 方法存储的字符串,或者如果拖动操作不包含数据,则返回空字符串。

JavaScript 拖放示例

我们将开发以下简单的拖放应用程序 来演示 JavaScript 拖放 API

创建项目结构

首先,创建一个名为drag-n-drop-basics 的新文件夹。在这个文件夹中,创建两个子文件夹,分别称为cssjs

其次,在 js 文件夹中创建一个名为 app.js 的新文件,在 css 文件夹中创建一个名为 style.css 的文件,并在 drag-n-drop-basics 文件夹中创建一个名为 index.html 的文件。

第三,将指向style.css 的链接和指向 app.js 的脚本标签放在 index.html 文件中,如下所示

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>JavaScript - Drag and Drop Demo</title>
    <link rel="stylesheet" href="css/style.css">
</head>

<body>
   
    <script src="js/app.js"></script>
</body>

</html>Code language: HTML, XML (xml)

对于 CSS,您可以从此处获取

构建 index.html 文件

将以下代码放在 index.html 文件中

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>JavaScript - Drag and Drop Demo</title>
    <link rel="stylesheet" href="css/style.css">
</head>

<body>
    <div class="container">
        <h1>JavaScript - Drag and Drop</h1>
        <div class="drop-targets">
            <div class="box">
                <div class="item" id="item">
                </div>
            </div>
            <div class="box"></div>
            <div class="box"></div>
        </div>
    </div>
    <script src="js/app.js"></script>
</body>

</html>Code language: HTML, XML (xml)

在这个 index.html 文件中,我们使用了 .container 元素来对齐标题和 drop-targets 元素。

在 drop-targets 元素中,我们放置了三个具有相同类 boxdiv 元素。我们还在第一个盒子中放置了另一个具有类 item 的 div 元素。

如果您打开 index.html 并尝试拖动黄色方块,您会看到光标指示您无法拖动。

要使元素可拖放,您需要将draggable 属性及其值为true 添加到其 HTML 标签中,如下所示

<div class="item" id="item" draggable="true">Code language: JavaScript (javascript)

现在,如果您保存 index.html,在浏览器中再次打开它,您会看到您可以拖动 item 元素,如下所示

处理可拖放元素上的事件

style.css 文件具有.hide 类,该类可以隐藏元素。

.hide {
    display: none;
}Code language: CSS (css)

app.js 文件中,您需要添加以下代码

// select the item element
const item = document.querySelector('.item');

// attach the dragstart event handler
item.addEventListener('dragstart', dragStart);

// handle the dragstart

function dragStart(e) {
   console.log('drag starts...');
}
Code language: JavaScript (javascript)

工作原理

  • 首先,使用 querySelector() 选择可拖放元素。
  • 其次,将 dragstart 事件处理程序附加到可拖放元素。
  • 第三,定义 dragStart() 函数来处理 dragstart 事件。

如果您打开 index.html 文件并开始拖动可拖放元素,您会在控制台中看到 drag starts... 消息。

dragStart 事件处理程序中,您需要存储可拖放元素的id。您还需要隐藏它。

function dragStart(e) {
    e.dataTransfer.setData('text/plain', e.target.id);
    e.target.classList.add('hide');
}Code language: JavaScript (javascript)

如果您拖动元素,您会看到它在您开始拖动时会消失。

要解决此问题,您可以使用 setTimeout() 函数

function dragStart(e) {
    e.dataTransfer.setData('text/plain', e.target.id);
    setTimeout(() => {
        e.target.classList.add('hide');
    }, 0);

}Code language: JavaScript (javascript)

现在,您可以将可拖放元素拖出其原始位置。

处理放置目标上的事件

style.css 文件还有一个名为 .drag-over 的 CSS 类,该类可以将放置目标的边框样式更改为虚线红色。

.drag-over {
    border: dashed 3px red;
}
Code language: CSS (css)

在 app.js 中,您需要选择放置目标元素,并处理这些元素的 dragenterdragoverdragleavedrop 事件。

const boxes = document.querySelectorAll('.box');

boxes.forEach(box => {
    box.addEventListener('dragenter', dragEnter)
    box.addEventListener('dragover', dragOver);
    box.addEventListener('dragleave', dragLeave);
    box.addEventListener('drop', drop);
});

function dragEnter(e) {
}

function dragOver(e) {
}

function dragLeave(e) {
}

function drop(e) {
}
Code language: JavaScript (javascript)

放置目标的边框样式应在 dragenterdragover 事件发生时更改。在 dragleavedrop 事件发生时,它应恢复样式。

为此,您需要向放置目标添加和删除 drag-over 类,如下所示

function dragEnter(e) {
    e.target.classList.add('drag-over');
}

function dragOver(e) {
    e.target.classList.add('drag-over');
}

function dragLeave(e) {
    e.target.classList.remove('drag-over');
}

function drop(e) {
    e.target.classList.remove('drag-over');

}Code language: JavaScript (javascript)

现在,如果您将可拖放元素拖放到另一个放置目标,您会看到放置目标的边框会发生变化,如下面的图片所示。

要使放置目标有效,您需要在 dragenterdragover 事件处理程序中调用 event.preventDefault(),如下所示

function dragEnter(e) {
    e.preventDefault();
    e.target.classList.add('drag-over');
}

function dragOver(e) {
    e.preventDefault();
    e.target.classList.add('drag-over');
}Code language: JavaScript (javascript)

如果您不这样做,drop 事件将永远不会触发,因为 div 元素默认不是有效的放置目标。

如果您将可拖放元素拖放到放置目标,您会看到光标会发生变化,表示您可以放置元素。

现在,如果您放置 item 元素,您会看到它会立即消失。

要解决此问题,您需要添加对 drop 事件的处理。

  • 首先,使用 dataTransfer 对象的 getData() 方法获取可拖放元素的 id
  • 其次,将可拖放元素作为子元素附加到放置目标元素。
  • 第三,从可拖放元素中删除 hide 类。

以下代码显示了完整的 drop 事件处理程序

function drop(e) {
    e.target.classList.remove('drag-over');

    // get the draggable element
    const id = e.dataTransfer.getData('text/plain');
    const draggable = document.getElementById(id);

    // add it to the drop target
    e.target.appendChild(draggable);

    // display the draggable element
    draggable.classList.remove('hide');
}Code language: JavaScript (javascript)

如果您现在拖放可拖放元素,它应该按预期工作。

以下是完整的 app.js 文件

/* draggable element */
const item = document.querySelector('.item');

item.addEventListener('dragstart', dragStart);

function dragStart(e) {
    e.dataTransfer.setData('text/plain', e.target.id);
    setTimeout(() => {
        e.target.classList.add('hide');
    }, 0);
}


/* drop targets */
const boxes = document.querySelectorAll('.box');

boxes.forEach(box => {
    box.addEventListener('dragenter', dragEnter)
    box.addEventListener('dragover', dragOver);
    box.addEventListener('dragleave', dragLeave);
    box.addEventListener('drop', drop);
});


function dragEnter(e) {
    e.preventDefault();
    e.target.classList.add('drag-over');
}

function dragOver(e) {
    e.preventDefault();
    e.target.classList.add('drag-over');
}

function dragLeave(e) {
    e.target.classList.remove('drag-over');
}

function drop(e) {
    e.target.classList.remove('drag-over');

    // get the draggable element
    const id = e.dataTransfer.getData('text/plain');
    const draggable = document.getElementById(id);

    // add it to the drop target
    e.target.appendChild(draggable);

    // display the draggable element
    draggable.classList.remove('hide');
}Code language: JavaScript (javascript)

以及演示的链接

概要

  • draggable 属性及其值为 true 添加到元素,以使其可拖放。
  • dragstartdragdragend 事件将在可拖放元素上触发。
  • dragenterdragoverdragleavedrop 事件将在放置目标上触发。
  • dragenterdragover 事件处理程序上调用 event.preventDefault(),以使元素成为有效的放置目标。
  • 使用 event.dataTransfer 对象及其 setData()getData() 方法在拖放操作中传输数据。
本教程是否有用?