はじめに
タスク管理ツールのカード並び替えや、ToDoリストの順序変更、画像ギャラリーの入れ替えなど、「ドラッグ&ドロップで並び替えたい」という要望はフロントエンド開発において頻繁に登場します。しかし、いざ自前で実装しようとすると、マウスイベントとタッチイベントの差異、スクロール中の挙動、複数リスト間の移動処理など、意外と考慮すべき点が多いことに気づきます。
そこで頼りになるのが「Sortable」です。jQueryなどの外部依存を持たず、モダンブラウザとタッチデバイスの両方でスムーズなドラッグ&ドロップ並び替えを実現してくれるJavaScriptライブラリです。今回はSortableの特徴から、基本的な使い方、React・Vueでの実践的な活用方法まで解説します。
とはいえ、ドラッグ&ドロップの操作感ばかりは文章では伝わりません。記事の中に実際に並び替えられるリストを置いてあるので、先に触ってみたい方はこちらからどうぞ。
Sortableとは
Sortableは、リオーダー可能なドラッグ&ドロップリストを実現するミニマリストなJavaScriptライブラリです。「No jQuery」を掲げており、素のJavaScriptだけで動作します。GitHub上の「SortableJS/Sortable」として公開されており、Vanilla JSはもちろん、React・Vue・Angularなど主要フレームワーク向けのラッパーライブラリも整備されているため、どのような技術スタックのプロジェクトでも導入しやすいのが魅力です。
主な特徴
- 依存ライブラリなし - jQuery不要で、素のJavaScriptのみで動作するため導入コストが低い
- タッチデバイス対応 - スマートフォンやタブレットでも違和感のない操作感を実現
- 複数リスト間のドラッグ -
groupオプションを使えば、別々のリスト間でアイテムを移動させることも可能 - フレームワーク対応 - React・Vue・Angular・Emberなど各フレームワーク向けのラッパーが用意されている
- CSSアニメーション - アイテム移動時に滑らかなアニメーションを自動で付与できる
インストール
npmを利用する場合は以下のコマンドでインストールします。
npm install sortablejs
Yarnを使う場合は次の通りです。
yarn add sortablejs
TypeScriptプロジェクトの場合は、型定義もあわせてインストールしておくと安心です。
npm install --save-dev @types/sortablejs
サンプル
コードの説明に入る前に、まずリストを掴んで動かしてみてください。 5つの工程が並んでいて、ドラッグして順序を入れ替えると、そのすぐ下に 現在の並び順が配列としてリアルタイムで表示されます。
「テスト」を一番上へ持ち上げる、「リリース」を真ん中に差し込む—— 掴んでいる間は元の位置が薄く残り、離した瞬間に下の配列が新しい順序へ書き換わります。 スマートフォンやタブレットで開いている方は、そのまま指でも同じように動かせるはずです。
ポイントはtoArray()です。並び替え後のDOMを自分で走査して順序を組み立てる必要はなく、
各アイテムにdata-idさえ振っておけば、画面の並びどおりのID配列がそのまま返ってきます。
この配列をそのままAPIへ送れば並び順の永続化が完了するので、
「ドラッグ&ドロップUI」と「保存したいデータ」の距離がかなり近いことが分かると思います。
基本的な使い方
まずはVanilla JSでの最もシンプルな実装例を見てみましょう。並び替えたい要素をリストとして用意し、そのコンテナ要素をSortableに渡すだけで完了します。
<ul id="item-list">
<li>タスク1</li>
<li>タスク2</li>
<li>タスク3</li>
</ul>
import Sortable from "sortablejs";
const el = document.getElementById("item-list");
Sortable.create(el, {
animation: 150, // 並び替え時のアニメーション速度(ms)
ghostClass: "sortable-ghost", // ドラッグ中のプレースホルダーに付与するクラス
});
これだけで、リスト内の<li>要素をドラッグして自由に並び替えられるようになります。さらに、ドラッグ終了時の処理をonEndコールバックで受け取ることもできます。
Sortable.create(el, {
animation: 150,
onEnd: (evt) => {
console.log(`${evt.oldIndex}番目から${evt.newIndex}番目へ移動しました`);
},
});
handleオプションを使えば、要素全体ではなく特定の部分(ドラッグハンドル)だけを掴んで並び替えることも可能です。テキストを選択したい要素とドラッグ操作を両立させたい場合に便利です。
Sortable.create(el, {
handle: ".drag-handle",
animation: 150,
});
<li>
<span class="drag-handle">::</span>
タスク1
</li>
実践的なユースケース
カンバンボードのようなカード移動
groupオプションを使うと、複数のリスト間でアイテムを移動できるようになります。タスク管理ツールでよく見る「未着手・対応中・完了」のようなカラム間のカード移動を実装してみましょう。
<div class="board">
<ul id="todo" class="column"></ul>
<ul id="doing" class="column"></ul>
<ul id="done" class="column"></ul>
</div>
import Sortable from "sortablejs";
const options = {
group: "kanban", // 同じgroup名同士でアイテムを移動できる
animation: 150,
ghostClass: "sortable-ghost",
};
["todo", "doing", "done"].forEach((id) => {
Sortable.create(document.getElementById(id), options);
});
これだけで、カラムをまたいだカードの移動が実現します。移動先の情報はonEndコールバックのevt.toやevt.fromから取得できるため、状態管理と組み合わせて永続化する処理を追加すればよいでしょう。
Reactでの活用
Reactプロジェクトでは、公式ラッパーであるreact-sortablejsを利用すると、Reactのコンポーネントとして自然に組み込めます。
npm install react-sortablejs sortablejs
import { ReactSortable } from "react-sortablejs";
import { useState } from "react";
function TaskList() {
const [items, setItems] = useState([
{ id: 1, name: "タスク1" },
{ id: 2, name: "タスク2" },
{ id: 3, name: "タスク3" },
]);
return (
<ReactSortable list={items} setList={setItems} animation={150}>
{items.map((item) => (
<div key={item.id}>{item.name}</div>
))}
</ReactSortable>
);
}
export default TaskList;
状態(items)を並び替え結果に応じて自動更新してくれるため、Reactの宣言的なスタイルを崩さずに導入できます。
Vue.jsでの活用
Vue.jsではvuedraggableという公式ラッパーが用意されています。Vue 3対応版を利用する場合は以下のようにインストールします。
npm install vuedraggable@next
<template>
<draggable v-model="items" item-key="id" :animation="150">
<template #item="{ element }">
<div class="task-item">{{ element.name }}</div>
</template>
</draggable>
</template>
<script setup>
import { ref } from "vue";
import draggable from "vuedraggable";
const items = ref([
{ id: 1, name: "タスク1" },
{ id: 2, name: "タスク2" },
{ id: 3, name: "タスク3" },
]);
</script>
v-modelで並び替え結果が自動的に反映されるため、Vueのリアクティブなデータとシームレスに連携できます。
まとめ
Sortableは、jQueryなどの依存を持たずに、モダンブラウザとタッチデバイスの両方で快適なドラッグ&ドロップ並び替えを実現できるライブラリです。Vanilla JSでの手軽な導入はもちろん、react-sortablejsやvuedraggableといった各フレームワーク向けのラッパーも充実しているため、既存プロジェクトの技術スタックを問わず取り入れやすい点が大きな魅力です。
まずは今回紹介した基本的な実装から試してみて、慣れてきたらgroupオプションを使ったカンバンボードのような複数リスト間の移動にも挑戦してみてください。ドラッグ&ドロップによる並び替えUIが必要になったときの第一候補として、ぜひ検討してみてはいかがでしょうか。