如何在HTML5中拖放元素以及如何使用JavaScript可排序库

138 阅读9分钟

简介

挑选一个项目或一大块文本,将其移动*(拖动*),然后将其放置*(丢弃*)到另一个位置的行为被称为 拖放功能.

大多数浏览器都默认使文本选择、图片和链接可以拖动。例如,如果您在任何网站上拖动图片或基于图片的标识,就会出现一个*"幽灵图片*"(这对 SVG 不起作用,因为那些不是图片)。

**注意:**要使其他类型的内容可以拖动,你需要使用HTML5拖放(DnD)API或一个外部JavaScript库

在本实践指南中,我们将看看如何通过使用拖放 (DnD) API 和外部 JavaScript 库sortable 来实现 JavaScript 中的拖放

大受欢迎的看板Trello利用拖放技术,使卡片从一个列表移动到另一个列表变得更容易!在本指南中,我们将介绍如何使用拖放技术。在本指南中,我们将构建非常类似的东西。

使用 HTML5 拖放 API+

要在传统的HTML4中实现拖放功能,开发人员必须利用困难的JavaScript编程或其他JavaScript框架,如jQuery等,但HTML5引入了拖放(DnD)API,为浏览器提供原生的DnD支持,使之更容易编码

所有主要的浏览器,包括Chrome、Firefox 3.5和Safari 4,都支持这个HTML 5 DnD。

我们可以使用API使我们网站上的任何元素都可以拖动。通过鼠标,用户可以挑选可拖动的项目,将其拖到可拖动的元素上,然后通过释放鼠标按钮将其放下。这既利用了DOM事件范式,也利用了拖放事件。

**注意:**在拖动操作过程中会触发几种事件类型,某些事件,如dragdragover 事件,可能会触发多次。

拖放事件

在拖放程序的不同阶段会触发一些事件。

  • **dragstart。**当用户开始拖动项目时,该事件发生。
  • **dragenter。**拖动时,当鼠标第一次移到目标元素上时,会触发此事件。
  • **dragover。**当拖动时,当鼠标被拖过一个元素时,这个事件被触发。在监听过程中发生的过程经常与 dragenter 事件相同。
  • **dragleave。**当鼠标在拖动时离开一个元素时,这个事件被触发。
  • **拖动。**当鼠标在拖动项目时被移动时,这个事件被触发。
  • **drop。**在拖动操作完成时,drop 事件在发生下降的元素上被触发。一个监听器将负责获取被拖动的数据,并将其放在下降的地方。
  • **dragend。**当用户在拖动一个项目时释放鼠标按钮,这个事件就会发生。

开始使用

让我们建立一个简单的Trello板副本吧其结果看起来会是这样的。

trello kanban board clone with vanilla javascript

创建项目和初始标记

让我们在HTML中创建基本结构--一个container ,其中有几个column ,作为任务的列表。例如,第一个列表,对应于*"所有任务 "*列,最初有所有的任务,我们可以将其拖放到其他列。

<div class="container">
    <div class="column">
        <h1>All Tasks</h1>
        <div class="item">Wash Clothes</div>
        <div class="item">Meeting at 9AM</div>
        <div class="item">Fix workshop</div>
        <div class="item">Visit the zoo</div>
    </div>
    <div class="column">
        <h1>In progress</h1>
    </div>
    <div class="column">
        <h1>Paused</h1>
    </div>
    <div class="column">
        <h1>Under Review</h1>
    </div>
    <div class="column">
        <h1>Completed</h1>
    </div>
</div>

让我们为containercolumns和items添加一些基本的样式。

.container{
    font-family: "Trebuchet MS", sans-serif;
    display: flex;
    gap: 30px;
}
.column{
    flex-basis: 20%;
    background: #ddd;
    min-height: 90vh;
    padding: 20px;
    border-radius: 10px;
}
.column h1{
    text-align: center;
    font-size: 22px;
}
.item{
    background: #fff;
    margin: 20px;
    padding: 20px;
    border-radius: 3px;
    cursor: pointer;
}
.invisible{
    display: none;
}

这个页面看起来应该是这样的。

vanilla js kanban board

使一个对象可拖动

