HTML 5拖放API:教程

2020年12月30日11:15:13 发表评论 35 次浏览

本文概述

在本教程中, 我们将了解HTML 5拖放API。拖放API将可拖动元素添加到HTML, 使开发人员可以构建包含丰富UI元素的应用程序, 这些UI元素可以从一个位置拖动到另一个位置。

要了解HTML 5的拖放功能, 我们将使用以下内容构建一个简单的看板Vue.js.

看板板是一种项目管理工具, 使用户可以从头到尾直观地管理项目。 Trello, Pivotal Tracker和Jira等工具是看板。

先决条件

对于本教程, 你需要以下内容:

  1. HTML和JavaScript的基础知识
  2. Vue.js 2.x的基本知识
  3. Vue CLI安装在计算机上的4(或更高版本)
  4. Node.js8.0.0和npm安装在你的机器上

设置看板

看板将是Vue CLI应用程序。要创建一个新的应用程序, 请运行以下命令:

vue create kanban-board

当提示你选择一个预设时, 选择仅包含的默认预设巴别塔和ESlint.

完成安装后, 删除默认组件你好, 世界, 由Vue在安装过程中创建。另外, 修改应用程式组件为空, 仅包含裸组件模板:

<template> <div></div> </template>
<script>
export default {
  name: 'App', components: {}, };
</script>
<style></style>

我们将使用Bootstrap样式, 但我们只需要引导CSS CDN。将其添加到头的部分public / index.html.

<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css"
    integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">
    <title><%= htmlWebpackPlugin.options.title %></title>
  </head>

在看板中构建UI组件

看板构建后的外观如下:

内置看板

我们为制作了一个自定义演示.
不完全是。点击这里查看.

HTML 5拖放API:教程1

通常, 看板有列和卡片。卡是要执行的单个项目或任务, 并且列显示特定卡的状态。

我们将创建三个Vue组件:一个用于列, 一个用于卡, 另一个用于创建新卡。

创建卡组件

我们将创建的第一个组件是卡组件。首先, 创建一个新文件, 卡牌, 在里面/零件目录。

将以下内容添加到新创建的组件中:

<template>
  <div class="card">
    <div class="card-body">A Sample Card</div>
  </div>
</template>
<script>
export default {};
</script>
<style scoped>
div.card {
  margin-bottom: 15px;
  box-shadow: 0 0 5px #cccccc;
  transition: all ease 300ms;
  background: #fdfdfd;
}
div.card:hover {
  box-shadow: 0 0 10px #aaaaaa;
  background: #ffffff;
}
</style>

这将创建并设置卡片组件的样式。我们尚未向该组件添加任何可拖动功能, 因为这只是组件的框架。

添加卡组件

顾名思义, 此组件将负责创建新卡并将其添加到列中。

创建一个AddCard.vue文件在/组件目录并添加以下内容:

<template>
  <div class="">
    <button
      class="btn btn-sm btn-info w-100"
      v-if="!inAddMode"
      @click="inAddMode = true"
    >
      Add Card
    </button>
    <form action="#" class="card p-3" ref="form" v-else>
      <div class="form-group">
        <input
          type="text"
          name="title"
          id="title"
          class="form-control"
          placeholder="Something interesting..."
          v-model="cardData"
        />
      </div>
      <div class="d-flex justify-content-center">
        <button type="submit" class="btn w-50 btn-primary mr-3">Save</button>
        <button type="reset" class="btn w-50 btn-danger">
          Cancel
        </button>
      </div>
    </form>
  </div>
</template>
<script>
export default {
  data() {
    return {
      inAddMode: false, cardData: '', };
  }, methods: {}, };
</script>
<style></style>

此功能将在接下来的部分中构建。

创建列组件

这是我们将创建的最后一个组件。该组件将显示卡列表, 还将包含"添加卡组件", 以便可以将新卡直接创建到列中。

创建一个列值组件目录中的文件, 并添加以下代码:

<template>
  <div class="col-md-3 card column" ref="column">
    <header class="card-header">
      <h3 class="col">Column Name</h3>
    </header>
    <div class="card-list"></div>
  </div>
