FlexSliderの画像をjsonで読み込む【javascript/Fetch】

JavaScript

スライダー表示にjQueryプラグインのFlexSliderを使っているのですが、カスタマイズしたのでメモ。

作業した内容は次の通りです!

完成したスライダーを確認する(デモ)

こちらが完成したスライダーです。ローディングアイコンを表示させるため、画像の読み込みを故意に遅らせて表示してます。

動作デモ

実際の動作はこちらでご確認ください!(別ウィンドウが開きます)

jsonファイルを確認する

スライダーの画像名と画像の説明(alt)をjsonにまとめたのがこちらです。
このデータを取り出して、HTMLに組み込んでいきます。

slider.json


[
    {
      "img": "slider_01.jpg",
      "alt": "サムネイル01"
    },
    {
        "img": "slider_02.jpg",
        "alt": "サムネイル02"
      },
      {
        "img": "slider_03.jpg",
        "alt": "サムネイル03"
      },
      {
        "img": "slider_04.jpg",
        "alt": "サムネイル04"
      },
      {
        "img": "slider_05.jpg",
        "alt": "サムネイル05"
      },
      {
        "img": "slider_06.jpg",
        "alt": "サムネイル06"
      },
      {
        "img": "slider_07.jpg",
        "alt": "サムネイル07"
      },
      {
        "img": "slider_08.jpg",
        "alt": "サムネイル08"
      },
      {
        "img": "slider_09.jpg",
        "alt": "サムネイル09"
      }
  ]

ソースコードを確認(全体)

HTML

スライダー部分は<li>で読み込みますが、この部分はjsonデータから要素を生成する為、空にしておきます。

HTML

<div class="flexslider">
    <ul class="slides">
        
    </ul>
</div>

javascript

FlexSliderのプラグインは、スライダーの要素を生成してから実行する必要があります。
その為、一旦関数createSlider()に処理をまとめておきます。後ほどPromiseで実行する順番を制御します。

//flexsliderプラグイン
function createSlider() {
        $('.flexslider').flexslider({
        animation: "slide",
    });			
}

//jsonデータ取得
async function fetchSliders() {
    const response = await fetch('slider.json');

    if(response.ok == true) {
        const slideData = await response.json();
        if(!slideData.length) {
            throw new Error('No slide data found');
        }
        return slideData;
    } else {
        throw new Error('The data could not be read');
    }
}

// 非同期で実行
async function init() {
    try {
        //slideデータ取得(json)
        const slides = await fetchSliders();
        //DOM作成
        for(const slide of slides) {
            const slideEml = document.createElement('li');
            const img = document.createElement('img');
            img.src = './images/' + slide.img;
            img.alt = slide.alt;
        
            slideEml.appendChild(img);
            slideWrap.appendChild(slideEml);
        }
// 非同期で実行
        const sliderElm = createSlider();
    } catch(e) {
        console.error(e);
    }		 
}
init();

jsonデータを取得する

fetch()メソッドで、取得したいjsonファイルのURLを指定します。
なお、読み込むファイル「slider.json」はjavascriptのファイルと同階層にあります。

fetch('slider.json').then(function(response) {
	return response.json();
}).then(function(json) {
	console.log(json);
})

上記のコードをコンソールで確認すると、slider.jsonの値が取得できています。

配列の中にオブジェクトが格納されているようです。
1番目のオブジェクトのプロパティ「img」の値を取り出すにはこのように記述します。

console.log(json[0].img); //slider_01.jpg

全ての値を取り出すにはfor文を使います。

for(const slide of json) {
	console.log(`img: ${slide.img}、alt: ${slide.alt}`)
}
すべtの値が取得できた

こちらの値を使ってHTML要素を作成します。

Fetch APIとは

そもそもfetch()メソッドとはなんぞや?ですが、Fetch APIの機能らしいです。
さらにFetch APIとはなんぞや?ですが、外部のデータをやり取りする為の機能がまとまったモノらしいです。平たくいうと非同期でサーバーのデータを取得できる機能です。

fetch(‘slider.json’)の戻り値をコンソールで確認すると、Promiseオブジェクトであることが分かります。戻ってきた値を確認すると、Stateがfulfilled(条件が満たされている)、OKの項目が「true」、statusが「200(正しく表示)」です。データがうまく取れている感じがします!

