2020.08.07

【jQuery】プラグインなしの多機能スライダーをもう一度作成してみた【改訂版】

スポンサーリンク

プラグイン無しでスライダーを実装してみよう!

プラグインを使わない理由としては、プラグインと自作を比べると軽量自分の好みにカスタマイズできることです。
少し複雑ですが、順を追って丁寧(のつもり)に説明していきます。


アウトプットも兼ねて、今回はリファクタリングの仕方に重きを置いた記事内容を書いていこうと思います。

 

リファクタリング (refactoring) とは、コンピュータプログラミングにおいて、プログラムの外部から見た動作を変えずにソースコードの内部構造を整理することである。

wikipediaより引用

 

前回多機能スライドショーを実装しましたが、今回は前回と全く同じ挙動ではありますが、リファクタリングを施したのでコードの可読性がかなり上がっています。

前回のクソコードと照らし合わせて見てみると面白いかもです。こんなに簡略化できるとは思わなんだ。

前回作成したスライドショーの記事

JQueryで多機能スライダーを一から実装してみた【プラグインなし】

 

綺麗なコードを書くうえで抑えておくべき要点

①ベースとなる動きから順に実装していく(今回の場合は時間経過でスライドアニメーションを実行がベースとなる機能です。)

∟ベースの機能が完成したらそこに必要な機能を一つひとつ肉付けしていくとコードが取っ散らかりづらい

②変数宣言は必要としている関数内に入れること。複数の関数に必要とされる変数はグローバルで宣言するようにする


③jQueryのオブジェクトとしてDOM操作が伴う場合、変数に格納する 例:const $sliderWrap = $("#sliderWrap");

∟また、親のidを起点として子要素をfind()で取得することで負担が減ります


④関数は一つひとつ切り分けて作成


⑤インデックス番号など他の関数にも紐づけるような変数は使いまわす

全体のコード

小分けで一つずつ掘り下げて説明しますが、まず全体のコードを載せときます。

$(function () {

  function slider() {
    //変数宣言
    const $sliderWrap = $('#sliderWrap');
    const $slider = $sliderWrap.find('.slider');
    const $slides = $slider.find('.slides');
    const $slide = $slides.find('.slide');
    const $indicator = $sliderWrap.find('.indicator');
    const $prev = $sliderWrap.find('.prev');
    const $next = $sliderWrap.find('.next');
    const slideLength = $slide.length;
    let currentIndex = 0;
    let $indicatorItem = '';
    
    //インジケータの生成
    function addIndicator(){
      const indicatorHTML = '<li class="indicatorItem"></li>';
      for (let i = 0; i < slideLength; i++){
        $indicator.append(indicatorHTML);
      }
      $indicatorItem = $('.indicatorItem');
      $indicatorItem.eq(currentIndex).addClass('is-active');
    }
    
    //スライドクローン
    function cloneSlide() {
      const $lastSlide = $slides.find('li:last-child');
      const $firstSlide = $slides.find('li:first-child');
      $lastSlide.clone(true).prependTo($slides);
      $firstSlide.clone(true).appendTo($slides);
    }

    //スライドアニメーション
    function changeSlide() {
      const duration = 1000;
      $slides.animate({
        left: (currentIndex + 1) * -100 + '%'
      },duration);
      if(currentIndex == slideLength){
        currentIndex = 0;
        $slides.animate({
          left: (currentIndex + 1) * -100 + '%'
        },0);
      }else if(currentIndex == -1) {
        currentIndex = slideLength -1;
        $slides.animate({
          left: (currentIndex + 1) * -100 + '%'
        },0);
      }
      updateNav();
    }

    //スタートタイマー
    function startTimer() {
      const interval = 1000;
      timer = setInterval(nextSlide, interval);
    }

    //ストップタイマー
    function stopTimer() {
      clearInterval(timer);
    }

    //インジケータの更新
    function updateNav() {
      $indicatorItem.removeClass("is-active").eq(currentIndex).addClass("is-active");
    }

    //インジケータークリック処理
    function goSomeWhere() {
      if(!$slides.is(':animated')){
        currentIndex = $(this).index();
        changeSlide();
      }
    }
    //prevボタンの処理
    function prevSlide() {
      if(!$slides.is(':animated')){
        currentIndex--;
        changeSlide();
      }
    }
    //nextボタンの処理
    function nextSlide() {
      if(!$slides.is(':animated')){
        currentIndex++;
        changeSlide();
      }
    }

    //クリック、マウスイベント群
    function setEvent() {
      $slider.on({
        mouseenter: stopTimer,
        mouseleave: startTimer
      });
      $prev.on('click', prevSlide);
      $next.on('click', nextSlide);
      $indicatorItem.on('click', goSomeWhere);
    }

    //初期発火イベント群
    function init() {
      addIndicator();
      cloneSlide();
      setEvent();
      startTimer();
    }
    //init発火
    init();
  }
  //slider発火
  slider();
});

根幹となるスライドアニメーションの関数作成

作成していく順番としてまず最初は根幹となるスライドアニメーションの関数を作成していきます。

とりあえず単純に時間経過で右にスライドさせる動きを実装していきます。

$(function () {

  function slider() {

    //変数の設定
    const $sliderWrap = $('#sliderWrap');
    const $slider = $sliderWrap.find('.slider');
    const $slides = $slider.find('.slides');
    let currentIndex = 0;

    //スライドアニメーション
    function changeSlide() {
      const duration = 1000;
      currentIndex++;
      $slides.animate({
        left: (currentIndex) * -100 + "%"
      },duration);
    }
    //タイマースタート
    function startTimer() {
      const interval = 1000;
      timer = setInterval(changeSlide, interval);
    }
    startTimer();
  }
  slider();
});

 

変数の宣言について

1.起点となる親クラスを$('クラス名');で取得し、変数に格納させること

2.その親クラスからfindメソッドで子クラスを取得し、変数を格納させること


こうすることで、1回だけでDOMから要素を取得できるので、処理がより軽くなります。

挙動の確認

挙動をブラウザで確認すると、右にスライドする動きになっていると思います。ただ現状だとスライドの上限に達すると無限に右にスライドしてしまいます。

 

無限にスライドし続けるように改善

    //スライドアニメーション
    function changeSlide() {
      const duration = 1000;
      console.log(currentIndex);
      $slides.animate({
        left: (currentIndex) * -100 + "%"
      },duration);
      currentIndex++;
      if(currentIndex == slideLength){
        currentIndex = 0;
      }
    }

ifでスライドの上限に達した場合、currentIndexを0に初期化する処理を追加します。

そうすることでスライドが最初に戻り、無限スライドが出来上がります。

ですがこのままだと、最後のスライドから最初のスライドに切り替わるとき、動きがぎこちない感じなので次はそこをブラッシュアップしていきます。

現状の挙動↓

スライドの動きを滑らかにする

最後のスライドから最初のスライドに切り替わるときの挙動が、現状だとギュルルっと巻き戻るような感じですが、これをスムーズな挙動に直していきます。

こんな感じの挙動にしていきます↓

 

スライドをクローンする

前回スライドショーの記事にも書きましたが、一応こっちの記事でも説明加えておきます。

    //最初と最後のスライドをクローン
    function cloneSlide() {
      const $lastSlide = $slides.find("li:last-child");
      const $firstSlide = $slides.find("li:first-child");
      $lastSlide.clone(true).prependTo($slides);
      $firstSlide.clone(true).appendTo($slides);
    }


最初に表示させたいスライドと最後に表示させたいスライドをclone(複製)します。cloneしたスライドをappendTo(後),prependTo(前)で$('.slides')に追加します。

 

クローンする理由

表示させたい画像を、前後に持ってくることで、滑らかな挙動の無限スライドを実現できます。

注意したいのが、クローン関数によってスライドのインデックスが表示させたいスライドと一個ずれます。これはクローンされた要素が追加されるためです。

簡単に図で表すとこんな感じです↓

 

よって、cloneSlide4のインデックスは0、slide1のインデックスは1になります。なのでコードは以下のようになります。

if文の条件がtrueになったら、indexに0(初期位置)を代入して、表示するスライド位置をanimateメソッドで移動させます。移動にかかる時間は0を設定してください。

0とすることでアニメーションにかかる時間を一瞬で実行できます。これでスライドアニメーションが滑らかな挙動になります。

    //スライドアニメーション
    function changeSlide() {
      const duration = 1000;
      currentIndex++;
      $slides.animate({
        left: (currentIndex+1) * -100 + "%"
      },duration);
      
      if(currentIndex == slideLength){
        currentIndex = 0;
        $slides.animate({
          left: (currentIndex + 1) * -100 + '%'
        },0);
      }
    }

 

インジケーターの生成

インジケーターをJS側で生成していきます。JS側でHTML生成する理由として、スライド数に応じて可変で生成できる点です。HTMLをいちいち書き換える必要なくなるので楽になります。

    //インジケーターの生成
    function addIndicator(){
      indicatorHTML = "<li class=item></li>";
      for (let i = 0; i < slideLength; i++){
        $indicator.append(indicatorHTML);
      }
      $item = $(".item");
      $item.eq(currentIndex).addClass("is-active");
    }

$item.eq(currentIndex).addClass("is-active");

このコードは初期位置となるインジケータにis-activeクラス(色変更)を付与する処理です。

$itemのインデックス番号とcurrentIndexの値が一緒の要素にaddClassを付与するといった処理で、この場合は一個目に色(白)がつきます↓

 

 

スライド移動に伴い、インジケーターもそれに応じて移動させる

    //スライドアニメーション
    function changeSlide() {
    ~~略~~
      updateNav(); //updateNav()関数の発火
    }

    //現在のスライド位置をインジケーターに表示
    function updateNav() {
      $item.removeClass("is-active");
      $item.eq(currentIndex).addClass("is-active");
    }


changeSlide()内のupdateNav()関数が発火するといった流れになります。

現在位置で色が変わる処理は単純にremoveメソッドとaddClassメソッドで付け替えしているだけです。

現状の挙動↓

 

タイマーのストップ機能

スライドショーにマウスを乗せるとタイマーがストップするようにしていきます。

    //タイマーの一時停止
    function stopTimer() {
      clearInterval(timer);
    }

    function setEvent() {
      $slides.on({
        mouseenter: stopTimer,
        mouseleave: startTimer
      });
    }

setEventでイベント実行関数をまとめています。

mouseenter マウスポインタが対象要素に被った時の処理を実行。

mouseleave マウスポインタが対象要素から外れた時の処理を実行。

 

矢印ボタン押下で、その方向にスライドさせる

    //nextボタンの処理
    function nextSlide() {
      if(!$slides.is(":animated")){
        currentIndex++;
        changeSlide();
      }
    }

    function setEvent() {
      $slides.on({
        mouseenter: stopTimer,
        mouseleave: startTimer
      });
      // $prev.on('click', prevSlide);
      $next.on('click', nextSlide);
      $indicatorItem.on('click', goSomeWhere);
    }

単純にcurrentIndexの値を加減する処理を行なっています。
この値はleft: (currentIndex+1) * -100 + "%" の部分に入る変数でスライドの位置を決めるものになります。


ifの部分はクリック連打防止のために記述

[セレクタ名]:is(:animated)」で、セレクタ要素内でアニメーション実行中の場合のみ処理します。
今回は連打の防止を目的としているため、アニメーション実行中の場合は処理しないようにしたい。

そのためには、上記のセレクタ名の前に否定演算子を置くことで、アニメーション実行中の場合は処理しないようになります。      

if(!$[セレクタ].is(":animated")){
  //アニメーション実行中は処理しない
}

 

 

まとめ

だいぶ長くなりましたがこれで終わりです。リファクタリングって難しいです。

チームで開発する際、単純にコードが短ければいいってものでもないらしい。あくまで他人が見て理解しやすいかどうかが重要で、それが一番難しかったり。。さじ加減が難しいですね。

初学者の方は本ではなく世界最大級のオンライン学習サイトUdemy などのオンライン学習がおすすめです。
プログラミングの技術は日進月歩なので、本だとどうしても情報が古くなりがちでサンプルコードが動かないってことが往々にしてあります。その点オンライン学習は最新情報を取り入れられます!
技術はもちろんマーケティングの教材などがたくさんあり、レビューもさかんなので一回使ってみるといいかもです。