JavaScript

【JavaScript】レスポンシブスライダーをライブラリなしで実装する

2022/05/25

前回の記事同様、先日行ったサイトリニューアル作業で得た知見を記事にしていきます。

前回の記事↓

今回は「複数アイテムを表示するレスポンシブのスライダー作成方法」という内容です。

1アイテム(画面いっぱいなど)ではなく複数アイテムを表示するスライダーにすることによって、レスポンシブにする難易度が非常に上がりました。

そのため、以前書いたスライダーの記事とは別物として発信する価値がありそうだと考えました。

当サイトではトップページで下のようにレスポンシブのスライダーを表示しています。

完成形と仕様確認

まずは完成形です。

今回はJqueryなどのライブラリ・フレームワークを使わずに、素のJavaScriptで作成していきます。

See the Pen Untitled by shinobi (@shinobi-hattori) on CodePen.

わかりやすくするためスライダー内のアイテムには番号が振ってあり、最低限のCSSをあてるだけに留めています。

レスポンシブスライダーなので、小さく表示されるCODEPENだと見にくいかもしれません。

CODEPENの枠下の倍率表示を0.5xあたりにしていただくか、右上の「EDIT ON CODEPEN」という部分のリンクに飛んで、別画面で確認してみてください。

次に、今回作っていくスライダーの仕様を確認していきます。

・画面幅が900pxより大きい場合は5アイテム表示する
・画面幅が600pxより大きく、900px以下の場合は4アイテム表示する
・画面幅が600px以下の場合は3アイテム表示する
・画面幅が変更されたらすぐに検知して、スライダーやアイテムの幅に反映する
・ユーザーのアクションがなければ、自動でスライドする

コード解説

順番に解説していきます。

HTMLとCSSに関しては特に解説はありませんが、もし基本の部分から知りたいということであれば、以下の記事が参考になるかと思いますので、ご参照ください。

変数の定義

const sliderFrame = document.querySelector('.slider-frame');
const slider = document.querySelector('.slider');
const items = document.querySelectorAll('li');
const prevBtn = document.querySelector('.prev');
const nextBtn = document.querySelector('.next');

まずは今回使う要素を取得していきます。

itemsだけquerySelectorAllの複数なのでご注意ください。

画面に幅に合わせて表示アイテム数を決める関数

let itemCount = null;
const itemsDisplayed = () => {
  if (window.innerWidth > 900) {
    return itemCount = 5;
  } else if (window.innerWidth > 600) {
    return itemCount = 4;
  } else if (window.innerWidth <= 600) {
    return itemCount = 3;
  }
}
itemsDisplayed();

このitemsDisplayed関数で、レスポンシブにアイテムの表示数が切り替わる準備をしています。

関数が実行される度に現在の画面幅を判定し、条件の合った数字をitemCountに入れて返します。

最初にitemsDisplayed関数が実行されるまでは変数itemCountに値が入っていませんので、その場合は初期値としてnullを入れておきます。

アイテム幅を決める関数

const setWidth = () => {
  const itemWidth = sliderFrame.offsetWidth / itemCount;
  items.forEach(item => {
    item.style.minWidth = `${itemWidth}px`;
  });
}
setWidth();

先ほどのitemsDisplayed関数のおかげで、itemCountの中身が3、4、5のどれかになっていると思います。

そこで、sliderFrame.offsetWidthという「スライダー表示コンテンツ領域の横幅」を、itemCountの数字で割ってあげます。

例えばスライダー表示コンテンツ領域が800pxでitemCountが4だった場合、割り算の結果アイテムの横幅が200pxで並ぶことになります。

画面幅の移動を検知する関数

const detectScreenWidth = () => {
  window.addEventListener('resize', () => {
    itemsDisplayed();
    setWidth();
  })
}
detectScreenWidth();

さきほどの関数2つだけでは「ローディングされた最初に実行されるだけ」なので、コンテンツが表示された後に画面幅が変更された場合を検知しなければいけません。

