もりけん塾 JavaScript課題12 クリックで、Fetchリクエストをする

もりけん塾 JavaScript課題12 クリックで、Fetchリクエストをする

今回は前回実装したものの続きで、クリックでFecthリクエストをする課題です。クリックイベントはJQueryでしか実装したことがなく、JavaScriptでどうやるんだろう?ちょっと難しそう、というところからスタートです。

前回の学習記録の記事はこちらです。

お題

クリックしたらリクエストをして、それらが表示されるようにしてください

(それらは、とってきたデータを使って作るテキストと画像のリストのことです。)

準備

それでは、JavaScriptで実装していきます。

クリックイベントを発生させるためには、起点となるボタンや要素が必要です。HTMLに記述します。必要に応じで、classやidを追加します。

<button id="js-btn" class="btn">Click</button>

ボタンには、cursor:pointer;をつけておくことにします。

.btn{
  cursor: pointer;
}

HTMLを書いたら、JavaScriptを書いていきます。

const btn = document.getElementById('js-btn');

btn.addEventListener('click',function(){
 //ここに処理を書く
},false);

アロー関数で書くこともできます。

const btn = document.getElementById('js-btn');

btn.addEventListener('click', event => {
  //ここに処理を書く
});

クリックイベントは、HTML側にonclickを書いて動かす方法もあります。onclickは複数のイベントを登録することができずに、上書きされてしまうという特性がありますが、複数の関数を指定することが可能なため、どちらを使うかは別れると思います。

.addEventListener(‘click’ ,function())を使う方法がポピュラーです。

<input type="button" value="button" onclick="buttonClick()">
function buttonClick(){
    //ここに処理を書く
}

最初のコード

クリックイベントの実装方法がわかったところで、コードを書いていきます。最初にPRしたコードはこちらです。

今回は、さえさん、senさんにレビューをして頂きました。ありがとうございます!

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <link rel="stylesheet" href="reset.css" type="text/css" />
    <link rel="stylesheet" href="style.css" type="text/css" />
    <script src="script.js" defer></script>
    <title>TerraceTechフロントエンドエンジニア養成所</title>
  </head>
  <body>
    <div id="js-loading">
    </div>
    <div>
      <ul id="js-list"></ul>
      <button type="button" id="js-button">Click</button>
    </div>
  </body>
</html>
function showLoadingImg(){
    const loadingImg = document.createElement('img');
    loadingImg.src = "loading-circle.gif";
    loadingImg.alt = "ローディング画像";
    document.getElementById('js-loading').appendChild(loadingImg);
}

function removeLoadingImg(){
    document.getElementById('js-loading').remove();
}

async function getData(){
    showLoadingImg();
    try{
        const endpoint = "https://api.json-generator.com/templates/szdgGQcOLXuk/data?access_token=hu4bc7qh9znx2m8f53mn4mz2hryvdntkavwbw8j0";
        const response = await fetch(endpoint);
        //responseがokではないとき
        if(!response.ok){
        throw new Error(`${response.status}:${response.text}`);
        }
        //responseがokのとき
        const json = await response.json();
        const data = await json.data;
        return data;
    }catch(e){
        document.getElementById('js-list').textContent = e;
        console.error(e);
    }finally{
        removeLoadingImg();
    }
}

async function  renderList(){
    const result = await getData();
    //resultがtrueではないとき
    if (!result) {
        return;
    }
    //resultがtrueのとき
    const fragment = document.createDocumentFragment();
    const ul = document.getElementById('js-list');
        for(const list of result){
            const li = document.createElement('li');
            const a = document.createElement('a');
            const img = document.createElement('img');
            a.textContent = list.text;
            a.href = `/${list.to}`;
            img.src = list.img;
            img.alt = list.alt;
            fragment.appendChild(li).appendChild(a).insertAdjacentElement('afterbegin',img);
        }
    ul.appendChild(fragment);
}

const btn = document.getElementById('js-button');
btn.addEventListener('click',function(){
    renderList();
    btn.remove();
},false);

最初に、ローディング画像を表示するのshowLoadingImg()関数で取得している要素は、removeLoadingImg()を実行したときに、取り除かれて存在しなくなるため、もう一度showLoadingImg()を実行したいときにエラーになる、とご指摘を頂きました。

これを解消するために、ローディング画像を入れるためのdivはHTMLに記述せず、JavaScriptで作ることにしました。

また、下記のfetch部分の処理は関数として切り出した方がわかりやすいこと、renderList関数はリストを作るだけの関数にした方がいいとのコメントを頂きました。

async function getData(){
    showLoadingImg();
    try{
        const endpoint = "https://api.json-generator.com/templates/szdgGQcOLXuk/data?access_token=hu4bc7qh9znx2m8f53mn4mz2hryvdntkavwbw8j0";
        const response = await fetch(endpoint);
        //responseがokではないとき
        if(!response.ok){
        throw new Error(`${response.status}:${response.text}`);
        }
        //responseがokのとき
        const json = await response.json();
        const data = await json.data;
        return data;
    }catch(e){
        document.getElementById('js-list').textContent = e;
        console.error(e);
    }finally{
        removeLoadingImg();
    }
}
async function  renderList(){
    const result = await getData();
    //resultがtrueではないとき
    if (!result) {
        return;
    }
    //resultがtrueのとき
    const fragment = document.createDocumentFragment();
    const ul = document.getElementById('js-list');
        for(const list of result){
            const li = document.createElement('li');
            const a = document.createElement('a');
            const img = document.createElement('img');
            a.textContent = list.text;
            a.href = `/${list.to}`;
            img.src = list.img;
            img.alt = list.alt;
            fragment.appendChild(li).appendChild(a).insertAdjacentElement('afterbegin',img);
        }
    ul.appendChild(fragment);
}

最終的なコード

最終的にApproveいただいたコードはこちらです。最初のものよりも、見通しやすいコードになったと思います。

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <link rel="stylesheet" href="reset.css" type="text/css" />
    <link rel="stylesheet" href="style.css" type="text/css" />
    <script src="script.js" defer></script>
    <title>TerraceTechフロントエンドエンジニア養成所</title>
  </head>
  <body>
    <div>
      <ul id="js-list"></ul>
      <button type="button" id="js-button">Click</button>
    </div>
  </body>
</html>
function showLoadingImg(){
    const div = document.createElement('div');
    const loadingImg = document.createElement('img');
    div.id = "js-loading";
    loadingImg.src = "loading-circle.gif";
    loadingImg.alt = "ローディング画像";

    document.body.insertAdjacentElement('afterbegin',div).appendChild(loadingImg);
}

function removeLoadingImg(){
    document.getElementById('js-loading').remove();
}

async function fetchdata(){
    const endpoint = "https://api.json-generator.com/templates/szdgGQcOLXuk/data?access_token=hu4bc7qh9znx2m8f53mn4mz2hryvdntkavwbw8j0";
    const response = await fetch(endpoint);
    //responseがokではないとき
    if(!response.ok){
    throw new Error(`${response.status}:${response.text}`);
    }
    //responseがokのとき
    const json = await response.json();
    const data = await json.data;
    return data;
}

const btn = document.getElementById('js-button');
btn.addEventListener('click',function(){
    btn.remove();
    init();
},false);

function renderList(result) {
    const fragment = document.createDocumentFragment();
    const ul = document.getElementById('js-list');
        for(const list of result){
            const li = document.createElement('li');
            const a = document.createElement('a');
            const img = document.createElement('img');
            a.textContent = list.text;
            a.href = `/${list.a}`;
            img.src = list.img;
            img.alt = list.alt;
            fragment.appendChild(li).appendChild(a).insertAdjacentElement('afterbegin',img);
        }
    ul.appendChild(fragment);
}

async function init(){
    showLoadingImg();
    try{
        const result = await fetchdata();
        //resultがtrueではないとき
        if (!result) {
            return;
        }
        //resultがtrueのとき
        renderList(result);
    }catch(e){
        document.getElementById('js-list').textContent = e;
        console.error(e);
    }finally{
        removeLoadingImg();
    }
}

今回学んだこと

  • .addEventListener(‘click’,function())の使い方
  • onclick属性の使い方

参考にしたサイト

https://developer.mozilla.org/ja/docs/Web/API/Element/click_event#html

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

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

https://kenjimorita.jp/

まい

フロントエンドエンジニア目指して、勉強中です。

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