Code-Along: Dog Fetcher

Follow these instructions to build a website that will provide the user with some pictures of dogs.

Click here to view the Dog Fetcher Starter project. Remix the project to begin the activity.

Part One: Background

Typically, dogs play fetch. In this activity, your code is going to be playing fetch, and the things that your code will be fetching will be dogs!

The Starter Code

The starter project has quite a bit of code, but it's all HTML and CSS! In the index.html file, there are a few elements of note:

  • An <input> with an id of "num-dogs"
  • A <button> with an onclick of "getDogs()"
  • An <img> with an id of "loading"
    • This is currently hidden using CSS
  • A <div> with an id of "dog-imgs"

All of these elements will be necessary to make the website functional.

The shibe.online API

The code needs something to fetch, and luckily, there is an API that provides random pictures of shiba inu dogs! These adorable pups are also known as "shibes" in internet parlance - hence the name, shibe.online. Take a look at the homepage to learn more about how the API works.

The base url is https://shibe.online/api/shibes. For the purposes of this activity, the only relevant query parameter will be count. The response will be a JSON array of strings; each one a URL pointing to a picture of a shibe.

Putting it all together, here's an example URL: https://shibe.online/api/shibes?count=3

The response should look something like this:

[
  "https://cdn.shibe.online/shibes/039e86853cb65ed16cb3823a4fd9528ae374cfec.jpg",
  "https://cdn.shibe.online/shibes/6179b94f4e16762f29e69b48a45f16372b11ed72.jpg",
  "https://cdn.shibe.online/shibes/e0419f6e1f00e800bada84233b0bad86723077ed.jpg"
]

Try opening one of the URLs to see the picture! Here is one of them:

The Plan

To utilize this API, the code should:

  • Make a request to the base URL using fetch
    • Use the <input> to determine the count to pass in as a query parameter
  • Extract the image URLs from the response
  • Create new <img> elements, one for each URL returned

Part Two: Button Begins 🦇

Currently, there is no JavaScript code in the project. That's bad! Start things off by hooking up the <button onclick="getDogs()"> with a new function definition.

  1. Open the script.js file for editing
  2. Define a new function named getDogs
  3. In the body of the function, create a new variable named loadingImg
  4. Set loadingImg to grab the <img id="loading"> picture
    • Use document.querySelector for this
  5. On the next line, set the style.display of the image to "inline-flex"
    • This will make the loading indicator appear

At this point, run the project, click the "Get Dogs" button, and verify that the loading indicator image appears! The code should look something like this:

function getDogs() {
  let loadingImg = document.querySelector("#loading");
  loadingImg.style.display = "inline-flex";
}

Part Three: Fetching Two Dogs 🐕🐕

Now the button is ready, so it's time to try to get some dogs! To start, the code can ignore the <input> and just try to get two dogs.

Trying to Fetch with fetch

Use the fetch function to send a request out to the API to try to get some shibe pics.

  1. Make a new line in the body of the getDogs function
  2. Create a new variable named response
  3. Set response to equal a call to fetch
    • Pass in `https://shibe.online/api/shibes?count=2` as the URL
  4. Under that, create a variable named responseJson
  5. Set responseJson to a call to the json() function on the response variable
  6. Finally, call alert on responseJson to display the result

With that, run the code, click the "Get Dogs" button again, and see what happens. It might not quite work... it should display a pop-up, but there is an issue with this code:

let response = fetch(`https://shibe.online/api/shibes?count=2`);
let responseJson = response.json();
alert(responseJson);

Catching an Error with try/catch

So something is wrong, and it may be possible to debug it, but it is also possible for the code itself to catch the error! Use the try/catch structure to try to figure out what is happening.

  1. Above the current variables, declare response and responseJson without giving them a value
    • This will allow the code to use them in and out of the try block
  2. Under that, create a try/catch structure
    • Start with the try keyword
    • Then, add curly brackets { and }
    • After that, add the catch
    • For the catch, add parentheses ( and )
    • Put e within the parentheses to represent the error
    • Finally, add another set of curly brackets { and }
  3. Move the original variable sets into the try block
    • Make sure to get rid of the let because these already exist
  4. In the catch block, call alert on the error
  5. Also in the catch block, return from the function

The modified code should look something like this:

let response, responseJson;

try {
  response = fetch(`https://shibe.online/api/shibes?count=2`);
  responseJson = response.json();
} catch (e) {
  alert(e);
  return;
}

Now, try running the project and clicking the button. It still will not work, but at least the error should appear in a pop-up! It should say the following:

TypeError: response.json is not a function

Fixing the Error with Asynchronicity

Hmm... why would response.json not be a function? The fetch call should return a Response object... or should it? What does the fetch function return again?

The fetch function returns a Promise object - so it must be awaited to yield the actual result!

  1. Start by adding the async keyword to the getDogs function definition
  2. Next, add the await keyword in front of the fetch call
  3. Also, add the await keyword in front of the json() call
  4. Additionally, add a finally block after the catch block
  5. There, make the loading indicator disappear
    • Use .style.display = "none" to accomplish this
  6. Additionally, for testing, purposes, call alert on responseJson

Run the program, click the "Get Dogs" button again, and verify that some dog image URLs appear! Copy and paste one into a new tab to see a shibe like this.

At this point, the entire code in the script.js file should look something like this:

async function getDogs() {
  let loadingImg = document.querySelector("#loading");
  loadingImg.style.display = "inline-flex";

  let response, responseJson;

  try {
    response = await fetch(`https://shibe.online/api/shibes?count=2`);
    responseJson = await response.json();
  } catch (e) {
    alert(e);
    return;
  } finally {
    loadingImg.style.display = "none";
  }

  alert(responseJson);
}

Part Four: Displaying the Images

The fetch request is actually working! The JavaScript code is talking to an API and retrieving something from the internet. The next step is to display the results in a nicer way.

Prepping the Container

There is already a <div id="dog-imgs"> container in the index.html file - this will hold the new pictures of dogs. The first step will be to grab it, and clear out anything that's in it.

  1. Remove the alert from the body of the getDogs function
  2. In its place, create a new variable named dogImgsDiv
  3. Use document.querySelector to grab the <div id="dog-imgs"> and store it in the variable
  4. Under that, set the innerHTML of the dogImgsDiv to be ""

The code for this part should look something like this:

let dogImgsDiv = document.querySelector("#dog-imgs");
dogImgsDiv.innerHTML = "";

Looping the Response

The goal is to create one picture element for each picture URL in the response. The responseJson object should be an array that stores something like this:

["https://cdn.shibe.online/shibes/9e5687af188a54e801762da4ee920f870e3db633.jpg","https://cdn.shibe.online/shibes/3e918c16e267262d9affef660758b76368f49125.jpg"]

The forEach array function will be perfect for this! First, establish the loop.

  1. Make a new line at the bottom of the getDogs function
  2. There, call the forEach() function on responseJson
  3. Between the parentheses, create a new arrow function
    • It should have one parameter: dogUrl
    • It should have an arrow: =>
    • It should have curly brackets: { and }

The loop code should look something like this:

responseJson.forEach(dogUrl => {});

Adding Each Image to the Container

Now the code is setup to run a block for every image URL retrieved from shibe.online. For each of them, a new <img> element should be created and added to the dogImgsDiv container!

  1. Within the forEach arrow function body, make a new line
  2. There, create a new variable named newDogImg
  3. Set newDogImg to be a new "img" element using document.createElement
  4. Under that, set the src of newDogImg to the dogUrl value
  5. Under that, append the newDogImg element to the dogImgsDiv element
    • Use appendChild to accomplish this

Run the project, click the "Get Dogs" button, and verify that the dog images actually appear now! Click it a few times to see some different pairs of dogs. The code in the body of the arrow function should look something like this:

let newDogImg = document.createElement("img");
newDogImg.src = dogUrl;
dogImgsDiv.appendChild(newDogImg);

Part Five: Dynamic Number of Dogs

The final step is to actually take into account the text box - this will allow the user to grab a specific number of shibes!

  1. Make a new line above the let response, responseJson line in the body of the getDogs function
  2. There, create a new variable named numDogsInput
  3. Set numDogsInput to be the <input id="num-dogs"> element
    • Use document.querySelector
  4. Under that, create a new variable named numDogs
  5. Set numDogs to be the value of the numDogsInput element
  6. Find the fetch call
  7. In the fetch URL, replace the 2 with the interpolated numDogs value
    • Use ${numDogs}

Run the project, enter a number of dogs, click the "Get Dogs" button, and verify that the proper number of dogs appears!

Conclusion

Wow. There sure are some dogs in that API! In this activity, the code used fetch to grab data from an API (using an HTTP GET request) and display images from it.

By the end of the code-along, the script.js file should look something like this:

async function getDogs() {
  let loadingImg = document.querySelector("#loading");
  loadingImg.style.display = "inline-flex";

  let numDogsInput = document.querySelector("#num-dogs");
  let numDogs = numDogsInput.value;

  let response, responseJson;

  try {
    response = await fetch(`https://shibe.online/api/shibes?count=${numDogs}`);
    responseJson = await response.json();
  } catch (e) {
    alert(e);
    return;
  } finally {
    loadingImg.style.display = "none";
  }

  let dogImgsDiv = document.querySelector("#dog-imgs");
  dogImgsDiv.innerHTML = "";

  responseJson.forEach(dogUrl => {
    let newDogImg = document.createElement("img");
    newDogImg.src = dogUrl;
    dogImgsDiv.appendChild(newDogImg);
  });
}

Next Steps

Click here to see how you can make more updates to the Dog Fetcher site!

results matching ""

    No results matching ""