そこで、上のdetectScreenWidth関数です。

addEventListenerの引数になっているresizeは、画面幅が変更されたことを検出してくれる便利なメソッドです。

画面幅が変更された際に先ほどのitemsDisplayed関数とsetWidth関数を実行してあげれば、表示されるアイテム数とアイテム幅を更新していくことができます。

クリックイベント

let index = 0;
prevBtn.addEventListener('click', () => {
  if (index !== 0) {
    index--;
    slide();
    count = 0;
  }
});

nextBtn.addEventListener('click', () => {
  if (index !== (items.length - itemCount)) {
    index++;
    slide();
    count = 0;
  }
});

クリックイベントの中身は以下のようになっています。

・prevボタンをクリックするとindexがマイナス1
・nextボタンをクリックするとindexがプラス1
・slide関数を実行する
・indexが0のときはprevをクリックしても中身が実行されない
・indexがアイテム数(items.length) – itemCountの数 のとき、nextをクリックしても中身が実行されない
・countの中身を0にする

このスライダーは、「indexという変数を用意し、クリックする度に変動するindexの値でスライドの位置を決める」というギミックが要になっています。

そこで、クリックされる度にindexの値を動かしてから、後述するslide関数を実行し、countを0にします。

ポイントとしては、items.length – itemCountのときはnextクリックを実行しないところです。

具体的な数字にしてしまうと、アイテム数の変動に対応できないスライダーになってしまいます。

このように抽象的な表現にすることによって、条件が変わっても対応するスライダーになります。

slide関数

const slide = () => {
  const offset = (sliderFrame.offsetWidth / itemCount) * index;
  slider.style.transform = `translateX(-${offset}px)`;
}

クリックイベントで実行されたslide関数の中身です。

まずはoffsetという変数を定義します。

何かの座標を取るようなときにoffsetという変数名が使われるのはプログラミング界の慣習のようです。

変数offsetの中身は、先ほどアイテム幅を取得する際に使った「sliderFrame.offsetWidthをitemCountで割った値」に対して、「クリックイベントで変動したindex」を掛けています。

そして、このoffsetの数値分だけスライドさせるためにslide.style.transformを指定します。

座標はoffsetピクセル分を指定します。

こうすることで、現在の画面幅と表示アイテム数に合わせて、いつでもぴったり1アイテム分のスライドをすることができます。

自動スライド

let count = 0;
setInterval(() => {
  if (count > 3) {
    if (index < (items.length - itemCount)) {
      index++;
      slide();
    } else if (index >= (items.length - itemCount)) {
      index = 0;
      slide();
    }
    count = 0;
  } else {
    count++;
  }
}, 1000);

最後に、「ユーザーからのアクションがなければ自動でスライドしていく関数」を作ります。

まずはcountという変数を用意して、setIntervalメソッドで1秒ごとに変数countがカウントアップされるようにします。

setIntervalはJavaScriptに元々あるメソッドです。

最後に書いてある第二引数の1000という部分が時間に対応しており、1000ミリ秒(1秒)ごとにカウントアップされるようになっています。

その後、countが3より大きくなったら(つまり4秒後)if文の中身が実行されます。

items.length – itemCountの部分は、スライドの最後尾を表しています。

最後尾に来るまではslide関数を実行させて変数indexを足していき、最後尾になればindexを0に戻すことでスライドの最初に戻ります。

このようにして自動スライドをループさせています。

また、先ほどクリックイベントの中にcountを0にする記述がありました。

クリックしたらcountを0(自動スライドをリセット)することで、自動スライドとユーザーのクリックのタイミングが重なって変な挙動になることを防いでいます。

最後に

レスポンシブ対応のスライダー機能は記事コンテンツを始め、会社のメンバー紹介部分やECサイトの商品紹介ページなど、様々なシーンで使われています。

汎用的に使える技術なので、ぜひ覚えてみてください。

おすすめ記事