プログラミング・エンジニア転職

【jQuery】slickを使ってカウントダウン機能の付いたスライドショーを作りました

ネットサーフィン中に、カウントダウン機能を盛り込んだスライドショーを見つけたので、実装しました。

↑こんなヤツですね。

slickを使ったスライドショーの実装

バリエーション豊富で安定感抜群!スライダープラグイン slick | デザイン・web制作の魅力を伝えたい inbox design

スライドショーはjQueryのプラグインである「slick」で実装します。

公式サイトを参考にしつつ、自分のディレクトリ構造などと比較しながら、実装します。

<html>
 <head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0">
  <link rel="stylesheet" href="base.css">
  <link rel="stylesheet" type="text/css" href="slick/slick.css"/>
  <link rel="stylesheet" type="text/css" href="slick/slick-theme.css"/>
  <script type="text/javascript" src="jQuery-3.5.1.min.js"></script>
  <script type="text/javascript" src="slick/slick.min.js"></script>
 </head>
 
 <body>
  <div class="your-class">
   <div>your content</div>
   <div>your content</div>
   <div>your content</div>
  </div>
  
  <script>
   $(document).ready(function(){
    $('.your-class').slick({
    });
   });
  </script>
 </body>
</html>

僕の場合、上記のようなコードです。

ソウ

ディレクトリ構造が気になる方は、GitHubでご確認ください。

コードを書いて、「your content」と1つだけ表示されたら、slickの導入はうまくいっています。

上手く表示されてる例

一方で、次のような表示の場合、slickが上手く導入できてないので確認が必要です。

こういった場合の原因としては、

  • 参照先を間違えてる
  • CSSやjavascriptの記載順を間違えてる(slickは一番最後に書く)
  • 単純なコピペのミス
  • (CDNの場合)そもそもパソコンとネットが繋がってない

こういったものが考えられます。

ソウ

いずれにせよ、大半の原因はケアレスミスにあるようです

スライドショーのデザインの整形

さて、話を戻して…

O-DANで拾ってきたそれっぽい画像を使って、スライドショーを実装します。

  <div class="slider-outer">
    <div class="your-class">
      <div class="count-down">
        <img src="img/audience.jpg" alt="">
      </div>
      <img src="img/soccer.jpg" alt="">
      <img src="img/bouldering.jpg" alt="">
    </div>
  </div>

  <script>
    $(document).ready(function(){
      $('.your-class').slick({
        autoplay:true,
        autoplaySpeed: 3000,
        dots:true,
        fade: true,
        speed: 2000,
      });
    });
  </script>
.count-down >img{
  width: 100%;
}

@media screen and (min-width: 960px) {
  .slider-outer{
    width: 60%;
    margin: auto;
  }
}

@media screen and (max-width: 959px) {
  .slider-outer{
    width: 90%;
    margin: auto;
  }
}

余談ですが、div内に画像を入れる際は、画像自体に幅(width=100%)を指定しないと枠からはみ出るので注意です。

ソウ

下図のように、写真の下にドットが表示されてたらOKです

カウントダウン機能の作成

次にカウントダウン機能の設定です。

僕は新しい機能を実装する際、まずは新しい機能だけを単独で実装します。

そうしないと、エラーが起きたときに、

  • 新規機能のコード
  • 新規機能と既存機能との接続

いずれの部分に問題が起きてるのか、判断が出来なくなるからです。

なので、カウントダウン機能をスライドショーと切り離して実装するために、「index.html」を次のように修正します。

  <p id="countDownMsg"></p>

  <div class="slider-outer">
    <div class="your-class">
      <div class="count-down">
        <img src="img/audience.jpg" alt="">
      </div>
      <img src="img/soccer.jpg" alt="">
      <img src="img/bouldering.jpg" alt="">
    </div>
  </div>

  <script>
  // スライドショー本体のコード
    $(document).ready(function(){
      $('.your-class').slick({
        autoplay:true,
        autoplaySpeed: 3000,
        dots:true,
        fade: true,
        speed: 2000,
      });
    });

    // カウントダウン部分のコード
    $(document).ready(function(){
      var openingDay = new Date('2021-07-23T00:00:00.000+09:00');
      var openingCeremony = new Date('2021-07-23T20:00:00.000+09:00');
      var closingSession = new Date('2021-08-08T20:00:00.000+09:00');
      var times = 24 * 60 * 60 * 1000;

      setInterval(function(){
        var today = new Date();
        var diff = openingDay - today;
        var countDownDay = Math.ceil(diff / times);// 開幕当日までの残り日数 //

        if (today < openingDay) { // 開幕式当日までの間 //
          $("#countDownMsg").html("<p>オリンピック開幕まで<br>残り<span id='count-down-day'></span>日!</p>");
          $("#count-down-day").html(countDownDay);
        } else if(today < openingCeremony) { // 開幕式当日~開幕式前 //
          $("#countDownMsg").html("<p>オリンピック<br><span id='count-down-day'></span></p>");
          $("#count-down-day").html("本日開会!");
        } else if(today < closingSession ) { // 大会期間中 //
          $("#countDownMsg").html("<p>オリンピック<br>現在、<span id='count-down-day'></span></p>");
          $("#count-down-day").html("開催中!");
        } else { // 閉幕後 //
          return;
        }
      },1000);

    });
  </script>

こんなかんじですね。