</template>
<script>
export default {};
</script>
<style scoped>
div.column {
  padding: 0;
  padding-bottom: 15px;
  margin: 0 15px;
  box-shadow: 0 0 10px #cccccc;
}
div.card-list {
  padding: 0 15px;
}
header {
  margin-bottom: 10px;
}
header h3 {
  text-align: center;
}
</style>

在添加功能并组成组件以使其正常工作之前, 这里概述一下拖放功能在浏览器中的工作方式。

什么是HTML 5拖放API?

当用户将鼠标移到可拖动元素上时, 拖动操作开始, 然后将元素移到启用拖放的元素上。

默认情况下, 唯一可拖动的HTML元素是图像和链接。要使其他元素可拖动, 你需要显式创建功能, 方法是将draggable属性添加到该元素, 或者在JavaScript中选择该元素, 并将draggable属性设置为true.

在元素上将draggable设置为true后, 你会注意到draggable属性已添加到元素。

<!-- Making an element draggable in HTML -->
<div draggable="true">This is a draggable div in HTML</div>

<script>
// Making an element draggable in javascript
const div = document.querySelector('div');
div.draggable = true;
</script>

拖动元素的目的是将数据从页面的一个部分传输到另一部分。

对于图像, 要传输的数据是图像URL或图像的base 64表示形式。对于链接, 正在传输的数据是URL。可以将链接移动到浏览器的URL栏中, 以使浏览器导航到该URL。

同样, 如果没有数据传输的能力, 拖动元素将毫无用处。将通过拖动操作传输的数据存储在拖动数据存储区中。可以通过数据传输API, 它提供了一种在拖放操作期间存储和访问数据的方法。

的数据传输对象用于此目的, 因为它提供了添加要通过拖放传输的项目的位置。

可以在拖动操作开始时将数据添加到拖动数据存储中(当拖动开始事件被调度), 并且只有在放置操作完成后(下降事件已调度)。

从拖动元素到拖放元素之间, 将在被拖动的元素上触发两个事件:拖动开始和拖曳, 删除元素后。

无法将可拖动元素拖放到任何地方。正如我们需要显式使元素可拖动以允许放置元素一样, 它也需要启用放置。

要启用元素拖放功能, 我们需要在拖曳事件并阻止默认的浏览器操作。

<!-- Make a section drop-enabled -->
<section class="section"></section>
<script>
const section = document.querySelector('.section');
section.addEventListener('dragover', (e) => {
  e.preventDefault();
});
</script>

将元素拖动到启用拖放的元素上时, 将在启用拖放的元素上触发以下事件:

德拉贡特

:当元素被拖放到启用放置的元素上时, 将触发一次

拖曳

:只要元素保留在启用了drop的元素上, 就会连续触发

下降

:在将拖动的元素拖放到启用拖放的元素上之后触发。

重要的是要注意, 仅在触发放置事件时才能访问存储在DataTransfer对象中的数据, 而不能在Dragenter或Dragover上访问。有关更多信息, 请参见此处。

组成组件

在向组件添加拖放功能之前, 我们先讨论一下应用程式状态.

此处的应用程序状态将存储在应用程式组件, 然后可以向下传递到柱组件作为道具。的柱另一方面, 组件会将所需的道具传递给卡组件渲染时。

修改应用程序反映状态和组件组成:

// App.vue
<template>
  <div class="container-fluid">
    <h2 class="m-5">
      Vue Kanban Board
    </h2>
    <div class="row justify-content-center">
      <Column
        v-for="(column, index) in columns"
        :column="column"
        :key="index"
      />
    </div>
  </div>
</template>
<script>
import Column from './components/Column';
export default {
  name: 'App', components: {
    Column, }, data() {
    return {
      columns: [
        {
          name: 'TO-DO', cards: [
            {
              value: 'Prepare breakfast', }, {
              value: 'Go to the market', }, {
              value: 'Do the laundry', }, ], }, {
          name: 'In Progress', cards: [], }, {
          name: 'Done', cards: [], }, ], };
  }, };
</script>
<style>
h2 {
  text-align: center;
}
</style>

