はじめに
写真ギャラリーや制作実績一覧、商品カードのようなUIでは、「カテゴリで絞り込む」「新しい順に並び替える」「カードをきれいに再配置する」といった動きがよく必要になります。
この処理をすべて自前で作ると、DOM操作、アニメーション、レスポンシブ時のレイアウト調整が意外と面倒です。
そんなときに使いやすいのが Shuffle.js です。
Shuffle.jsは、グリッド状の要素をカテゴリでフィルタリングしたり、条件に応じてソートしたりできるJavaScriptライブラリです。
この記事では、npmでの導入から基本的な使い方、実案件で使いやすい応用パターンまで紹介します。
Shuffle.jsとは
Shuffle.jsは、カード型の要素を対象にして、次のような操作を行えるライブラリです。
- カテゴリによる絞り込み
- 複数カテゴリの絞り込み
- タイトル順・日付順などの並び替え
- ランダム表示
- 要素の追加・削除
- レスポンシブなグリッド再配置
公式サイトでも、Shuffle.jsは「responsive grid of items」をカテゴリ分け、ソート、フィルタリングするためのライブラリとして紹介されています。
向いているUIは、たとえば次のようなものです。
- 写真ギャラリー
- 制作実績一覧
- スタッフ紹介
- 店舗一覧
- ブログ記事一覧
- 商品・プラン一覧
- FAQや資料カードの絞り込み
CSS GridやFlexboxだけでも見た目は作れますが、「状態に応じて表示・非表示を切り替えながら、自然に詰めて並べ直す」ところまで考えると、Shuffle.jsを使うメリットが出てきます。
基本の使い方
インストール
npmで使う場合は、公式ドキュメントどおり shufflejs をインストールします。
npm install shufflejsViteやWebpackなどのバンドラーを使っているプロジェクトでは、ES Modulesとして読み込めます。
import Shuffle from 'shufflejs';HTMLを用意する
Shuffle.jsでは、グリッドの親要素と、その中にあるアイテム要素を指定します。
カテゴリ情報は data-groups にJSON配列として持たせるのが基本です。
<div class="work-filter">
<button type="button" data-filter="all">すべて</button>
<button type="button" data-filter="website">Webサイト</button>
<button type="button" data-filter="branding">ブランディング</button>
<button type="button" data-filter="photo">写真</button>
</div>
<div class="work-grid js-work-grid">
<article class="work-card js-work-item" data-groups='["website","branding"]' data-title="Ichiryu Website" data-date="2026-05-10">
<img src="/images/work-01.jpg" alt="Ichiryu Website">
<h2>Ichiryu Website</h2>
</article>
<article class="work-card js-work-item" data-groups='["photo"]' data-title="Season Photo" data-date="2026-04-18">
<img src="/images/work-02.jpg" alt="Season Photo">
<h2>Season Photo</h2>
</article>
<article class="work-card js-work-item" data-groups='["website"]' data-title="Recruit Page" data-date="2026-03-02">
<img src="/images/work-03.jpg" alt="Recruit Page">
<h2>Recruit Page</h2>
</article>
</div>data-groups は、公式ドキュメントでも配列形式が基本として紹介されています。
1つのカードに複数カテゴリを持たせられるので、「Webサイト」かつ「ブランディング」のような分類にも対応できます。
CSSでカードを並べる
Shuffle.jsはアイテムの位置を制御しますが、カード自体の見た目や幅はCSSで作ります。
.work-grid {
position: relative;
display: flex;
flex-wrap: wrap;
gap: 24px;
}
.work-card {
width: calc((100% - 48px) / 3);
margin: 0;
}
.work-card img {
display: block;
width: 100%;
aspect-ratio: 4 / 3;
object-fit: cover;
}
@media (max-width: 767px) {
.work-grid {
gap: 16px;
}
.work-card {
width: 100%;
}
}実案件では、カード幅をCSSで明示しておくとレイアウトが安定しやすくなります。
JavaScriptで初期化する
親要素を取得し、new Shuffle() で初期化します。
import Shuffle from 'shufflejs';
const grid = document.querySelector('.js-work-grid');
if (grid) {
const shuffle = new Shuffle(grid, {
itemSelector: '.js-work-item',
speed: 300
});
document.querySelectorAll('[data-filter]').forEach(button => {
button.addEventListener('click', () => {
const filter = button.dataset.filter;
if (filter === 'all') {
shuffle.filter(Shuffle.ALL_ITEMS);
return;
}
shuffle.filter(filter);
});
});
}itemSelector には、Shuffle.jsの対象にしたいカード要素のセレクタを指定します。
ボタンをクリックしたら shuffle.filter() を呼び出すだけで、表示対象が切り替わります。
便利な使いどころ
制作実績一覧のカテゴリ切り替え
もっとも使いやすいのは、制作実績やポートフォリオの一覧です。
「Webサイト」「LP」「ロゴ」「写真」などのカテゴリでカードを切り替えられます。
WordPressのカスタム投稿と組み合わせる場合は、PHP側でカテゴリを data-groups に出力して、フロント側ではShuffle.jsで絞り込む構成にすると扱いやすいです。
<article
class="work-card js-work-item"
data-groups='<?php echo esc_attr( wp_json_encode( $category_slugs ) ); ?>'
>
...
</article>一覧全体をページ遷移させずに絞り込めるため、ユーザーが複数のカテゴリを比較しやすくなります。
写真ギャラリーの見せ方を切り替える
写真ギャラリーでは、「イベント」「日常」「施設」「食事」などで分類できます。
カードの高さが画像ごとに変わる場合でも、Shuffle.jsで再配置すると自然に詰まったグリッドにできます。
画像読み込み後に高さが変わる場合は、読み込み完了後に layout() を呼ぶとズレを補正できます。
grid.querySelectorAll('img').forEach(image => {
if (image.complete) {
return;
}
image.addEventListener('load', () => {
shuffle.layout();
});
});公式APIでも、画像読み込みなどで計算がずれる場合には layout() が使えると説明されています。
商品一覧や資料一覧のソート
Shuffle.jsはフィルタリングだけでなく、ソートにも対応しています。data-title や data-date のような属性を持たせておくと、タイトル順・日付順の並び替えを実装できます。
<select class="js-sort">
<option value="default">標準</option>
<option value="title">タイトル順</option>
<option value="date">新しい順</option>
</select>const sortSelect = document.querySelector('.js-sort');
sortSelect?.addEventListener('change', event => {
const value = event.target.value;
if (value === 'title') {
shuffle.sort({
by: element => element.dataset.title.toLowerCase()
});
return;
}
if (value === 'date') {
shuffle.sort({
reverse: true,
by: element => element.dataset.date
});
return;
}
shuffle.sort({});
});sort({}) を呼ぶとDOM順に戻せます。
管理画面やCMSから出力される順番を標準順として使いたい場合にも便利です。
応用コード
ここでは、カテゴリフィルターとソートを組み合わせた実践的なコードを紹介します。
制作実績一覧やアルバム一覧でそのまま使いやすい形です。
<div class="archive-toolbar">
<div class="archive-filter" aria-label="カテゴリで絞り込み">
<button type="button" class="is-active" data-filter="all">すべて</button>
<button type="button" data-filter="website">Webサイト</button>
<button type="button" data-filter="design">デザイン</button>
<button type="button" data-filter="photo">写真</button>
</div>
<select class="js-archive-sort" aria-label="並び替え">
<option value="default">標準順</option>
<option value="newest">新しい順</option>
<option value="title">タイトル順</option>
</select>
</div>
<div class="archive-grid js-archive-grid">
<article class="archive-card js-archive-item" data-groups='["website","design"]' data-title="Corporate Site" data-date="2026-06-01">
<img src="/images/archive-01.jpg" alt="Corporate Site">
<h2>Corporate Site</h2>
</article>
<article class="archive-card js-archive-item" data-groups='["photo"]' data-title="Photo Album" data-date="2026-05-20">
<img src="/images/archive-02.jpg" alt="Photo Album">
<h2>Photo Album</h2>
</article>
</div>import Shuffle from 'shufflejs';
const grid = document.querySelector('.js-archive-grid');
const filterButtons = document.querySelectorAll('[data-filter]');
const sortSelect = document.querySelector('.js-archive-sort');
if (grid) {
const shuffle = new Shuffle(grid, {
itemSelector: '.js-archive-item',
speed: 300,
staggerAmount: 20,
staggerAmountMax: 120
});
let currentFilter = Shuffle.ALL_ITEMS;
let currentSort = {};
const applyView = () => {
shuffle.filter(currentFilter, currentSort);
};
filterButtons.forEach(button => {
button.addEventListener('click', () => {
filterButtons.forEach(item => item.classList.remove('is-active'));
button.classList.add('is-active');
currentFilter = button.dataset.filter === 'all'
? Shuffle.ALL_ITEMS
: button.dataset.filter;
applyView();
});
});
sortSelect?.addEventListener('change', event => {
const value = event.target.value;
if (value === 'newest') {
currentSort = {
reverse: true,
by: element => element.dataset.date
};
} else if (value === 'title') {
currentSort = {
by: element => element.dataset.title.toLowerCase()
};
} else {
currentSort = {};
}
applyView();
});
}ポイントは、現在のフィルター状態とソート状態を変数で持っておくことです。
Shuffle.jsの filter() は第2引数にソート設定を渡せるため、「カテゴリで絞り込んだうえで新しい順に並べる」といったUIを作れます。
Ajaxや追加読み込みと組み合わせる
一覧に「もっと見る」ボタンがある場合は、新しいHTMLをDOMに追加したあと、追加した要素を shuffle.add() に渡します。
async function appendItems() {
const response = await fetch('/api/works?page=2');
const html = await response.text();
const template = document.createElement('template');
template.innerHTML = html.trim();
const newItems = [...template.content.querySelectorAll('.js-archive-item')];
newItems.forEach(item => {
grid.appendChild(item);
});
shuffle.add(newItems);
}公式ドキュメントでも、要素の追加後に add() を使ってShuffle.jsへ知らせる形が紹介されています。
無限スクロールや「もっと見る」UIとの相性もよいです。
注意点
data-groups は正しいJSONにする
data-groups='["website","photo"]' のように、JSON配列として正しく出力する必要があります。
CMSから出力する場合は、文字列連結ではなくJSONエンコードを使うほうが安全です。
WordPressなら wp_json_encode() を使うと扱いやすくなります。
data-groups='<?php echo esc_attr( wp_json_encode( $groups ) ); ?>'画像の読み込みタイミングに注意する
カード内の画像があとから読み込まれると、初期計算時と実際の高さが変わることがあります。
画像サイズが固定できるなら、CSSの aspect-ratio を指定しておくのが簡単です。
.archive-card img {
aspect-ratio: 4 / 3;
object-fit: cover;
}画像サイズが可変の場合は、画像読み込み後に shuffle.layout() を呼びます。
フィルター対象が多すぎる場合は設計を見直す
Shuffle.jsは手軽に使えますが、数百件以上のカードを一度にDOMへ出すような画面では、描画や画像読み込みが重くなります。
その場合は、サーバー側の絞り込み、ページネーション、仮想スクロールなども検討したほうがよいです。
「表示中の一覧を気持ちよく切り替える」用途には向いていますが、「大量データを検索する」用途では別の設計が必要です。
破棄処理を忘れない
SPAやモーダル内でShuffle.jsを使う場合は、画面を離れるタイミングで destroy() を呼ぶと不要なイベントや参照を片付けられます。
shuffle.destroy();通常の静的ページでは意識しなくても問題になりにくいですが、ReactやVueなどでコンポーネント化する場合は重要です。
まとめ
Shuffle.jsは、カード型UIに「絞り込み」「並び替え」「自然な再配置」を加えたいときに便利なライブラリです。
CSSだけでは難しい表示切り替えやソートを、比較的少ないコードで実装できます。
特に、写真ギャラリー、制作実績一覧、商品一覧、記事一覧のように、ユーザーがカテゴリを行き来しながら探す画面と相性がよいです。
導入時は、まず次の流れで考えるとスムーズです。
- HTMLに
data-groupsを持たせる - CSSでカード幅と画像比率を安定させる
new Shuffle()で初期化する- ボタンやセレクトから
filter()/sort()を呼ぶ - 画像読み込みや追加要素がある場合は
layout()/add()を使う
小さなギャラリーから始めて、必要に応じてソートや追加読み込みを足していくと、実案件でも扱いやすい構成になります。
ポイント
- Shuffle.jsは、カード型グリッドの絞り込み・並び替え・再配置に使いやすい
- npmでは
npm install shufflejsで導入できる data-groupsにカテゴリ情報を持たせ、filter()で表示を切り替えるsort()を使うと、日付順・タイトル順・ランダム表示にも対応できる- 画像の高さが変わる場合は
aspect-ratioやlayout()でズレを防ぐ - 大量データの検索用途では、サーバー側の絞り込みやページネーションも検討する