jQueryの部分が長いので、動きをザックリ説明すると、

  1. 「開会式」など特定の日時を設定する(27~29行目)
  2. 現在の日時を取得する(33行目)
  3. ①と②の差を活用して、「あと〇日」の表示と作る(34~35行目)
  4. 現在日時に応じて、表示内容を変更する(37~49行目)

こんなかんじです。

画面としては次のような状態になってるかと思います。

ソウ

33行目の括弧内に適当な日付を入れて、残り日数の表示が変わればOKです

カウントダウン機能は悩んだ部分が多かったので、少し補足します。

時刻表示について

27~29行目で、「開会式」など特定の時刻を設定してますが、地味に曲者でした。

具体的には、

パソコンでは表示されるのに、スマホだと日時が表示されない!

という事態が生じました。

時刻表示は特定フォーマット以外はブラウザごとに扱いが異なるというjavascriptの特性が原因でした。

本来、全てのブラウザで表示するには、時刻表示を共通フォーマットの「ISO 8601 方式」で設定すべきでした。

なので、日付はISO8601方式に沿った形で記載してます。

else ifは前の条件が当然false

一番最初、40&43行目の「else if」の条件式は、「前の条件がfalse&〇〇」という記載をしてました。

具体的なコードにすると、

if (today < openingDay) {
 …
} else if(openingDay <= today && today < openingCeremony) {
 …
} else if(openingCeremony <= today && today < closingSession ) {
 …
} else { 
 …
}

こんなかんじです。

「少し冗長的な条件式だなぁ」と思って気付いたんですが、else if構文って、

  1. 条件式Aを判断する
  2. (条件式Aがfalseの場合)条件式Bを判断する
  3. (条件式Bがfalseの場合)条件式Cを判断する

こんな動きをするので、②の段階で「条件式A=false」は確定なんですよね。

ソウ

なので、完成したコードのように前の条件式と重なる部分は削除しました

完成したコードとデモ画面

最後にスライドショーとカウントダウン表示を合体して完成です。

完成したコードとデモ画面は次のとおり。

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0">
  <link rel="stylesheet" href="base.css">
  <link rel="stylesheet" type="text/css" href="slick/slick.css"/>
  <link rel="stylesheet" type="text/css" href="slick/slick-theme.css"/>
  <script type="text/javascript" src="jQuery-3.5.1.min.js"></script>
  <script type="text/javascript" src="slick/slick.min.js"></script>
  <title></title>
</head>

<body>
  <div class="slider-outer">
    <div class="your-class">
      <div class="count-down">
        <p id="countDownMsg"></p>
        <img src="img/audience.jpg" alt="">
      </div>
      <img src="img/soccer.jpg" alt="">
      <img src="img/bouldering.jpg" alt="">
    </div>
  </div>

  <script>
  // スライドショー本体のコード
    $(document).ready(function(){
      $('.your-class').slick({
        autoplay:true,
        autoplaySpeed: 3000,
        dots:true,
        fade: true,
        speed: 2000,
      });
    });

    // カウントダウン部分のコード
    $(document).ready(function(){
      var openingDay = new Date('2021-07-23T00:00:00.000+09:00');
      var openingCeremony = new Date('2021-07-23T20:00:00.000+09:00');
      var closingSession = new Date('2021-08-08T20:00:00.000+09:00');
      var times = 24 * 60 * 60 * 1000;

      setInterval(function(){
        var today = new Date();
        var diff = openingDay - today;
        var countDownDay = Math.ceil(diff / times);// 開幕当日までの残り日数 //

        if (today < openingDay) { // 開幕式当日までの間 //
          $("#countDownMsg").html("<p>オリンピック開幕まで<br>残り<span id='count-down-day'></span>日!</p>");
          $("#count-down-day").html(countDownDay);
        } else if(today < openingCeremony) { // 開幕式当日~開幕式前 //
          $("#countDownMsg").html("<p>オリンピック<br><span id='count-down-day'></span></p>");
          $("#count-down-day").html("本日開会!");
        } else if(today < closingSession ) { // 大会期間中 //
          $("#countDownMsg").html("<p>オリンピック<br>現在、<span id='count-down-day'></span></p>");
          $("#count-down-day").html("開催中!");
        } else { // 閉幕後 //
          return;
        }
      },1000);
    });
  </script>
</body>
</html>
.count-down{
  position: relative;
}
.count-down >p{
  display: block;
  position: absolute;
  text-shadow: 2px 2px 4px black;
  color: white;
  font-weight:bold;
  left: 10%;
}
.count-down >img{
  width: 100%;
}

@media screen and (min-width: 960px) {
  .slider-outer{
    width: 60%;
    margin: auto;
  }
  #count-down-day{
    color: tomato;
    font-size: 70px;
  }
  .count-down >p{
    font-size: 40px;
  }
}

@media screen and (max-width: 959px) {
  .slider-outer{
    width: 90%;
    margin: auto;
  }
  .count-down >p{
    font-size: 25px;
  }
  #count-down-day{
    color: tomato;
    font-size: 45px;
  }
}

デモ画面はこちら

まとめ

「まとめ」と書かれた画像

今回一番ハマったのは、「カウントダウンがsafariで表示されない」という部分です。

ソウ

やっぱりsafariは少し曲者ですね。笑

とはいえ、おかげで一つ成長できたわけだし、teratailデビューも果たしたので良しとします。