【もりけん塾】JavaScript課題17 -Vanilla JSでスライドショーを実装する①-

JavaScript

もりけん塾のJavaScript課題17で、Vanilla JSを使用したスライドショー作りにチャレンジしました。

こんにちは。Webコーダーのはるです。

現在、所属している「もりけん塾」でハンズオン課題 に取り組み、レビューをいただいています。

今日は、課題17 についてアウトプットをしていきます。
(前回までのアウトプットは、こちらです。)

実装する過程で学んだことを、学習ノートとして記録していきます。

認識の間違えている箇所がありましたら、お問い合わせからご指摘いただけるとうれしいです。

【もりけん塾】JavaScript課題17 -Vanilla JSでスライドショーを実装する①-

今回、取り組んだ課題はこちらです。

(もりけん先生のJavaScriptハンズオン課題より)

作成したJSONデータ

こちらにまとめました。

3秒後に解決されるPromiseが返すオブジェクト

async function fetchData(api) {
 const response = await fetch(api);
 const json = await response.json();

 if(!response.ok){
  console.error(`${response.status}:${response.statusText}`);
 }
 return json.data;
}

async function fetchImgData() {
 return new Promise((resolve) => {
  setTimeout(() => resolve(fetchData("https://mocki.io/v1/2db56900-1359-4d61-bd4c-d477c01b0122")), 3000);
 });
}

async function getImgData() {
 addLoading();
 try {
  return await fetchImgData();
 } catch(e) {
  createErrorMessage(e);
 } finally {
  removeLoading();
 }
}

今まで学習したことに加えて、fetchImgData()関数を新たに作って3秒後にPromiseが解決されるようにしました。

また、fetchData()関数で !response.ok の時に、今までthrow new Errorを投げていたのですが、console.errorに修正しました。

これは、throw new Errorは例外のエラーを投げるものであり、今回のサーバーエラーのような致命的なエラーの場合は適さないからです。

imgタグを5つ作成する

function renderListsOfImg(data) {
 const fragment = document.createDocumentFragment();

 for (let i = 0; i < data.length; i++) {
  const li = document.createElement("li");
  const img = document.createElement("img");

  li.classList.add("slideshow__pict-item","js-slideshow-item");
  li.dataset.index = `${i}`;
  img.src = data[i].img;
  img.alt = data[i].alt;

  //JSONデータでdisplay:trueの場合はis-activeを付与
  data[i].display && li.classList.add("is-active");

  fragment.appendChild(li).appendChild(img);
 }
 ul.appendChild(fragment);
}

最初、createImg()という関数名にしていましたが、さえさん(sae_prog)にレビューをいただきrenderListOfImgに修正しました。

  • create**という命名は、何か返り値があることを期待する
  • 返り値がなく作成したものを追加するときはrender**という命名が適している
  • 5つのListを作成していることが伝わる命名が良い

と教えていただきました。ありがとうございます!

クリックイベントの作成

まずクリックイベントを作成しました。
それぞれ、進むボタンと戻るボタンをクリックした時の関数です。
const addEventListenerForNextButton = (length) => {
 nextButton.addEventListener ("click", () => {
  switchImg("nextElementSibling");
  incrementCurrentIndex();
  toggleButtonDisabled(length);
 })
}

const addEventListenerForPreviousButton = (length) => {
 previousButton.addEventListener ("click", () => {
  switchImg("previousElementSibling");
  incrementCurrentIndex();
  toggleButtonDisabled(length);
 })
}

先生(@terrace_tec)にレビューをいただきました。

関数内でやっていることは addEventListerへの設定なので、addEventListenerForPreviousButton などがわかりやすい、と教えていただきました。

また、引数にはJSONデータを全て渡していましたが、toggleButtonDisabled()関数内ではdata.lengthしか使用していなかったため、lengthの値だけ渡ってくるように修正しました。

データを渡していた以下部分です。
async function addSlide() {
 const data = await getImgData();

 if(data){
  renderListsOfImg(data);
  init(data.length); //lengthのみに変更
  addEventListenerForNextButton(data.length); //lengthのみに変更
  addEventListenerForPreviousButton(data.length); //lengthのみに変更 
 }
}