不过,这些对象还不是可拖动的。它们只是在那里!要使一个对象可拖动--我们将其draggable 属性设置为true 。你网站上的任何东西,包括照片、文件、链接和文件,都可以被拖动!

让我们在我们的item 元素上设置draggable="true"

<div class="column">
    <h1>All Tasks</h1>
    <div class="item" draggable="true">Wash Clothes</div>
    <div class="item" draggable="true">Meeting at 9AM</div>
    <div class="item" draggable="true">Fix workshop</div>
    <div class="item" draggable="true">Visit the zoo</div>
</div>

现在,这些元素是可拖动的,它们可以发出拖动事件!让我们设置事件监听器来接收这些事件,并对这些事件作出反应。

用JavaScript处理拖放事件

让我们收集所有我们想要实现拖放的项目和列。我们可以使用document.querySelectorAll() DOM选择器轻松地收集它们。这将产生一个NodeList 数组,我们可以对其进行循环操作,对每一个项目/列进行操作。

const items = document.querySelectorAll('.item')
const columns = document.querySelectorAll('.column')

当然--如果你没有一个项目的列表可以操作,你可以单独选择它们

让我们循环浏览这些元素,并为每个元素添加一个事件监听器。我们将为dragstartdragend 事件添加一个事件监听器,并在它们发生时运行函数。

items.forEach(item => {
    item.addEventListener('dragstart', dragStart)
    item.addEventListener('dragend', dragEnd)
});

dragStart() 将在每个 事件中运行,而 将在每个 事件中运行。'dragstart' dragEnd() 'dragend'

**注意:**这些函数可以用来添加造型,以便在用户拖动特定项目并将其放下时有更好的视觉互动性,例如你正在移动的卡片的平滑动画。

让我们通过只记录信息来测试一下这个功能。

function dragStart() {
    console.log('drag started');
}
function dragEnd() {
    console.log('drag ended');
}

draggable elements with javascript

很好!当一个元素被拖动的时候--事件被触发了。现在,让我们不只是记录消息,而是给卡片应用一个类名。让我们先把被移动的卡片变得不可见,这样它就会从原来的列表中消失。我们稍后会对被拖动的元素进行样式化处理,并为其添加逻辑,使其出现在新的列表中。

没有必要让元素消失;你也可以通过调整不透明度让它变淡,以说明它正从一个位置被拖到另一个位置。请自由地发挥创意吧

让我们修改一下dragStart() 的功能。

function dragStart() {
    console.log('drag started');
    setTimeout(() => this.className = 'invisible', 0)
}

现在--我们不只是与卡片互动。我们还想与每一列互动,以接受新的卡片,并从旧的一列移除卡片。为此,我们要在列上的事件发生时运行方法,就像对项目一样!让我们循环并添加事件。

让我们在columns 中循环并添加事件监听器。

columns.forEach(column => {
    column.addEventListener('dragover', dragOver);
    column.addEventListener('dragenter', dragEnter);
    column.addEventListener('dragleave', dragLeave);
    column.addEventListener('drop', dragDrop);
});

让我们测试一下这些事件监听器。

function dragOver() {
    console.log('drag over');
}
function dragEnter() {
    console.log('drag entered');
}
function dragLeave() {
    console.log('drag left');
}
function dragDrop() {
    console.log('drag dropped');
}

当你在浏览器中查看时,当你拖动项目时,它们应该消失,当你用项目 "跨入 "一个新的列时,控制台日志应该弹出。

dragging events in javascript

**注意:**如果你仔细检查控制台区域,你会发现dragDrop() 方法并没有记录信息。为了使其发挥作用,你必须禁用dragOver() 方法中的默认行为。

function dragOver(e) {
  e.preventDefault()
  console.log('drag over');
}

你将能够注意到,现在 "drag dropped "被记录下来了,当你放下物品时。

这些就是我们需要的所有事件了!现在,我们只想实现删除项目的逻辑,在项目被丢弃时将其添加到新的列中,等等。由于我们在一个时间点上只拖动一个项目,让我们为它创建一个全局变量。由于我们将普遍改变引用,它将是一个let ,而不是const

当开始拖动时--我们将把this 元素设置为dragItem ,把它添加到我们要拖动的列中,并把它设置为null

let dragItem = null;

