Swiper × window.matchMedia でレスポンシブなスライダー制御を実現する方法

Webサイトでスライダー(カルーセル)を実装する際、「スマホではスライダーを表示したいが、PCでは静的なレイアウトにしたい」といったニーズはよくあります。今回は、Swiperと window.matchMedia を組み合わせて、画面サイズに応じて動的にSwiperの有効・無効を切り替える方法を段階的に解説します。

実装のゴール

画面幅表示レイアウトSwiperの動作
768px未満スライダー表示(Swiper有効)有効
768px以上横並びの静的レイアウト無効

1. 基本の構成:Swiperを画面幅で切り替える

まずは、スマホでSwiperを有効化し、PCでは破棄する基本の実装です。

HTML(Swiperの構造)

<div class="swiper-container js-my-swiper"> <div class="swiper-wrapper"> <div class="swiper-slide">スライド1</div> <div class="swiper-slide">スライド2</div> <div class="swiper-slide">スライド3</div> </div></div>
HTML

JavaScript

let mySwiper = null;const mediaQuery = window.matchMedia('(max-width: 767px)');function initSwiper() { if (!mySwiper) { mySwiper = new Swiper('.js-my-swiper', { slidesPerView: 1, spaceBetween: 10, loop: true, }); }}function destroySwiper() { if (mySwiper) { mySwiper.destroy(true, true); mySwiper = null; }}function handleBreakpoint(e) { if (e.matches)  initSwiper(); } else { destroySwiper(); }}handleBreakpoint(mediaQuery);mediaQuery.addEventListener('change', handleBreakpoint);
JavaScript

2. PCでは静的レイアウトに切り替える

Swiperだけでなく、PCでは静的な横並びのブロックにレイアウトを切り替える例です。

HTML(Swiperと静的レイアウトを両方用意)

<!-- スライダー:スマホ用 --><div class="swiper-container js-my-swiper" data-role="slider"> <div class="swiper-wrapper"> <div class="swiper-slide">アイテム1</div> <div class="swiper-slide">アイテム2</div> <div class="swiper-slide">アイテム3</div> </div></div><!-- 静的レイアウト:PC用 --><div class="static-layout" data-role="static"> <div class="item">アイテム1</div> <div class="item">アイテム2</div> <div class="item">アイテム3</div></div>
HTML

CSS(表示の切り替え)

[data-role="slider"],[data-role="static"] { display: none;}@media (max-width: 767px) { [data-role="slider"] { display: block; }}@media (min-width: 768px) { [data-role="static"] { display: flex; gap: 16px; } .static-layout .item { flex: 1; background: #eee; padding: 16px; text-align: center; }}
CSS

3. HTMLを共通化してJSで動的に切り替える

HTML構造をSwiper用と静的レイアウト用に分けると、同じデータを重複して書くことになるというデメリットがあります。これを避けるために、共通のアイテムリストから、JSでSwiper構造/静的構造に変換する方法を紹介します。

HTML(共通のリストのみ)

<div id="item-container"> <div class="item">アイテム1</div> <div class="item">アイテム2</div> <div class="item">アイテム3</div></div>
HTML

CSS(共通アイテムと静的レイアウト用)

.item { background: #eee; padding: 16px; text-align: center;}.static-layout { display: flex; gap: 16px;}.swiper-container { width: 100%;}
CSS

JavaScript(DOM変換とSwiper初期化)

let mySwiper = null;const container = document.getElementById('item-container');const mediaQuery = window.matchMedia('(max-width: 767px)');let currentLayout = null;function setupSwiperLayout() { if (currentLayout === 'swiper') return; const wrapper = document.createElement('div'); wrapper.classList.add('swiper-wrapper'); [...container.children].forEach((item) => { const slide = document.createElement('div'); slide.classList.add('swiper-slide'); slide.appendChild(item); wrapper.appendChild(slide); }); container.innerHTML = ''; const swiperEl = document.createElement('div'); swiperEl.classList.add('swiper-container'); swiperEl.appendChild(wrapper); container.appendChild(swiperEl); mySwiper = new Swiper('.swiper-container', { slidesPerView: 1.2, spaceBetween: 10, loop: true, }); currentLayout = 'swiper';}function setupStaticLayout() { if (currentLayout === 'static') return; if (mySwiper) { mySwiper.destroy(true, true); mySwiper = null; } const items = []; container.querySelectorAll('.swiper-slide .item').forEach((item) => { items.push(item); }); container.innerHTML = ''; const staticLayout = document.createElement('div'); staticLayout.classList.add('static-layout'); items.forEach((item) => staticLayout.appendChild(item)); container.appendChild(staticLayout); currentLayout = 'static';}function handleResize(e) { if (e.matches) { setupSwiperLayout(); } else { setupStaticLayout(); }}handleResize(mediaQuery);mediaQuery.addEventListener('change', handleResize);
JavaScript

read next