Building Pokemon Index in Vanilla JS
Join the DZone community and get the full member experience.
Join For FreeIn this post, we are going to build a Pokemon index using Pokemon API in plain Javascript. First, let’s discuss what this project is about. There will be a search bar where users can come and search for a Pokemon, and after the search, Pokemon with its image and stats, as a result, will be shown. So, the functionality is pretty basic but you using this project as a base can tweak and make your own version of this project and add it to your portfolio.
The full version is deployed at https://poke-mon.now.sh/.
This project is inspired by Chris on Code.
HTML Structure
Before we dive into JavaScript, we first have to code some HTML, just to structure our project. So let’s do that
xxxxxxxxxx
<html lang="en">
<head>
<title>Poke.mon</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
</head>
<body>
<form>
<input type="text" placeholder="Search Pokemon" />
<input type="submit" value="Search" />
</form>
<div class="pokemon-container"></div>
</body>
</html>
It's just a simple HTML file with an empty div with class pokemon-container, and above it is a form with a search bar and a button, Nothing fancy. We will use the empty div to display our Pokemon data from our JavaScript file.
Some Styling
The webpage looks too plain as of now so let’s just style it a bit. Let’s create a style.css
file and link it to our index.html
xxxxxxxxxx
@import url("https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap");
body {
padding: 30px;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background: url("https://i.imgur.com/zwp2EXP.png") center top;
background-color: #41740e;
background-repeat: repeat-x;
background-size: contain;
font-family: "Press Start 2P";
position: relative;
}
form {
background: #ef5350;
position: fixed;
display: flex;
justify-content: center;
padding: 10px;
top: 0;
left: 0;
right: 0;
}
form input {
border: none;
outline: none;
border-radius: 20px;
font-size: 18px;
padding: 12px 20px;
}
form input[type="text"] {
background: rgba(255, 254, 254, 0.7);
min-width: 300px;
max-width: 100%;
}
form input[type="submit"] {
cursor: pointer;
margin-left: 8px;
background: #c62828;
color: #f0d0d0;
}
.pokemon {
text-align: center;
}
h2 {
text-transform: capitalize;
}
.stats {
font-size: 14px;
}
Javascript
So let's code some JavaScript now. We will start with an empty index.js
file and discuss what are the steps needed.
- Grab the things we need from HTML in index.js.
- Take input from the search bar and use that to make API requests.
- Make requests to the Pokemon API and fetch the data.
- Display the data on the webpage.
Okay, these will be the steps, sounds simple, doesn’t it.
The first step is to get all the div’s, form, etc, from HTML file which is done as follows.
xxxxxxxxxx
// grab the things we need ----------
const pokemonContainer = document.querySelector(".pokemon-container");
const formEl = document.querySelector("form");
const inputEl = document.querySelector("input[type=text]");
We can use getElementById()
or getElementByClass()
here it makes no difference.
Now, we will add event listeners to listen to the submit command
xxxxxxxxxx
// listen for user events -------------
formEl.addEventListener("submit", (e) => {
e.preventDefault();
pokemonContainer.innerHTML = "";
getPokemon(inputEl.value);
});
Here, we use e.preventDefault()
to stop the page from refreshing. Then, we clear the div with class pokemonContainer
although it is empty for now. In the future, we may search multiple times, and each time we would have to clear the div which is still holding the previous information. Next is getPokemon()
method in which the search input is passed as an argument. This function will do everything from making requests to the API and to displaying it to the page. So let’s discuss this function.
The third step is to make requests to the Pokemon API, which is: https://pokeapi.co/api/v2/pokemon/+ id
We will be making an async function here as follows
xxxxxxxxxx
async function getPokemon(name = "bulbasaur") {
const res= await fetch(`https://pokeapi.co/api/v2/pokemon/${name}`);
const pokemon = await res.json();
}
This is a simple async function, named getPokemon()
in which API requests are made using fetch and stored in res variable which is further converted to JSON using json().
In addition to users being able to search for the Pokemon, by adding name = bulbasaur
in parameters, we can show a default Pokemon, even before a search is initiated.
Another thing that I want to discuss here is that only lowercase Pokemon names are considered valid, so keep that in mind when making requests. Optionally, you can add a method or function to convert user input into lower case.
Now that we have stored the Pokemon information into res, let’s take a look at a traditional response of the API.
Since the response JSON is too big to show here, you can go to https://pokeapi.co/ and view the raw file.
We have the information on the Pokemon so let’s show it, we will start by creating a different div for showing the information, as follows:
xxxxxxxxxx
const pokemonEl = document.createElement("div");
pokemonEl.classList.add("pokemon");
We created a div element using document.createElement()
and then gave it a class of pokemon
using classList.add()
.
We will first show the image of the Pokemon. For that we will use .innerHTML
and appendChild()
to put the information or data on the web page.
And remember we are doing all this inside getPokemon()
function.
xxxxxxxxxx
pokemonEl.innerHTML = `<div class="info">
<img src="https://pokeres.bastionbot.org/images/pokemon/${pokemon.id}.png" width="200">
<h2>${pokemon.name}</h2>
</div>
`
pokemonContainer.appendChild(pokemonEl);
We used the innerHTML on pokemonEl
and then used Template Literals to add HTML. We added a div with class info, and basically, the next steps are just figuring out where in the JSON are the information that you need and want to show on your application, the response files are large so you have to be selective as to how much information and what information do you want to show.
As for the image of the pokemon, you can access all of them just by changing the id
in pokemon.id}.png
. Then we display the name of the Pokemon in <h2>
. The last step in this is to append the div on to the webpage to actually show the information which is done using pokemonContainer.appendChild(pokemonEl)
By now, we can show the name and image of the Pokemon, but there are tons of information we can display, so let’s go on with.
We will first display stats of the Pokemon, by accessing the stats from the response using pokemon.stats
but first look at the raw JSON of stats. Below are the stats of Bulbasaur:
xxxxxxxxxx
"stats": [
{
"base_stat": 45,
"effort": 0,
"stat": {
"name": "speed",
"url": "https://pokeapi.co/api/v2/stat/6/"
}
},
{
"base_stat": 65,
"effort": 0,
"stat": {
"name": "special-defense",
"url": "https://pokeapi.co/api/v2/stat/5/"
}
},
{
"base_stat": 65,
"effort": 1,
"stat": {
"name": "special-attack",
"url": "https://pokeapi.co/api/v2/stat/4/"
}
},
{
"base_stat": 49,
"effort": 0,
"stat": {
"name": "defense",
"url": "https://pokeapi.co/api/v2/stat/3/"
}
},
{
"base_stat": 49,
"effort": 0,
"stat": {
"name": "attack",
"url": "https://pokeapi.co/api/v2/stat/2/"
}
},
{
"base_stat": 45,
"effort": 0,
"stat": {
"name": "hp",
"url": "https://pokeapi.co/api/v2/stat/1/"
}
}
],
xxxxxxxxxx
<div class="stats">${pokemon.stats.map((stat) => {
return `<p>${stat.stat.name}: ${stat.base_stat}</p>`;
}).join("")}</div>
We made another div inside innerHTML
and gave it a class stats
.
As you can see above, in the raw JSON, the stats actually contains an array of objects, so to access that and to just display the name and base_stat. For that, we use map()
method on the stat array, what map()
does that it takes every element of the array one by one in this case objects and creates a new array with the results of calling a function for every array element.
Or in simple words, the map()
method calls the provided function once for each element in an array, in order. That function, in this case, is a callback that takes stat
as an argument and returns a paragraph <p>
that holds the name and base_stat using Template Literals. The whole array is later converted into a string using join()
method.
Now, let’s take a look at the output of the above code,
xxxxxxxxxx
speed: 45
special-defense: 65
special-attack: 65
defense: 49
attack: 49
hp: 45
Before proceeding further, let’s show different information using the same logic as above, let’s show the abilities of the Pokemon, this time there is no numeric value to be shown, other than that the code remains almost same.
xxxxxxxxxx
<div class="abilities">${pokemon.abilities.map((ability) => {
return `<p>${ability.ability.name}</p>`;
})
.join("")}
<div>
The output of the above will be as follows,
xxxxxxxxxx
chlorophyll
overgrow
xxxxxxxxxx
<div class="moves">
${pokemon.moves.map((move) => {
return `${move.move.name} `;
})
.join("")}
<div>
The output of the above is shown below
xxxxxxxxxx
razor-wind swords-dance cut bind vine-whip headbutt tackle body-slam take-down double-edge growl strength mega-drain
leech-seed growth razor-leaf solar-beam poison-powder sleep-powder petal-dance string-shot toxic rage mimic double-team
defense-curl light-screen reflect bide sludge skull-bash amnesia flash rest substitute snore curse protect sludge-bomb mud-slap
giga-drain endure charm swagger fury-cutter attract sleep-talk return frustration safeguard sweet-scent synthesis hidden-power
sunny-day rock-smash facade nature-power ingrain knock-off secret-power grass-whistle bullet-seed magical-leaf natural-gift
worry-seed seed-bomb energy-ball leaf-storm power-whip captivate grass-knot venoshock round echoed-voice grass-pledge work-up
grassy-terrain confide
xxxxxxxxxx
// grab the things we need ----------
const pokemonContainer = document.querySelector(".pokemon-container");
const formEl = document.querySelector("form");
const inputEl = document.querySelector("input[type=text]");
// listen for user events -------------
formEl.addEventListener("submit", (e) => {
e.preventDefault();
pokemonContainer.innerHTML = "";
getPokemon(inputEl.value);
});
// define our functions/actions ------------
async function getPokemon(name = "bulbasaur") {
const res = await fetch(`https://pokeapi.co/api/v2/pokemon/${name}`);
const pokemon = await res.json();
const pokemonEl = document.createElement("div");
pokemonEl.classList.add("pokemon");
pokemonEl.innerHTML = `
<div class="info">
<img src="https://pokeres.bastionbot.org/images/pokemon/${
pokemon.id
}.png" width="200">
<h2>${pokemon.name}</h2>
</div>
<div class="stats">
${pokemon.stats
.map((stat) => {
return `<p>${stat.stat.name}: ${stat.base_stat}</p>`;
})
.join("")}
</div>
<div class="abilities">
${pokemon.abilities
.map((ability) => {
return `<p>${ability.ability.name}</p>`;
})
.join("")}
<div>
<div class="moves">
${pokemon.moves
.map((move) => {
return `${move.move.name} `;
})
.join("")}
<div>
`;
pokemonContainer.appendChild(pokemonEl);
}
// run things ----------------
getPokemon();
Your Versions
Following are a few ideas that can be done to this existing project to make it more interesting, you can use these to make your own version of the project
- Screenshot Feature
You can add a screenshot feature in this project so that users can take a screenshot of the pokemon information.
2. Displaying additional information
As you have seen in the raw JSON, many of the moves and abilities contain a URL that has additional in-depth information about moves or abilities. You can show that information on the web page.
3. Fight
A really amazing option, user can search and select two pokemon, images displaying at opposite sides of the webpages, and then click on a fight button, then they will come close suing some animations or CSS, and then finally the pokemon with higher stats wins and a WIN message is shown.
Published at DZone with permission of Ashutosh Singh, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments