JavaScript

UI Implementation for Kanban Boards and Task Management with Swapy [JavaScript]

In this article, we will provide a detailed explanation of “Swapy,” which makes it easy to implement drag-and-drop functionality in JavaScript. Swapy is a handy tool that leverages the HTML5 Drag and Drop API to enable lightweight and intuitive operations. It is particularly useful for building UIs that require visual rearrangement and offers excellent performance since it can be implemented without relying on special libraries.

This article will first introduce the basic usage of Swapy, followed by more advanced examples with server integration, drag and drop across multiple zones, and the addition of animations, all with accompanying code. This content is perfect for those looking to create task management tools or Kanban boards, as well as for anyone aiming to build visually rich user interfaces.

What is Swapy?

Swapy is a feature that allows elements such as cards or panels to be freely rearranged through drag-and-drop using JavaScript. It utilizes the HTML5 Drag and Drop API, enabling a lightweight and simple implementation without relying on special libraries. With Swapy, you can easily create interfaces where users can visually manipulate elements.

For example, you can effortlessly build systems like task management tools or Kanban boards, where users can freely move and rearrange elements.

 

Main Features of Swapy

Swapy offers simple yet effective drag-and-drop functionalities, including the following features:

  • Intuitive Drag-and-Drop Operations
    Users can simply drag elements and drop them at their desired positions to change the order or move them to different zones. This significantly enhances the usability of the UI.
  • Utilization of the HTML5 Drag and Drop API
    Swapy leverages the native HTML5 functionalities, allowing for the implementation of lightweight and efficient code without relying on external libraries.
  • Easily Customizable
    By using event listeners, you can customize the behavior during dragging and dropping, enabling flexible movements tailored to your user interface.

 

Benefits of Swapy

Utilizing Swapy offers several advantages:

  • Improved User Experience (UX)
    With the ability to visually rearrange elements, Swapy provides an easy-to-use and interactive user experience. It is especially ideal for applications with dynamic layouts.
  • Lightweight and Efficient Codebase
    Swapy is simple and performs well. By using the HTML5 API, it avoids redundant code and unnecessary library additions, keeping the overall codebase lightweight.
  • Highly Extensible Design
    Swapy goes beyond basic rearranging features, enabling flexible functionalities such as data synchronization with servers and dropping elements into multiple zones. It also allows for adding animations and custom events to create richer interfaces.

 

Examples of Using Swapy and Code Explanation

See the Pen
Untitled
by Rin (@rinblog0408)
on CodePen.

First, let’s introduce a basic implementation example of Swapy. Below is a simple sample code for rearranging cards using drag-and-drop.

HTML

<div class="container">
  <div class="card" draggable="true">Card 1</div>
  <div class="card" draggable="true">Card 2</div>
  <div class="card" draggable="true">Card 3</div>
</div>

CSS

.container {
  display: flex;
  gap: 10px;
}

.card {
  width: 100px;
  height: 100px;
  background-color: #f2f2f2;
  text-align: center;
  line-height: 100px;
  border: 1px solid #ccc;
  cursor: grab;
}

JavaScript

const cards = document.querySelectorAll('.card');
let draggedCard = null;

cards.forEach(card => {
  card.addEventListener('dragstart', function (e) {
    draggedCard = card;
    setTimeout(() => {
      card.style.display = 'none';
    }, 0);
  });

  card.addEventListener('dragend', function () {
    setTimeout(() => {
      draggedCard.style.display = 'block';
      draggedCard = null;
    }, 0);
  });

  card.addEventListener('dragover', function (e) {
    e.preventDefault();
  });

  card.addEventListener('drop', function (e) {
    e.preventDefault();
    if (draggedCard !== this) {
      const allCards = Array.from(document.querySelectorAll('.card'));
      const droppedIndex = allCards.indexOf(this);
      const draggedIndex = allCards.indexOf(draggedCard);
      
      if (droppedIndex > draggedIndex) {
        this.parentNode.insertBefore(draggedCard, this.nextSibling);
      } else {
        this.parentNode.insertBefore(draggedCard, this);
      }
    }
  });
});

 

In this code, the cards can be dragged and moved before or after another card. Simple event listeners are used to handle the drag start, determine the drop position, and perform the insertion process.

 

Advanced Examples

The drag-and-drop functionality with Swapy can be applied in various ways. Here are three specific examples of advanced applications:

1. Implementing Multiple Drop Zones

See the Pen
Swapy02
by Rin (@rinblog0408)
on CodePen.

In a task management app, for example, you can implement a system where elements can be dragged and moved across multiple columns. The code below is an example of freely moving cards between the “To Do,” “In Progress,” and “Done” columns.

HTML

<div class="container">
  <div class="column" id="todo">
    <h3>To Do</h3>
    <div class="card" draggable="true">Task 1</div>
    <div class="card" draggable="true">Task 2</div>
  </div>

  <div class="column" id="inprogress">
    <h3>In Progress</h3>
  </div>

  <div class="column" id="done">
    <h3>Done</h3>
  </div>
</div>
  • .container: This is the parent element that arranges all the columns side by side.
  • .column: Represents each column (To Do, In Progress, Done). Each column contains draggable task cards.

CSS

body {
  font-family: Arial, sans-serif;
  display: flex;
  flex-direction: column;
  align-items: center;
  margin: 20px;
}

.container {
  display: flex;
}

.column {
  width: 200px;
  min-height: 300px;
  background-color: #f0f0f0;
  border: 2px solid #ccc;
  padding: 10px;
  margin: 0 10px;
}

.card {
  background-color: #ffffff;
  border: 1px solid #ccc;
  padding: 10px;
  margin: 10px 0;
  text-align: center;
  cursor: grab;
  transition: transform 0.2s ease;
}

.card.dragging {
  opacity: 0.5;
  transform: scale(1.1);
}

JavaScript

const cards = document.querySelectorAll('.card');
const columns = document.querySelectorAll('.column');

let draggedCard = null;

cards.forEach(card => {
  card.addEventListener('dragstart', function () {
    draggedCard = card; // ドラッグが開始されたカードを設定
    setTimeout(() => {
      card.classList.add('dragging'); // ドラッグ中のカードにクラスを追加
    }, 0);
  });

  card.addEventListener('dragend', function () {
    setTimeout(() => {
      card.classList.remove('dragging'); // ドラッグが終了したらクラスを削除
      draggedCard = null; // ドラッグしたカードをnullに設定
    }, 0);
  });
});

columns.forEach(column => {
  column.addEventListener('dragover', function (e) {
    e.preventDefault(); // ドラッグオーバーを許可
  });

  column.addEventListener('drop', function (e) {
    e.preventDefault(); // ドロップイベントのデフォルト動作を防止
    if (draggedCard) {
      this.appendChild(draggedCard); // ドロップゾーンにカードを追加
      draggedCard = null; // ドラッグしたカードをnullに設定
    }
  });
});
  • Drag Start (dragstart): When a card is dragged, it is set to the draggedCard variable, and the dragging style is applied.
  • Drag End (dragend): When the drag ends, the dragging style is removed, and draggedCard is set to null.
  • Drag Over (dragover): When a drag is over a column, the default behavior is prevented to allow dropping.
  • Drop (drop): When a drop event occurs, if draggedCard exists, it adds the card to the current column.

 

2. Adding Animations

See the Pen
Swapy03
by Rin (@rinblog0408)
on CodePen.

When moving a card, a smooth animation is added to enhance the visual effect.

HTML

<div class="container">
  <div class="column" id="todo">
    <h3>To Do</h3>
    <div class="card" draggable="true">Task 1</div>
    <div class="card" draggable="true">Task 2</div>
  </div>

  <div class="column" id="inprogress">
    <h3>In Progress</h3>
  </div>

  <div class="column" id="done">
    <h3>Done</h3>
  </div>
</div>

CSS

body {
  font-family: Arial, sans-serif;
  display: flex;
  flex-direction: column;
  align-items: center;
  margin: 20px;
}

.container {
  display: flex;
}

.column {
  width: 200px;
  min-height: 300px;
  background-color: #f0f0f0;
  border: 2px solid #ccc;
  padding: 10px;
  margin: 0 10px;
  transition: background-color 0.3s ease; /* カラムの背景色のアニメーション */
}

.column:hover {
  background-color: #e0e0e0; /* ホバー時の背景色 */
}

.card {
  background-color: #ffffff;
  border: 1px solid #ccc;
  padding: 10px;
  margin: 10px 0;
  text-align: center;
  cursor: grab;
  transition: transform 0.2s ease; /* カードの変形に対するアニメーション */
}

.card.dragging {
  opacity: 0.5;
  transform: scale(1.1);
  transition: opacity 0.2s ease, transform 0.2s ease; /* ドラッグ中のアニメーション */
}

.card.move {
  transition: transform 0.5s ease; /* カードの移動に対するアニメーション */
}
  • transition: The transition property in CSS is used to set the animation effect when the card transforms. This makes the card move smoothly as it is dragged.
  • .dragging class: This class is applied while the card is being dragged, reducing its opacity and scaling it up to provide visual feedback.
  • .move class: This class is applied when the card is dropped, creating a smooth moving animation.

JavaScript

const cards = document.querySelectorAll('.card');
const columns = document.querySelectorAll('.column');

let draggedCard = null;

cards.forEach(card => {
  card.addEventListener('dragstart', function () {
    draggedCard = card;
    setTimeout(() => {
      card.classList.add('dragging');
    }, 0);
  });

  card.addEventListener('dragend', function () {
    setTimeout(() => {
      card.classList.remove('dragging');
      draggedCard = null;
    }, 0);
  });
});

columns.forEach(column => {
  column.addEventListener('dragover', function (e) {
    e.preventDefault();
  });

  column.addEventListener('drop', function (e) {
    e.preventDefault();
    if (draggedCard) {
      this.appendChild(draggedCard);
      draggedCard.classList.add('move'); // 移動アニメーションを追加
      setTimeout(() => {
        draggedCard.classList.remove('move'); // アニメーションのクラスを削除
      }, 500); // アニメーションが終了するまで待つ
      draggedCard = null; // draggedCardをnullにして、次のドラッグ用に準備
    }
  });
});
  • draggedCard.classList.add('move'): This adds the move animation when the card is dropped.
  • setTimeout: Waits for the animation to finish before removing the move class. This ensures that the animation can be applied again during the next drag-and-drop.

 

3. Saving the Sorted Order to the Server

Here’s an example of how to add functionality to save the sorted order to the server. The fetch function is used to send the new order to the database.

JavaScript

function saveCardOrder() {
  const cardOrder = Array.from(document.querySelectorAll('.card')).map(card => card.textContent);
  console.log('Saving order:', cardOrder);

  fetch('/saveOrder', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ order: cardOrder })
  }).then(response => response.json())
    .then(data => console.log('Order saved successfully:', data))
    .catch(error => console.error('Error saving order:', error));
}

 

By using these advanced examples, you can further enhance Swapy’s basic functionality, enabling more complex and rich interfaces with features such as data synchronization with the server, support for multiple zones, and the addition of animations.