Promiseオブジェクト

Promiseオブジェクトとは

fetch()メソッドの戻り値はPromiseオブジェクトですが、このオブジェクトは非同期処理の結果を成功(resolve)か失敗(reject)で返します。

構文はこちらです。変数exampleの値が「true」なので、コンソールログにはsuccessと終了が表示されます。

new Promise(function(resolve,reject) {
	const example = true;
	if(example) {
		resolve('success');
	} else {
		reject('failure');
	}
}).then(function(data) {
	console.log(data); //success
}).catch(function(data) {
	console.log(data); //failure
}).finally(function() {
	console.log('終了');
});

結果が成功ならthen()メソッドで非同期処理を順次実行し、失敗ならcatch()メソッドに処理が移動します。その際、Promiseの戻り値を引数として渡すことができます。
finally()メソッドは、非同期処理の結果に関わらず実行されます。

今回は、jsonの値を引数として次の処理に渡します。
jsonファイルの読み込みが完了してからthen()メソッド(次の処理)が実行されます。

省略して記述する AwaitとAsync

awaitとasyncを使うと、記述を省略できます。
先ほど、then()メソッドで次の処理に引数を渡していましたが、awaitを使うと戻り値を変数に格納できます。

先程のコードをawaitとasyncで書き直しました。
awaitはasyncで宣言された関数の中でしか使えません。

(async function() {
	const response = await fetch('slider.json');
	const json = await response.json();
	for(const slide of json) {
	 console.log(`img: ${slide.img}、alt: ${slide.alt}`)
	}
})();

先程のコードより、スッキリと記述できました。

例外処理とエラー

外部からデータを取得する際、100%成功する訳ではないです。

例えば、ファイル自体が存在しなければエラー(404)になりますし、jsonの記述が間違っていてもエラーになります。

エラーが発生した場合、プログラムが止まってしまうかもしれません。
その為、エラーも含め予期しない例外が発生した時の処理を記述しておきます。

構文は次の通りです。

try {
	{例外が想定される処理}
	//エラーを故意に発生させる
	throw new Error();
} catch(e) {
	{例外が発生した時の処理}
	console.error(e);
} finally {
	{例外発生に関わらず実行される処理}
	console.log('finalluy')
}

try{}に例外が想定される処理を記述します。もし例外が発生したらcatch()に処理が移動します。
throwは故意にエラーを発生させます。この場合でもcatchに処理が移動します。
finallyは最終的に実行される処理を記述します。

Promiseオブジェクトにも似たような機能がありますが、今回のエラー処理はこの構文を使用します。

jsonファイルが読み込まれなかった時(エラー)

上記の内容を踏まえて、次のような処理を記述します。

  • jsonファイルの読み込みが完了したら
  • jsonファイルの中身をとりだし、HTML要素を生成
  • FlexSliderプラグインを実行

もし、jsonがうまく読み込めなかったらエラーを発生させ、catch()に処理を移動させます。
catch()ではエラーの内容をコンソールログに表示します。

jsonファイルを読み込む記述は関数fetchSliders()にまとめました。
例外が発生したらthrowでエラーを発生させて、catchに処理を移動しています。

//jsonデータ取得
async function fetchSliders() {
    const response = await fetch('slider.json');

    if(response.ok == true) {
        const slideData = await response.json();
        if(!slideData.length) {
            throw new Error('No slide data found');
        }
        return slideData;
    } else {
        throw new Error('The data could not be read');
    }
}

// 非同期で実行
async function init() {
    try {
        //slideデータ取得(json)
        const slides = await fetchSliders();
        //DOM作成
        for(const slide of slides) {
            const slideEml = document.createElement('li');
            const img = document.createElement('img');
            img.src = './images/' + slide.img;
            img.alt = slide.alt;
        
            slideEml.appendChild(img);
            slideWrap.appendChild(slideEml);
        }
     //FlexSliderプラグインを実行
        const sliderElm = createSlider();
    } catch(e) {
        console.error(e);
    }		 
}
init();

最終的には、FlexSliderプラグインを実行しています。

まとめ

今回初めて非同期処理を使いました。外部ファイルの読み込み順を制御できるので、今後も使いそうな機能です。