function dragStart() {
    console.log('drag started');
    dragItem = this;
    setTimeout(() => this.className = 'invisible', 0)
}

function dragEnd() {
    console.log('drag ended');
  	this.className = 'item'
  	dragItem = null;
}

function dragDrop() {
    console.log('drag dropped');
    this.append(dragItem);
}

**注意:**每个发出事件的元素都可以通过this 关键字,在事件被触发时调用的方法内访问。

就这样--我们现在可以把卡片从一列拖到另一列。

drag and drop in vanilla javascript

我们不需要使用所有可用的事件来实现这个工作--它们被添加了,可以用来进一步使这个过程风格化。

有一点需要注意的是--我们依次将元素添加到每一列的末尾,始终如此,因为我们没有跟踪它们的相对位置,只是在需要时调用append() 。使用Sortable库可以很容易地解决这个问题!

使用SortableJS实现拖放功能

Sortable是一个轻量级和简单的JavaScript模块,它使用原生的HTML5拖放API对对象列表进行排序,就像我们一样它兼容所有当代浏览器和触摸设备。

它非常适合列表对元素进行排序,并允许你在一列中的不同位置拖放元素,而不仅仅是在列之间。这对我们的应用程序来说将是一个很好的补充!

事实上--使用Sortable,我们可以通过一组列来完全自动化整个过程,每个列都可以共享项目。

Sortable可以通过CDN导入。

<script src="https://cdnjs.cloudflare.com/ajax/libs/Sortable/1.14.0/Sortable.min.js" integrity="sha512-zYXldzJsDrNKV+odAwFYiDXV2Cy37cwizT+NkuiPGsa9X1dOz04eHvUWVuxaJ299GvcJT31ug2zO4itXBjFx4w==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

或通过NPM安装。

$ npm install sortablejs --save

使用Sortable就像在一个给定的HTML元素上实例化一个Sortable 对象一样简单。

const column = document.querySelector('.column');

new Sortable(column, {
    animation: 150,
    ghostClass: 'blue-background-class'
});

你可以设置相当数量的属性来定制这个过程--其中两个我们已经使用过了。animation 是动画时间,以毫秒为单位,而ghostClass 可用于对拖动元素的 "幽灵 "进行风格化处理。这使得拖动一个元素的体验更加美好。

让我们回到我们的Trello例子,并将Sortable应用到任务中!这需要我们使用。它要求我们使用list-group-item 类而不是item

<div class="container">
    <div class="column">
        <h1>All Tasks</h1>
        <div class="list-group-item" draggable="true">Wash Clothes</div>
        <div class="list-group-item" draggable="true">Take a stroll outside</div>
        <div class="list-group-item" draggable="true">Design Thumbnail</div>
        <div class="list-group-item" draggable="true">Attend Meeting</div>
        <div class="list-group-item" draggable="true">Fix workshop</div>
        <div class="list-group-item" draggable="true">Visit the zoo</div>
    </div>
    <div class="column">
        <h1>In progress</h1>
    </div>
    <div class="column">
        <h1>Paused</h1>
    </div>
    <div class="column">
        <h1>Under Review</h1>
    </div>
    <div class="column">
        <h1>Completed</h1>
    </div>
</div>

让我们应用与之前相同的样式。

.container {
    font-family: "Trebuchet MS", sans-serif;
    display: flex;
    gap: 30px;
}
.column {
    flex-basis: 20%;
    background: #ddd;
    min-height: 90vh;
    padding: 5px;
    border-radius: 10px;
}
.column h1 {
    text-align: center;
    font-size: 22px;
}
.list-group-item {
    background: #fff;
    margin: 20px;
    padding: 20px;
    border-radius: 5px;
    cursor: pointer;
}

现在,让我们为页面上的每一列实例化一个Sortable ,将它们的group 设置为"shared" ,这样卡片就可以在列之间共享。

const columns = document.querySelectorAll(".column");

columns.forEach((column) => {
    new Sortable(column, {
        group: "shared",
        animation: 150,
        ghostClass: "blue-background-class"
    });
});

这就是了!剩下的就交给Sortable来处理了。

drag and drop in javascript with sortableJS

结论

在这篇文章中,我们看了如何在HTML5中拖放元素,也看了如何使用JavaScript可排序库。