スライド画像を動かす関数

function switchImg(direct) {
 const activeImg = document.querySelector(".is-active");
 activeImg.classList.remove("is-active");
 activeImg[direct].classList.add("is-active");
}

is-activeクラスのついた<li> (画像リスト)をquerySelectorで検索。

const activeImg = document.querySelector(".is-active");

該当<li>のis-activeクラスを削除。

activeImg.classList.remove("is-active");

該当<li>の前後の要素にis-activeを付与。こちらは、さえさん(sae_prog)に教えていただきました。

activeImg[direct].classList.add("is-active");

実際に、クリックイベント内で関数を実行するときに引数で前後の要素を指定します。

switchImg("nextElementSibling");
switchImg("previousElementSibling");

el.nextElementSibling として使用する読み取り専用のプロパティで、すぐ次(previousの場合はすぐ前)の要素を返します。

5枚目中何枚目かを表示する

初期設定として以下の関数を作成しました。

“5枚目”の部分は固定なので、init関数内でdata.lengthを表示させるようにしました。

function init(length) {
 const allofCountElement = document.getElementById("js-counter-all");

 allofCountElement.textContent = length;
 incrementCurrentIndex();
 toggleButtonDisabled(length);
}

また、

  • 最初と最後の画像の時に左右のボタンをそれぞれ disabledにする
  • 現在のindexを取得する
  • indexを1進める

という3つの関数を作成しました。

function toggleButtonDisabled(length) {
 const currentIndex = getCurrentIndex();
 const firstIndex = 0;
 const lastIndex = length - 1;

 previousButton.disabled = currentIndex === firstIndex;
 nextButton.disabled = currentIndex === lastIndex;
}

function getCurrentIndex() {
 const activeImg = document.querySelector(".is-active");
 return Number(activeImg.dataset.index);
}

function incrementCurrentIndex() {
 const currentCountElement = document.getElementById("js-counter-current");
 currentCountElement.textContent = getCurrentIndex() + 1;
}

ここでは、先生(@terrace_tec)ともなかさん(ruby443n)にレビューをいただきました!

toggleButtonDisabled()関数でdisabledを付与する部分を、最初以下のように書いていました。

//最初の画像の時は、disabledを付与。それ以外の時は外す。
if (activeIndex == firstIndex) {
 previousButton.setAttribute("disabled",true);
} else {
 previousButton.removeAttribute("disabled",true);
}

//最後の画像の時は、disabledを付与。それ以外の時は外す。
if (activeIndex == lastIndex) {
 nextButton.setAttribute("disabled",true);
} else {
 nextButton.removeAttribute("disabled",true);
}

冗長なコードすぎる・・・笑

まず、disabledプロパティというものが使えると知りました。

さらに、条件演算子を使用することで冗長なif文をすっきり書くことができました。

previousButton.disabled = currentIndex === firstIndex ? true : false;
nextButton.disabled = currentIndex === lastIndex ? true : false;

条件演算子を初めて知りました。

/* 構文 */
条件 ? trueの時に実行する文 : falseの時に実行する文

さらにこの文を短く表現できると教わりました。

previousButton.disabled = currentIndex === firstIndex;
nextButton.disabled = currentIndex === lastIndex;

最初のif文からここまで短くすっきりなるとは驚きです・・・!勉強になりました・・!

今回のコード

※最低限の挙動を実装したため、レスポンシブ対応していません。プレビューはPCでご覧ください🙇‍♀️ 

//
学習に使用している本は、もりけん先生推奨の”JavaScript本格入門”です。

あとがき

今回レビューをしてくださったもりけん先生(@terrace_tec)、さえさん(sae_prog)、もなかさん(ruby443n)ありがとうございました!

レビューをいただき、適した関数名を付けられていない箇所が多くありました。

返り値があるのかないのか、そもそも関数名と内容が一致しているのか、などチェックポイントを多く知ることができました。次回に活かしたいです。

お時間を割いていただきありがとうございました!

今日は以上です。

//

【もりけん塾で勉強しています】

もりけん先生(@terrace_tec)のHPはこちら