Splide.jsとmatchMediaで画面幅で切り替える方法

はじめに

スマホでは指でスワイプできるスライダー、PCでは横並びの静的なレイアウト、という出し分けはよく出てくる要件です。Splide.js単体では「画面幅でスライダーそのものをON/OFFする」機能は用意されていないため、window.matchMediaと組み合わせて初期化と破棄を切り替えます。

動作確認はSplide.js v4.1系で行っています。CDNから読み込む構成で例示しますが、npmやESM環境でも同じAPIで動きます。

全体の方針

window.matchMediaでスマホ向けのメディアクエリを購読しておき、マッチしたらSplide.jsのインスタンスを生成してmount()、マッチが外れたらdestroy()でDOMを元に戻します。「マッチ判定」と「初期化・破棄」を1つのハンドラに集約しておくと、ロード時とリサイズ時の処理を共有できます。

CSS側は、Splide.jsが付与するis-initializedクラスを利用して、初期化されていない状態では静的なグリッドにし、初期化中はSplide.js側のレイアウトに任せる二段構えにします。Splide.jsのCSSは読み込まれているだけで.splide__listflexが当たるため、未初期化時の見た目を別系統で上書きしておくのがポイントです。

window.matchMediaの基本

window.matchMedia(query)は、CSSメディアクエリと同じ書式の文字列を受け取り、MediaQueryListを返します。返ってきたオブジェクトのmatchesプロパティが、現在のマッチ状態を真偽値で示します。

JavaScript
const mql = window.matchMedia('(max-width: 768px)');
console.log(mql.matches); // true / false

状態が変わった瞬間を捕まえるには、changeイベントを購読します。

JavaScript
mql.addEventListener('change', (event) => {
  console.log(event.matches);
});

changeイベントは閾値をまたいだときだけ発火します。ロード時には呼ばれないため、初期状態はハンドラを直接呼び出すか、mql.matchesを見て分岐する書き方が必要です。

基本コードとサンプル

See the Pen Splide.js × matchMedia v1 by watanabe (@web-sourcecode) on CodePen.

HTML

HTML
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@splidejs/splide@4.1.4/dist/css/splide.min.css">

<section class="splide js-slider" aria-label="お知らせ">
  <div class="splide__track">
    <ul class="splide__list">
      <li class="splide__slide">
        <article class="card">
          <img class="card__image" src="https://picsum.photos/seed/news1/600/400" alt="">
          <div class="card__body">
            <p class="card__date">2026.05.01</p>
            <h3 class="card__title">新サービスのお知らせ</h3>
            <p class="card__text">画面幅に応じてレイアウトが切り替わります。</p>
          </div>
        </article>
      </li>
      <li class="splide__slide">
        <article class="card">
          <img class="card__image" src="https://picsum.photos/seed/news2/600/400" alt="">
          <div class="card__body">
            <p class="card__date">2026.04.20</p>
            <h3 class="card__title">メンテナンスのお知らせ</h3>
            <p class="card__text">スマホ表示のときだけスライダー化します。</p>
          </div>
        </article>
      </li>
      <li class="splide__slide">
        <article class="card">
          <img class="card__image" src="https://picsum.photos/seed/news3/600/400" alt="">
          <div class="card__body">
            <p class="card__date">2026.04.10</p>
            <h3 class="card__title">サポート時間変更のご案内</h3>
            <p class="card__text">PC表示のときは3カラムのグリッドになります。</p>
          </div>
        </article>
      </li>
    </ul>
  </div>
</section>

<script src="https://cdn.jsdelivr.net/npm/@splidejs/splide@4.1.4/dist/js/splide.min.js"></script>

splideクラスはSplide.jsの仕様で必須です。js-sliderはJavaScriptから対象を取るための識別子として別に付けています。li.splide__slideの中には、CodePenなどに貼り付けてすぐ確認できるようカード風のマークアップを入れています。

CSS

CSS
/* PC:静的なグリッドレイアウト */
.js-slider:not(.is-initialized) .splide__track {
  overflow: visible;
}

.js-slider:not(.is-initialized) .splide__list {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1rem;
  transform: none !important;
}

.js-slider:not(.is-initialized) .splide__slide {
  width: auto;
}

/* カード自体のスタイル(CodePenでの見た目確認用) */
.card {
  display: flex;
  flex-direction: column;
  background: #fff;
  border: 1px solid #e5e5e5;
  border-radius: 8px;
  overflow: hidden;
}

.card__image {
  width: 100%;
  aspect-ratio: 3 / 2;
  object-fit: cover;
}

.card__body {
  padding: 1rem;
}

.card__date {
  margin: 0 0 0.25rem;
  font-size: 0.85rem;
  color: #888;
}

.card__title {
  margin: 0 0 0.5rem;
  font-size: 1rem;
}

.card__text {
  margin: 0;
  font-size: 0.9rem;
  line-height: 1.6;
}

is-initializedはSplide.jsがmount()で付与する初期化完了の目印です。「未初期化のときだけ」にセレクタのスコープを限定すると、Splide.jsのデフォルトCSS(flexoverflow: hidden)を上書きできます。transform: none !importantは、破棄直後に残ることがあるインラインスタイルを打ち消す保険です。