在这里, 我们导入柱组件, 并以状态存储为状态进行循环列, 我们将每一列的数据传递给柱零件。在这种情况下, 只有三列:"待办事项", "进行中"和"完成", 每列都有一张纸牌阵列。

接下来, 更新柱接收道具并适当显示的组件:

// Column.vue
<template>
  <div class="col-md-3 card column" ref="column">
    <header class="card-header">
      <h3 class="col">{{ column.name }}</h3>
      <AddCard />
    </header>
    <div class="card-list">
      <Card v-for="(card, index) in column.cards" :key="index" :card="card" />
    </div>
  </div>
</template>
<script>
import Card from './Card';
import AddCard from './AddCard';
export default {
  name: 'Column', components: {
    Card, AddCard, }, props: {
    column: {
      type: Object, required: true, }, }, };
</script>

...

的柱组件从应用程式组件, 并呈现一个列表卡道具的组件。我们也使用新增卡组件, 因为新卡应该可以直接添加到列中。

最后, 我们更新卡组件以显示从中接收的数据柱.

// Card.vue
<template>
  <div class="card" ref="card">
    <div class="card-body">{{ card.value }}</div>
  </div>
</template>
<script>
export default {
  name: 'Card', props: {
    card: {
      type: Object, required: true, }, }, };
</script>

的卡组件只是从中接收它将需要的所有数据柱并显示它。我们还在此处添加对card元素的引用。通过JavaScript访问card元素时, 这将很有用。

完成上述操作后, 你的应用程序应如下所示:

查看正在进行的看板

添加拖放功能

添加拖放功能的第一步是识别可拖动组件和放置目标。

用户应该能够按照卡片中的活动进度将卡片从一列拖到另一列。因此, 此处的可拖动组件应为卡组件, 而此处的放置目标是柱零件。

使卡片可拖动

要使卡组件可拖动, 我们需要执行以下操作:

  1. 设置可拖动的归因于true
  2. 使用设置要传输的数据数据传输Object

设置可拖动的totrue应当尽早发生, 并且根据Vue救生钩, 安全的位置是在已安装的钩中。将以下内容添加到卡零件:

