JavaScript スライドショーにオートプレイ機能をつける / もりけん塾 JavaScript課題18

JavaScript スライドショーにオートプレイ機能をつける  / もりけん塾 JavaScript課題18

前回は、スライドショーにドットページネーションをつけました。今回は、スライドショーにオートプレイ機能をつけていきます。

前回の記事はこちら

仕様の確認

スライドショーにドットのページネーションを作りましょう
それぞれのドットではクリッカブルになっていて、押下するとその画像に切り替わります。
それとともに1/5も切り替わります。
また、3秒毎に次のスライドに自動で切り替わるauto機能を提供してください。←今回の記事ではここの機能を追加します。

オートプレイ機能の実装方法

オートプレイ機能は、setInterval()メソッドで実装していきます。

setInterval()を実行すると、繰り返し同じ処理を一定間隔で繰り返す、タイマー機能をつけることができます。serInteval()の実行を止めたい時は、clearInterval()メソッドを使います。

setTimeOut()というメソッドもあり、こちらは一定間隔で一回のみ処理するタイマー機能をつけることができます。

  • setTimeOut() 1回のみのタイマー機能
  • setInterval() 繰り返しのタイマー機能
  • clearInterval() タイマー機能の停止

https://developer.mozilla.org/ja/docs/Web/API/setInterval

setInterval()メソッドの使い方

下記のような書き方で使うことができます。

let count = 0;
const ms  = 3000; //タイマーの秒数
function counter(){
 count += 3;
 console.log(`${count}秒経過しました`);
}
setInterval(counter,ms);//myFuncの処理を3秒ごとに繰り返す

アロー関数で書いた場合

let count = 0;
const ms  = 3000; //タイマーの秒数
const counter = setInterval(() => {
 count += 3;
 console.log(`${count}秒経過しました`);
},ms);

スライドショーのオートプレイ機能の場合は、スライドを切り替える処理をsetInterval() にセットして、繰り返し自動で切り替えができるようにします。

まず、スライドショーの切り替えに必要な関数を用意します。こちらをsetInterval()関数で実行したいと思います。

const switchSlider = (sliderData) => {
    changeCurrentNumber();
    switchSliderItems();
    switchPaginations();
    switchDisabledButton(sliderData);
}

スライドが最後のスライド画像になったら、最初の画像に戻るように、条件分岐を入れておきました。

const autoSwitchSlider = (sliderData) =>{
    sliderStatus.autoPlay = setInterval(() =>{
        sliderStatus.currentIndex ++;
        if(sliderStatus.currentIndex === sliderData.length){
            sliderStatus.currentIndex = 0;
        }
        switchSlider(sliderData);
    }, 3000);
}

//タイマークリア
const resetAutoPlay = (sliderData) => {
    clearInterval(sliderStatus.autoPlay);
    autoSwitchSlider(sliderData);
}

ページネーションをクリックした時は、一旦スライドショーのオートプレイをストップさせて、再度スタートするように、クリックイベントのイベントリスナーにも登録しておきます。

const addClickEventForPagination = (sliderData) => {
    const paginations      = document.getElementById("js-pagination");
    const arrayPaginations = [...document.getElementsByClassName("js-pagination__item")];

        paginations.addEventListener("click",function(e){
            //イベントハンドラを登録した要素は何もしない
            if(e.currentTarget === e.target){
                return;
            }
            sliderStatus.currentIndex = arrayPaginations.indexOf(e.target);

            switchSlider(sliderData);
            resetAutoPlay(sliderData);
        });
}

最終的にApprove頂いたコードはこちらです。長いっ。

const slider = document.getElementById("js-slider");
const sliderStatus = {
    currentIndex: 0,
    autoPlay:""
}

const createAttributedElements = ({tag,attrObj,str}) =>{
    const element = document.createElement(tag);
    Object.keys(attrObj).forEach((attribute) => {
        element.setAttribute(attribute, attrObj[attribute]);
    });
    if(str !== undefined) element.textContent = str;
    return element;
}

const createErrorMessage = (error) => {
    const errMsg = createAttributedElements({tag:"p",attrObj:{class:"error-msg"},str:error});

    slider.appendChild(errMsg);
}

const showLoading = () => {
    const loadingPlace = createAttributedElements({tag:"div",attrObj:{id:"js-loading",class:"loading"}});
    const loadingImage = createAttributedElements({tag:"img",attrObj:{src:"img/loading-circle.gif",alt:"ローディング"}});

    slider.appendChild(loadingPlace).appendChild(loadingImage);
}

const removeLoading = () => document.getElementById("js-loading").remove();

const fetchData = async (URL) => {
    const response = await fetch(URL);
    if(!response.ok){
        console.error(`${response.status}:${response.statusText}`);
        createErrorMessage('現在、サーバーの通信が壊れています');
    }
    const json = await response.json();
    return json.data;
}

const fetchSliderData = (ms) => new Promise(resolve => setTimeout(() => resolve(fetchData("https://myjson.dit.upm.es/api/bins/2bzx")),ms));

const init = async () => {
    showLoading();
    try{
        const sliderData = await fetchSliderData(3000);
        if (!sliderData) {
            return;
        }
        renderSliderElements(sliderData);
        autoSwitchSlider(sliderData);
    }catch(error){
        console.error(error);
        createErrorMessage(error);
    }finally{
        removeLoading();
    }
}

const renderSliderElements = (sliderData) => {
    renderSliderItems(sliderData);
    getPageNumber(sliderData);
    renderButtons(sliderData);
    renderPagination(sliderData);
    isActiveFirstItem();
}

const autoSwitchSlider = (sliderData) =>{
    sliderStatus.autoPlay = setInterval(() =>{
        sliderStatus.currentIndex ++;
        if(sliderStatus.currentIndex === sliderData.length){
            sliderStatus.currentIndex = 0;
        }
        switchSlider(sliderData);
    }, 3000);
}

const resetAutoPlay = (sliderData) => {
    clearInterval(sliderStatus.autoPlay);
    autoSwitchSlider(sliderData);
}

const renderSliderItems = (sliderData) => {
    const fragment = document.createDocumentFragment();
    for(const image of sliderData){
        const sliderItem = createAttributedElements({tag:"li",attrObj:{class:"js-slider__item slider__item"}});
        const slideImage = createAttributedElements({tag:"img",attrObj:{class:"slide-image",src:image.img,alt:image.alt}});

        fragment.appendChild(sliderItem).appendChild(slideImage);
    }

    slider.appendChild(fragment);
}

const getPageNumber = (sliderData) => {
    const pageNumber    = createAttributedElements({tag:"p",attrObj:{class:"page-number"},str:`/${sliderData.length}`});
    const currentNumber = createAttributedElements({tag:"span",attrObj:{id:"js-currentNumber",class:"current-number"},str:"1"});

    slider.appendChild(pageNumber).insertAdjacentElement("afterbegin",currentNumber);
}

const renderButtons = (sliderData) => {
    const prevBtn = createAttributedElements({tag:"button",attrObj:{id:"js-prevBtn",class:"js-btn prev-btn slider-btn",disabled:true}});
    const nextBtn = createAttributedElements({tag:"button",attrObj:{id:"js-nextBtn",class:"js-btn next-btn slider-btn"}});

    slider.appendChild(prevBtn).after(nextBtn);
    addClickEventForButton(sliderData);
}

const renderPagination = (sliderData) => {
    const paginationList = createAttributedElements({tag:"ul",attrObj:{id:"js-pagination",class:"pagination"}});

    const fragment = document.createDocumentFragment();
    const length   = sliderData.length;
    for(let i = 0; i < length; i++){
        const paginationItem = createAttributedElements({tag:"li",attrObj:{class:"js-pagination__item pagination__item"}});
        fragment.appendChild(paginationItem);
    }

    slider.appendChild(paginationList).appendChild(fragment);
    addClickEventForPagination(sliderData);
}

const addClickEventForButton = (sliderData) => {
    const sliderButtons = document.getElementsByClassName("js-btn");

    for (const button of sliderButtons ){
        button.addEventListener("click",function(){
            this.id === "js-nextBtn" ? sliderStatus.currentIndex++ : sliderStatus.currentIndex--;

            switchSlider(sliderData);
            resetAutoPlay(sliderData);
        });
    }
}

const addClickEventForPagination = (sliderData) => {
    const paginations      = document.getElementById("js-pagination");
    const arrayPaginations = [...document.getElementsByClassName("js-pagination__item")];

        paginations.addEventListener("click",function(e){
            //イベントハンドラを登録した要素は何もしない
            if(e.currentTarget === e.target){
                return;
            }
            sliderStatus.currentIndex = arrayPaginations.indexOf(e.target);

            switchSlider(sliderData);
            resetAutoPlay(sliderData);
        });
}

const isActiveFirstItem = () => {
    document.querySelector(".js-slider__item").classList.add("is-active");
    document.querySelector(".js-pagination__item").classList.add("is-active");
}

const switchSlider = (sliderData) => {
    changeCurrentNumber();
    switchSliderItems();
    switchPaginations();
    switchDisabledButton(sliderData);
}

const changeCurrentNumber = () => document.getElementById("js-currentNumber").textContent = `${sliderStatus.currentIndex +1}`;

const switchSliderItems = () => {
    const sliderItems = document.getElementsByClassName("js-slider__item");
    document.querySelector(".is-active").classList.remove("is-active");
    sliderItems[sliderStatus.currentIndex].classList.add("is-active");
}

const switchPaginations = () => {
    const paginations = document.getElementsByClassName("js-pagination__item");
    document.querySelector(".js-pagination__item.is-active").classList.remove("is-active");
    paginations[sliderStatus.currentIndex].classList.add("is-active");
}

const switchDisabledButton = (sliderData) => {
    const nextBtn    = document.getElementById("js-nextBtn");
    const prevBtn    = document.getElementById("js-prevBtn");
    const firstIndex = 0;
    const lastIndex  = sliderData.length -1;

    prevBtn.disabled = sliderStatus.currentIndex === firstIndex;
    nextBtn.disabled = sliderStatus.currentIndex === lastIndex;
}

init();

オブジェクトで管理したり、イベントの委譲や、setInterval()メソッドなど、たくさんのことを学びました!次はタブとスライドショーを合わせる課題です。

私が所属しているフロントエンドエンジニアを目指す方のための塾 「もりけん塾」の森田賢二先生のTwitterはこちら!

先生のブログ「武骨日記」はこちら!

https://kenjimorita.jp/

まい

Webサービス制作会社で、Wordpressのテーマ開発や2Dシミュレーターの開発、JavaScriptを使用したフロントエンド周りの実装を担当しています。 JavaScriptが好きです。 最近は、3D Model Configuratorの制作にチャレンジしています。

もりけん塾カテゴリの最新記事