.card以下はカード自体の見た目を作るスタイルです。CodePenなどに貼り付けたときにレイアウトの切り替わりを目視確認しやすくする意図で添えています。実際のサイトに組み込むときは、サイト側の既存スタイルに合わせて差し替えてください。

画面幅でSplide.jsの有効・無効を切り替える

ここからが本題のJavaScriptコードです。

JavaScript

JavaScript
const sliderElement = document.querySelector('.js-slider');
const mql = window.matchMedia('(max-width: 768px)');
let splide = null;

const handleChange = (event) => {
  if (event.matches) {
    // スマホ幅:スライダーを有効化
    if (!splide) {
      splide = new Splide(sliderElement, {
        type: 'loop',
        perPage: 1,
        gap: '1rem',
        pagination: true,
        arrows: true,
      });
      splide.mount();
    }
  } else {
    // PC幅:スライダーを破棄して静的レイアウトに戻す
    if (splide) {
      splide.destroy(true);
      splide = null;
    }
  }
};

// 初期化時の状態を反映
handleChange(mql);

// 以後の状態変化を購読
mql.addEventListener('change', handleChange);

ハンドラはMediaQueryListMediaQueryListEventのどちらを受け取ってもmatchesプロパティで判定できるよう統一しています。初期化時はmqlをそのまま渡し、リサイズ時はイベントオブジェクトが渡されます。

splide.destroy(true)の引数trueは、Splide.jsが生成したDOM・クラス・インラインスタイルを完全に取り除くフラグです。引数なしで呼ぶとイベントリスナーは外れますがDOMの一部が残るため、再度new Splide()したときに状態が二重になることがあります。動的に行き来する用途ではtrueを付けて綺麗に戻すのが安全です。

splide変数を外側に保持してnullで初期化しているのは、二重初期化と破棄漏れを防ぐためです。すでにインスタンスがあるかを存在チェックで分岐すれば、状態が連続して変わっても無駄に作り直しません。

状態が頻繁に変わるときの対応

changeイベントは閾値をまたぐ瞬間にしか発火しないため、ウィンドウのドラッグリサイズでも処理は1往復ぶんで済みます。デバウンスを噛ませなくても重くなりにくい構造です。

ただし、複数のメディアクエリを並列で使う場合や、初期化処理に外部APIや画像のプリロードが絡む場合は、requestAnimationFrameで1フレーム遅らせると安心です。

JavaScript
let frameId = null;

const handleChange = (event) => {
  if (frameId) cancelAnimationFrame(frameId);
  frameId = requestAnimationFrame(() => {
    // 切り替え処理本体
  });
};

changeが連続発火するわけではないので過剰な対応にはなりますが、初期化が重たいケースで有効です。

実装時の注意点

splide.destroy(true)を呼ぶと、Splide.jsはDOMを元のマークアップに戻します。破棄後の状態は「最初に書いたHTMLと同じ」になるため、CSS側で静的レイアウトを書いておけば破棄直後からPC用の表示に切り替わります。

破棄したインスタンスは復元できません。再度モバイル幅に戻った場合は、新しいSplideインスタンスを作る必要があります。refresh()はオプション変更などには使えますが、destroy()済みのものを呼び戻す用途では使えない点に注意します。

MediaQueryListaddEventListenerは古いSafari(14未満)で未対応の時期がありました。現行の主要ブラウザでは利用可能ですが、サポート範囲を広く取りたい場合は、フォールバックとしてaddListenerを併用する書き方もあります。

JavaScript
if ('addEventListener' in mql) {
  mql.addEventListener('change', handleChange);
} else {
  mql.addListener(handleChange); // 旧仕様
}

動きに敏感な方への配慮として、prefers-reduced-motionを考慮する場合は、メディアクエリをAND条件で組み合わせる方法があります。動きを抑える設定にしている環境ではスライダー化しない、という運用です。

JavaScript
const mql = window.matchMedia('(max-width: 768px) and (prefers-reduced-motion: no-preference)');

CSS側でも、スライド遷移の動きを抑える指定を併用しておくと安心です。

CSS
@media (prefers-reduced-motion: reduce) {
  .splide__list {
    transition: none !important;
  }
}

まとめ

Splide.jsの動的なON/OFFは、window.matchMediachangeイベントで「マッチした時にmount()、外れた時にdestroy(true)」のシンプルなルールに落とし込めます。CSS側ではis-initializedクラスで静的レイアウトと切り分けると、未初期化時の見た目が崩れません。

リサイズが何度も行き来するケースでも、インスタンスの存在チェックを入れておけば二重初期化や破棄漏れを防げます。次にレスポンシブなUIを設計するときは、画面幅で「機能そのものを差し替える」選択肢として、この組み合わせを引き出しに入れておくと便利です。

ポイント

  • window.matchMediachangeイベントで初期化と破棄を切り替える
  • 初期化時はハンドラを手動で呼んで現在の状態を反映する
  • インスタンスは外側に保持し、二重初期化と破棄漏れを防ぐ
  • splide.destroy(true)で生成DOMを完全に戻す
  • CSS側はis-initializedクラスで静的レイアウトと切り分ける

参考リンク

read next