// Card.vue
<script>
export default {
  name: 'Card', props: {...}, mounted() {
    this.setDraggable();
  }, methods: {
    setDraggable() {
      // Get Card element.
      const card = this.$refs.card;
      card.draggable = true;
      // Setup event listeners.
      card.addEventListener('dragstart', this.handleDragStart);
      card.addEventListener('dragend', this.handleDragEnd);
    }, }, </script>

上面, 我们创建了一个方法setDraggable处理卡组件的可拖动性。

InsetDraggable, 我们从上一节中添加的参考中获取卡, 并将draggable属性设置为true.

我们还将设置事件监听器, 这对于使用以下命令将数据添加到拖动数据存储区将非常有用。数据传输目的。

让我们创建事件监听器来做到这一点。

// Card.vue
<script>
export const CardDataType = 'text/x-kanban-card';

export default {
...
  methods: {
    setDraggable() {...}, handleDragStart(event) {
      const dataTransfer = event.dataTransfer;
      // Set the data to the value of the card which is gotten from props.
      dataTransfer.setData(CardDataType, this.card.value);
      dataTransfer.effectAllowed = 'move';
      // Add visual cues to show that the card is no longer in it's position.
      event.target.style.opacity = 0.2;
    }, handleDragEnd(event) {
      // Return the opacity to normal when the card is dropped.
      event.target.style.opacity = 1;
    }
  }
}
</script>

从"拖放API概述"部分回想起, 仅当拖动开始事件被调度。因此, 我们需要将数据添加到handleDragStart方法。

的数据传输从调度的拖动事件中接收对象, 并使用setData, 我们将在此拖动操作期间要移动的数据设置为从道具接收的卡的值。

设置数据时需要的重要信息是格式。这可以是任何字符串。在我们的情况下, 它设置为文字/ x看板卡。我们正在存储此数据格式并导出它, 因为在柱删除卡后获取数据时的组件。

最后, 减少卡的不透明度0.2以便向用户提供一些反馈, 表明该卡实际上已从其原始位置中拉出。拖动完成后, 我们将不透明度返回到1.

卡现在可以拖动。但是, 由于我们尚未添加放置目标, 因此无法将它们放置在任何地方。让我们做到这一点。

使列下降Ë启用

从拖放API的概述中, 我们需要侦听拖曳事件, 以使列启用拖放。的拖曳将卡拖到列上时将触发事件。

的德拉贡特当卡进入列组件时, 将立即触发事件, 而将卡放入列中后, 将触发放置事件。

因此, 要使卡片掉落到列中, 我们需要侦听这些事件。

首先, 更新柱启用放置的组件。

// Column.vue
<template>...</template>
<script>
import Card { CardDataType } from './Card';
import AddCard from './AddCard';
export default {
  name: 'Column', components: {...}, props: {...}, mounted() {
    this.enableDrop();
  }, methods: {
    enableDrop() {
      const column = this.$refs.column;
      column.addEventListener('dragenter', this.handleDragEnter);
      column.addEventListener('dragover', this.handleDragOver);
      column.addEventListener('drop', this.handleDrop);
    }, /**
     * @param {DragEvent} event
     */
    handleDragEnter(event) {
      if (event.dataTransfer.types.includes[CardDataType]) {
        // Only handle cards.
        event.preventDefault();
      }
    }, handleDragOver(event) {
      // Create a move effect.
      event.dataTransfer.dropEffect = 'move';
      event.preventDefault();
    }, /**
     * @param {DragEvent} event
     */
    handleDrop(event) {
      const data = event.dataTransfer.getData(CardDataType);
      // Emit a card moved event.
      this.$emit('cardMoved', data);
    }, }, };
</script>

在这里, 我们设置了所有必要的事件监听器, 以便在删除柱组件被安装。

在这三个事件中, 第一个被触发的是德拉贡特, 当任何可拖动元素被拖动到列中时立即触发。对于我们的应用程序, 我们只希望将卡片放入一列中, 因此, 在德拉贡特事件中, 我们仅阻止默认数据类型, 包括卡组件中定义的卡数据类型。

在里面拖曳事件, 我们将放置效果设置为移动.

移动显示项目(卡)正在从一个位置移动到另一位置(从一列移动到另一列)。其他效果包括复制, 链接和无。有关这些的更多信息, 请参见MDN。

在放置事件中, 我们获得了从数据传输目的。如果我们没有检查数据类型德拉贡特事件, 此处的数据可能是任意随机的东西, 具体取决于拖动的内容。

但是在这里, 我们确定要传输的数据是卡中所指定的内容。拖动开始的事件卡零件。

接下来, 我们需要更新状态并将卡移动到当前列。因为我们的应用程序状态位于应用程式组件, 我们发出一个卡已移动放置侦听器中的事件, 传递已传输的数据并侦听卡已移动事件中应用程式零件。

要了解有关在Vue中调度自定义事件的信息, 请检查Vue官方文档.

现在, 更新应用程序听卡已移动事件:

// App.vue

<template>
  <div class="container-fluid">
    ...
    <div class="row justify-content-center">
      <Column
        v-for="(column, index) in columns"
        :column="column"
        :key="index"
        @cardMoved="moveCardToColumn($event, column)"
      />
    </div>
  </div>
</template>

<script>
import Column from './components/Column';
export default {
  name: 'App', components: {...}, data() {
    return {...}
  }, methods: {
    moveCardToColumn(data, newColumn) {
      const formerColumn = this.columns.find(column => {
        // Get all the card values in a column.
        const cardValues = column.cards.map((card) => card.value);
        return cardValues.includes(data);
      })
      // Remove card from former column.
      formerColumn.cards = formerColumn.cards.filter(
        (card) => card.value !== data
      );
      // Add card to the new column.
      newColumn.cards.push({ value: data });
    }, }, }
</script>

在这里, 我们正在听卡已移动通过事件@cardMoved, 然后致电moveCardToColumn方法。的卡已移动事件发出一个值(卡数据), 可以通过$事件, 并且我们还传递了放置卡的当前列(这是调度事件的位置)。

的moveCardToColumn函数执行三件事:查找卡先前所在的列, 从该列中删除卡, 然后将卡添加到新列中。

完成看板

祝贺你在本教程中达到这一点!现在已经添加了拖放功能, 剩下的唯一任务就是创建"添加卡"功能。

更新AddCard.vue如下所示:

<template>
  <div class="">
    <button
      class="btn btn-sm btn-info w-100"
      v-if="!inAddMode"
      @click="inAddMode = true"
    >
      Add Card
    </button>
    <form
      action="#"
      class="card p-3"
      @submit.prevent="handleSubmit"
      @reset="handleReset"
      ref="form"
      v-else
    >
      ...
    </form>
  </div>
</template>
<script>
export default {
  data() {
    return {...};
  }, methods: {
    handleSubmit() {
      if (this.cardData.trim()) {
        this.cardData = '';
        this.inAddMode = false;
        this.$emit('newcard', this.cardData.trim());
      }
    }, handleReset() {
      this.cardData = '';
      this.inAddMode = false;
    }, }, };
</script>

我们创建了要在"添加卡片"表格提交或重置时运行的功能。

重置后, 我们清除cardData(在输入字段中键入当前数据), 然后进行设置inAddModetofalse.

提交表单后, 我们还会清除cardData, 这样当添加新项目时, 将不会显示以前的数据, 并且我们还设置了inAddModetofalse并发出一个新卡事件。

请记住, 状态存储在应用程式组件, 以某种方式, 我们需要通知应用程式卡添加的组成部分;因此, 我们需要发出一个事件, 该事件将到达应用程式零件。

的新增卡组件用于柱组件, 所以我们需要听新卡事件中柱零件。

更新柱侦听组件新卡事件。

<template>
  <div class="col-md-3 card column" ref="column">
    <header class="card-header">
      <h3 class="col">{{ column.name }}</h3>
      <AddCard @newcard="$emit('newcard', $event)"></AddCard>
    </header>
    ...
</template>
...

在这里, 我们重新发射新卡事件, 以便它可以起来应用程式组件, 实际操作将在其中发生。

自定义Vue事件不会冒泡, 因此App组件不是直接子组件, 因此将无法侦听AddCard组件中发出的newcard事件。

现在, 更新应用程式处理组件新卡事件:

// App.vue

<template>
  <div class="container-fluid">
    ...
    <div class="row justify-content-center">
      <Column
        v-for="(column, index) in columns"
        :column="column"
        :key="index"
        @cardMoved="moveCardToColumn($event, column)"
        @newcard="handleNewCard($event, column)"
      />
    </div>
  </div>
</template>

<script>
import Column from './components/Column';
export default {
  name: 'App', components: {...}, data() {
    return {...}
  }, methods: {
    moveCardToColumn(data, newColumn) {...}, handleNewCard(data, column) {
      // Add new card to column.
      column.cards.unshift({ value: data });
    }, }, };
</script>

在这里, 我们听新卡从发出的事件柱组件, 然后获取数据, 我们创建一个新卡并将其添加到创建它的列中。

总结

在本文中, 我们介绍了HTML 5拖放API是什么, 如何使用它以及如何在Vue.js应用程序中实现它。

但是, 拖放功能和本教程都可以在任何其他前端框架和原始JavaScript中使用。

你可以找到本文的代码这里.

准确体验用户的Vue应用程序

调试Vue.js应用程序可能会很困难, 尤其是在用户会话中有数十种甚至数百种变体的情况下。如果你有兴趣监视和跟踪生产中所有用户的Vue突变,

尝试notlogy

.

LogRocket仪表板免费试用横幅

https://notlogy.com/signup/

日志火箭就像Web应用程序的DVR一样, 实际上记录了Vue应用程序中发生的所有事情, 包括网络请求, JavaScript错误, 性能问题等等。你可以汇总并报告问题发生时应用程序所处的状态, 而不用猜测为什么会发生问题。

notlogy Vuex插件将Vuex突变记录到notlogy控制台, 为你提供导致错误的原因以及发生问题时应用程序处于何种状态的上下文。

现代化调试Vue应用程序的方式-免费开始监控.

一盏木

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: