home
twitter
ページトップへ
目次
  • 「スライダー」とは何か?
  • 「スライダー」の作り方!
  • 基本的なスライダーの作り方!
  • ナビゲーションのためのボタンを追加!
  • 時間経過で自動的に次のスライドに移動させる!
  • 関連リンク!
  • おすすめの再生リスト!
  • まとめ!
about
categories
page
archive
皇帝ペンギンブログ

2025/01/30 (更新日:2025/02/01)

【JavaScript】ライブラリを使わない!シンプルな「スライダー」の作り方!【カルーセル】

Categories > JavaScriptの入門編
こんばんは!初めましての方は初めまして!
しゅーた(@chibasyuta)です!

この記事では「スライダー」の作り方を解説します!「カルーセル」とも呼ばれるやつですね。

「スライダー」の作り方には様々なものがり、「Swiper」や「jQuery」などのライブラリを使う方法もありますが、この記事で扱うのは、そういった類のものは使わず、そのままの「JavaScript」で実装する、というものになります。

「JavaScript」の理解に繋がるだろうし、「JavaScript」だけで作った方がカスタマイズ性に優れていたりします。

実務では「Swiper」を使う場合も多いと思いますが、「JavaScript」だけによる「スライダー」の実装方法を知っておくと「Swiper」の使い方のさらなる理解に繋がるとも思うので、ぜひ挑戦してみてください!

この記事の内容!

  • 「スライダー」とは何か?
  • 「スライダー」の作り方!
  • 自動再生機能の追加方法
  • 「プログレスバー」の追加方法!
また、下の動画でも「スライダー」の作り方について詳しく解説しているので、一緒に実際にコードを書きながら学びたい方はこちらをどうぞ!

動画の方が細かいステップに分けて解説していますし、細かい解説も多くつけ加えているので、分かりやすさという点では動画の方がおすすめです!もう既に作った経験がある方や基本来なロジックが分かる方は、ブログ記事版の方が素早く作れると思います!

それでは、内容へ入ります!

目次
非表示
  • 「スライダー」とは何か?
  • 「スライダー」の作り方!
  • 基本的なスライダーの作り方!
  • ナビゲーションのためのボタンを追加!
  • 時間経過で自動的に次のスライドに移動させる!
  • 関連リンク!
  • おすすめの再生リスト!
  • まとめ!

「スライダー」とは何か?

https://chibasyuta.org/wp-content/uploads/2025/01/what-carousel_new.mp4

Web制作における「スライダー」や「カルーセル」とは、複数のコンテンツ(画像やテキストなど)を一定の間隔で切り替えながら表示するインターフェースのことを指します。

これらはWebサイトのデザインでよく使われる手段で、限られたスペースにコンテンツを収めたい場合に活用できます。

また、動きのコンテンツはユーザーの目につくので、コンテンツを目立たせることができます。

デメリットとしては、表示されないコンテンツがあるので、スルーされるとユーザーに届かない可能性もある、ということですかね。

とにかく、それが「スライダー」や「カルーセル」です。以後この記事では「スライダー」と統一して呼ぶことにします。

「スライダー」の作り方!

それでは、スライダーの作り方をまとめます!
わーい!

まずは基本的な実装から始めて、段階的に機能を追加していこうと思います。

皆さんも順番に作り上げてもらった方が、作りやすいと思いますね。いろんな実装に慣れている方は、好きな方法で進めてください!

基本的なスライダーの作り方!

https://chibasyuta.org/wp-content/uploads/2025/01/base-slider_new.mp4
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>

<div class="carousel">
  <div class="container">
    <ul>
      <li><img src="./img/portfolio1_new.jpg"></li>
      <li><img src="./img/portfolio2_new.jpg"></li>
      <li><img src="./img/portfolio3_new.jpg"></li>
      <li><img src="./img/portfolio4_new.jpg"></li>
    </ul>
  </div>
  <div class="button-box">
    <button id="prev">&lt;</button>
    <button id="next">&gt;</button>
  </div>
</div>

<script src="test.js"></script>
</body>
</html>
@charset "utf-8";

html {
    font-size: 16px;
}
* {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}

.carousel {
    overflow: hidden;
    width: 500px;
    height: 450px;
    margin: 64px auto;
    position: relative;
}
.container {
    height: 350px;
    font-size: 40px;
    overflow: hidden;
    margin-bottom: 20px;
}
ul {
    display: flex;
    list-style: none;
    height: 100%;
    transition-duration: .6s;
}
li {
    width: 100%;
    height: 100%;
    min-width: 100%;
}
img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}
.button-box {
    text-align: right;
}
button {
    border: none;
    padding: 8px 16px;
    font-size: 30px;
    cursor: pointer;;
}
button:hover {
    background-color: black;
    color: white;
}
"use strict";


const ul = document.querySelector('ul');
const slides = ul.children;
const prev = document.getElementById('prev');
const next = document.getElementById('next');

let currentIndex = 0;

prev.addEventListener('click', () => {
    currentIndex--;
    currentIndex = currentIndex % slides.length;
    moveSlide();
});
next.addEventListener('click', () => {
    currentIndex++;
    currentIndex = currentIndex % slides.length;
    moveSlide();
});
function moveSlide() {
    if ( currentIndex < 0 ) {
        currentIndex = slides.length - 1;
    }
    const slideWidth = slides[0].getBoundingClientRect().width;
    ul.style.transform = `translateX(${-1 * slideWidth * currentIndex}px)`;
}

ここでは「JavaScript」の解説だけをします。

「currentIndex」という変数で「今表示している画像はどれなのか」を管理します。

「ul」を動かすことで、今表示されている画像を変更するというもので、その部分の基本的なロジックは下記です。

const slideWidth = slides[0].getBoundingClientRect().width;
ul.style.transform = `translateX(${-1 * slideWidth * currentIndex}px)`;

この部分でスライドの幅を取得して、何枚分のスライドを動かすのか、というのを「currentIndex」と連携することで実現しています。

currentIndex が増えすぎても大丈夫なように調整
currentIndex = currentIndex % slides.length;

また「currentIndex 」をこのように少しいじることで、常にどれかのスライドが表示されるようにしています。スライドの枚数を超えてcurrentIndex が増えそうになったら、最初のスライドに戻るような仕組みを「%」という剰余演算子で作っています。

currentIndex がマイナスになることを防ぐ調整
if ( currentIndex < 0 ) {
        currentIndex = slides.length - 1;
}

「currentIndex」がマイナスになることを防ぐためには、このif文を用いています。

ナビゲーションのためのボタンを追加!

https://chibasyuta.org/wp-content/uploads/2025/01/nav_button_new.mp4
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>

<div class="carousel">
  <div class="container">
    <ul>
      <li><img src="./img/portfolio1_new.jpg"></li>
      <li><img src="./img/portfolio2_new.jpg"></li>
      <li><img src="./img/portfolio3_new.jpg"></li>
      <li><img src="./img/portfolio4_new.jpg"></li>
    </ul>
  </div>
  <div class="button-box">
    <button id="prev">&lt;</button>
    <button id="next">&gt;</button>
  </div>
  <div class="nav"></div>
</div>

<script src="test.js"></script>
</body>
</html>
@charset "utf-8";

html {
    font-size: 16px;
}
* {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}

.carousel {
    overflow: hidden;
    width: 500px;
    height: 450px;
    margin: 64px auto;
    position: relative;
}
.container {
    height: 350px;
    font-size: 40px;
    overflow: hidden;
    margin-bottom: 20px;
}
ul {
    display: flex;
    list-style: none;
    height: 100%;
    transition-duration: .6s;
}
li {
    width: 100%;
    height: 100%;
    min-width: 100%;
}
img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}
.button-box {
    text-align: right;
}
button {
    border: none;
    padding: 8px 16px;
    font-size: 30px;
    cursor: pointer;;
}
button:hover {
    background-color: black;
    color: white;
}
.nav {
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    text-align: center;
}
.nav button {
    width: 40px;
    height: 40px;
    background-color: #ddd;
    border-radius: 50%;
}
.nav button + button {
    margin-left: 8px;
}
.nav button.current {
    background-color: black;
}
"use strict";


const ul = document.querySelector('ul');
const slides = ul.children;
const prev = document.getElementById('prev');
const next = document.getElementById('next');
const nav = document.querySelector('.nav');
const navButton = [];

let currentIndex = 0;

createNavButtons();

prev.addEventListener('click', () => {
    currentIndex--;
    currentIndex = currentIndex % slides.length;
    moveSlide();
    updateNavBtn()
});
next.addEventListener('click', () => {
    currentIndex++;
    currentIndex = currentIndex % slides.length;
    moveSlide();
    updateNavBtn()
});
function moveSlide() {
    if ( currentIndex < 0 ) {
        currentIndex = slides.length - 1;
    }
    const slideWidth = slides[0].getBoundingClientRect().width;
    ul.style.transform = `translateX(${-1 * slideWidth * currentIndex}px)`;
}
function createNavButtons() {
    for (let i = 0; i < slides.length; i++) {
        const button = document.createElement('button');
        if ( i === currentIndex ) {
            button.classList.add('current');
        }
        navButton.push(button);
        button.addEventListener('click', () => {
            currentIndex = i;
            moveSlide();
            navButton.forEach( btn => {
                btn.classList.remove('current');
            } );
            navButton[currentIndex].classList.add('current');
        });
        nav.appendChild(button);
    }
}
function updateNavBtn() {
    navButton.forEach( btn => {
        btn.classList.remove('current');
    } );
    navButton[currentIndex].classList.add('current');
}

まずHTMLファイルに「nav」というクラス属性を持った要素を追加します。

そして、「createNavButtons()」という関数を作り、そこでボタンを作り追加するという実装にしました。これは画像の数が今後増えても対応できるようにするためです。

追加するボタンを配列に入れておいて管理!
const navButton = [];

配列に参照情報を持たせておくことで、そこから扱うことができるので便利です。

ナビゲーションのボタンをクリックした時の挙動!

クリックしたボタンに応じて「currentIndex」を更新したあと、「moveSlide()」を実行できるようにします。

「#prev」と「#next」のボタンを押した時の挙動も追加!

「#prev」と「#next」のボタンを押した時に、ナビゲーションのボタンのスタイルも変更されるように「updateNavBtn()」という関数を作り、実行できるようにします。

時間経過で自動的に次のスライドに移動させる!

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>

<div class="carousel">
  <div class="container">
    <ul>
      <li><img src="./img/portfolio1_new.jpg"></li>
      <li><img src="./img/portfolio2_new.jpg"></li>
      <li><img src="./img/portfolio3_new.jpg"></li>
      <li><img src="./img/portfolio4_new.jpg"></li>
    </ul>
  </div>
  <div class="button-box">
    <button id="prev">&lt;</button>
    <button id="next">&gt;</button>
  </div>
  <div class="text"></div>
  <div class="nav"></div>
  <div class="progress-bar">
    <div class="progress"></div>
  </div>
</div>


<script src="js.js"></script>
</body>
</html>
.progress-bar {
    width: 100px;
    height: 20px;
    background-color: #ddd;
    position: absolute;
    bottom: 50px;
}
.progress {
    width: 0;
    height: 100%;
    background-color: black;
}
.progress.go {
    width: 100%;
    transition-duration: 2s;
    transition-timing-function: linear;
}

これらのCSSファイルを追加

"use strict";



const ul = document.querySelector('ul');
const prev = document.getElementById('prev');
const next = document.getElementById('next');
const slides = ul.children;
const nav = document.querySelector('.nav');
const navButton = [];
const progress = document.querySelector('.progress');

let currentIndex = 0;
let autoPlayId;

createNavButtons();

prev.addEventListener('click', () => {
    currentIndex--;
    currentIndex = currentIndex % slides.length;
    moveSlide();
    updateNavBtn();
    clearInterval( autoPlayId );
    restartProgress();
    autoPlay();
});
next.addEventListener('click', () => {
    currentIndex++;
    currentIndex = currentIndex % slides.length;
    moveSlide();
    updateNavBtn();
    clearInterval( autoPlayId );
    restartProgress();
    autoPlay();
});

function moveSlide() {
    if ( currentIndex < 0 ) {
        currentIndex = slides.length - 1;
    }
    const slideWidth = slides[0].getBoundingClientRect().width;
    ul.style.transform = `translateX(${-1 * slideWidth * currentIndex}px)`;
    document.querySelector('.text').textContent = (currentIndex);
}
function createNavButtons() {
    for (let i = 0; i < slides.length; i++) {
        const button = document.createElement('button');
        if ( i === currentIndex ) {
            button.classList.add('current');
        }
        navButton.push(button);
        button.addEventListener('click', () => {
            currentIndex = i;
            moveSlide();
            navButton.forEach( btn => {
                btn.classList.remove('current');
            } );
            navButton[currentIndex].classList.add('current');
            clearInterval( autoPlayId );
            restartProgress();
            autoPlay();
        });
        nav.appendChild(button);
    }
}
function updateNavBtn() {
    navButton.forEach( btn => {
        btn.classList.remove('current');
    } );
    navButton[currentIndex].classList.add('current');
    restartProgress();
}


autoPlay();
restartProgress();
function autoPlay() {
    autoPlayId = 
    setInterval(() => {
        currentIndex++;
        currentIndex = currentIndex % slides.length;
        moveSlide();
        updateNavBtn();
        restartProgress();
    }, 2000);
}
function restartProgress() {
    progress.classList.remove('go');
    setTimeout(() => {
        progress.classList.add('go');
    }, 100);
}

「autoPlay()」と「restartProgress()」を作り、それぞれ必要なタイミングで呼び出します。

ボタンのクリックなどで画像を手動で切り替えた場合、「clearInterval( autoPlayId );」のようにして、自動的に次のスライドに切り替わるのをキャンセルし、また再び新しいタイマーを起動させる、というような実装です。

関連リンク!

これらの記事もおすすめです。

【JavaScript】「アコーディオンメニュー」をライブラリなしで作る方法【アニメーションつき】

関連記事

【JavaScript】「ライブラリーを使わずに「ハンバーガーメニュー」を作る方法!【分かりやすい】

関連記事

【JavaScript】ライブラリを使わない「タブメニュー」の作り方!【シンプル】

関連記事

【JavaScript】モーダルウィンドウの作り方!【分かりやすい】

関連記事

おすすめの再生リスト!

こちらは僕が制作したYouTubeの再生リスト「JavaScriptの入門編」です。

「JavaScriptの入門編」はWeb制作を勉強している方がJavaScriptに入門する際に知っておきたい情報をまとめたコンテンツとなります。

こちらの再生リストで「JavaScript」について「丁寧」に解説しているので、もし気になる方はチェックしてみてください!一緒に楽しく学習しましょう!!

まとめ!

今回は段階的にスライダーの作り方の解説をしました!

もっと細かい解説が必要な場合は、僕のYouTubeの動画をご視聴ください!また、質問などがありましたらコメントをお気軽にどうぞ!

また、下の動画でも「スライダー」の作り方について詳しく解説しているので、一緒に実際にコードを書きながら学びたい方はこちらをどうぞ!

それではまた!

あ、あと因みに、この前チャンネル登録者数1,000人達成できました!っていうね!ありがとう!

自分のロゴ!

皇帝ペンギン

全力で、書きます。
少しでもお役に立てたら幸いです。
楽しんで読んで下さい 。-_- 。
よろしくお願いします!

x(旧twitter) youtube
Categories
  • トップへ
  • ITパスポート (1)
  • programming (168)
    • CSSのアニメーション編 (26)
    • CSSのグリッドレイアウト編 (1)
    • CSSのセレクター編 (58)
    • CSSのフレックスボックス編 (1)
    • CSSのメディアクエリー編 (7)
    • CSSの基本編 (34)
    • HTMLのformタグ編 (1)
    • HTMLの基本編 (3)
    • JavaScriptの入門編 (30)
    • JavaScriptの基本編 (5)
    • Web制作をする環境を整えよう! (1)
  • Uncategorized (23)
  • VScode (1)
  • おすすめ商品! (1)
  • クラロワ (2)
  • マーケティング (17)
  • 大学生活 (7)
  • 心理学 (9)
  • 映画 (1)
  • 法学 (4)
  • 経済学 (4)
  • 読書 (7)
Pages
  • プライバシーポリシー
  • 免責事項
  • プロフィール
Archive
  • 2025年3月 (1)
  • 2025年1月 (9)
  • 2024年12月 (13)
  • 2024年11月 (5)
  • 2024年10月 (10)
  • 2024年9月 (15)
  • 2024年8月 (5)
  • 2024年7月 (6)
  • 2024年6月 (5)
  • 2024年5月 (10)
  • 2024年4月 (24)
  • 2024年3月 (11)
  • 2024年2月 (6)
  • 2024年1月 (10)
  • 2023年12月 (4)
  • 2023年11月 (3)
  • 2023年10月 (2)
  • 2023年9月 (3)
  • 2023年8月 (6)
  • 2023年7月 (3)
  • 2023年6月 (8)
  • 2023年5月 (2)
  • 2023年3月 (1)
  • 2023年2月 (1)
  • 2022年10月 (2)
  • 2022年9月 (2)
  • 2022年8月 (8)
  • 2022年7月 (2)
  • 2022年6月 (6)
  • 2022年5月 (9)
  • 2022年4月 (2)
  • 2022年2月 (4)
  • 2021年12月 (1)
  • 2021年11月 (4)
  • 2021年10月 (8)
  • 2021年9月 (14)
  • 2021年8月 (6)
  • 2021年6月 (1)
  • 2021年5月 (2)
  • 2021年4月 (1)
  • 2021年3月 (1)
  • 2021年2月 (2)
  • 2020年10月 (1)
  • 2020年7月 (1)
  • 2020年4月 (1)
  • 2020年1月 (1)
  • 2019年12月 (2)
© Copyright Syuta Chiba, 2019 All Rights Reserved.