Exercise: Dog App
We're going to build a dog picture viewer using the dog.ceo API. This project is going to allow users to select a breed from a drop down list and then display a random picture of that breed.
Requirements:
- Here are the API docs.
- The list of breeds can be obtained by
List all breeds
API (see the docs linked above.) Request the list of breeds and then use those to create all theoption
s for theselect
. - When the page first loads, show a loading spinner until the image has loaded. I used an emoji with CSS animations to do so. Feel free to look at what other people have done, write your own (short tutorial will be below), or use an animated GIF. When the image loads, hide the spinner and show the image.
- You already know how to change the
src
of animg
, but if you immediately hide the loading spinner and show theimg
, you'll have a flash of time where nothing on the page since the image hasn't loaded yet. You can side step this by usingaddEventListener
on theimg
tag and listen for the"load"
event. Once the"load"
event happens, it means the image is loaded and ready to be shown. - You can do AJAX request using fetch and promises like I showed you in the previous chapter. I'm now going to show you a brand new feature called async / await and it makes this a lot easier.
Let's chat about CSS animations a second here:
<style>
@keyframes spin {
to {
transform: rotateZ(360deg);
}
}
@keyframes psychadelic {
100% {
filter: hue-rotate(360deg);
}
}
.mole {
height: 334px;
width: 334px;
background-color: brown;
border-radius: 50%;
}
.color {
animation: psychadelic 3s linear infinite;
}
.spin {
animation: spin 3s ease-in-out infinite;
}
</style>
<img src="https://frontendmasters.github.io/bootcamp/mole-game/mole-hungry.png" style="background-color: brown; border-radius: 50%" class="mole color" />
<img src="https://frontendmasters.github.io/bootcamp/mole-game/king-mole-hungry.png" style="background-color: brown; border-radius: 50%" class="mole spin" />
- These
@keyframes
animations are re-usable animations that we define once and can use as many times as we want. - The second word (in our case
spin
andpsychadelic
) are what we called the animations, similar to a variable name. It's what we can refer to it as later. - Inside the
@keyframes
we have ato
property. You can define very fine grain steps in the animation but here we want it to infer the steps. We could addfrom { transform: rotateZ(0deg); }
but the browser already knows that it's at0deg
rotation. In this case, it just infers that you want it to start from where it is and animate to theto
point. - You can also use percentages. Instead of
to
you'd use100%
and instead offrom
you'd use0%
. You can put as many steps as you want in there too e.g.1%
,10%
,50%
,92%
, etc. - Where you want to use animation, put something like
animation: spin 1s infinite linear;
. This will use thespin
animation we defined above, will take 1 second to do one iteration of the animation, will do it with linear easing, and will continue to do it infitiely. The order is not important here. - Easing allows you to make it seem elastic when it starts and stops, like a ball bouncing. It's faster at the bottom of the bounce and slower at the top.
Let's talk about async / await for a second. So far we've been using promises and .then
. There's an easier way to do it and it's called async / await.
<button id="get-doggo">Get Doggo</button>
<img style="height: 250px;" id="doggo" src="http://placecorgi.com/250">
<script>
async function getDoggo(event) {
const res = await fetch("https://dog.ceo/api/breeds/image/random");
const resJson = await res.json();
document.getElementById("doggo").src = resJson.message;
}
document.getElementById('get-doggo')
.addEventListener('click', getDoggo)
</script>
Notice the function getDoggo has the async
keyword there. This means inside of this function we can use await
. await
takes a promise and then pauses the function until that promise resolves and gives you the answer back. It makes the code a lot simpler to understand. If you need to deal with errors, you can just try / catch. Feel free to write code this way for this exercise or continue using promises. Do note that I can't say await
in the top level; it has to be inside of an async function.
Your page does not have to look like mine. Feel free to design it however you want. Feel free to copy me too!
Good luck!
If you want to see my answers, here is the JavaScript, here is the CSS, and here is the HTML.