Build a Pokémon Search App Project - Build a Pokémon Search App - Red test

Tell us what’s happening:

Hello, my code passes every test except for the “Red” test, the console gives me the following:

// running tests
When the #search-input element contains the value Red and the #search-button element is clicked, an alert should appear with the text “Pokémon not found”.
// tests completed
// console output
Uncaught TypeError: undefined is not iterable (cannot read property Symbol(Symbol.iterator))
[Error: TypeError: Cannot read properties of undefined (reading ‘trim’)]

Any help is appreciated.

Your code so far

<!-- file: index.html -->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link rel="stylesheet" href="styles.css" />
    <title>Pokémon Search App</title>
  </head>
  <body>
    <head>
      <div id="headline">
          <h3>insert logo here</h3>
          <h1>Pokémon Search App</h1>
        </div>
    </head>
    <main>
      <div id="container">
        <div id="input-container">
          <p>Search for Pokémon Name or ID:</p>
          <div id="input-btn-container">
            <input id="search-input" required/>
            <button id="search-button">Search</button>
          </div>
        </div>
        <div id="pokemon-container">
          <div id="name-id-container">
            <div id="pokemon-name"></div>
            <div id="pokemon-id"></div>
          </div>
          <div id="weight-height-container">
            <div id="weight"></div>
            <div id="height"></div>
          </div>
          <div id="sprite-container"></div>
          <div id="types"></div>
        </div>
        <div id="stats-container">
          <div id="base-desc" class="left">Base</div>
          <div id="stats-desc" class="right">Stats</div>
          <div id="hp-desc" class="left">HP:</div>
          <div id="hp" class="right">0</div>
          <div id="attack-desc" class="left">Attack:</div>
          <div id="attack" class="right">0</div>
          <div id="defense-desc" class="left">Defense:</div>
          <div id="defense" class="right">0</div>
          <div id="special-attack-desc" class="left">Sp. Attack:</div>
          <div id="special-attack" class="right">0</div>
          <div id="special-defense-desc" class="left">Sp. Defense:</div>
          <div id="special-defense" class="right">0</div>
          <div id="speed-desc" class="left">Speed:</div>
          <div id="speed" class="right">0</div>
        </div>
      </div>
    </main>
    <script src="./script.js"></script>
  </body>
</html>
/* file: styles.css */
:root {
  --purple-color: rgba(150, 0, 150,0.8);
  --purple-color-hover: rgba(190, 0, 190,0.8);
  --normal-color: #aaaa99;
  --fire-color: #ff4422;
  --water-color: #3399ff;
  --electric-color: #ffcc33;
  --grass-color: #77cc55;
  --ice-color: #66ccff;
  --fighting-color: #bb5544;
  --poison-color: #aa5599;
  --ground-color: #ddbb55;
  --flying-color: #8899ff;
  --psychic-color: #ff5599;
  --bug-color: #aabb22;
  --rock-color: #bbaa66;
  --ghost-color: #6666bb;
  --dragon-color: #7766ee;
  --dark-color: #775544;
  --steel-color: #aaaabb;
  --fairy-color: #ee99ee;
}

body {
  background-color: rgb(0,0,60);
  font-family: Tahoma;
}

#container {
  border: 0px red solid;
  margin: 50px auto;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  min-width: 350px;
  max-width: 500px;
}

#headline {
  text-align: center;
  color: white;
}

#container {
  background-color: rgb(220,220,220);
  border-radius: 20px;
}

#input-container {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  margin-bottom: 15px;
}

#input-btn-container {
  display: flex;
  flex-direction: row;
  justify-content: center;
  align-items: center;
}

p {
  font-size: 22px;
  color: black;
  padding-top: 20px;
}

input {
  width: 50%;
  font-size: 30px;
  text-align: center;
  border: 1px solid white;
  margin-right: 15px;
}

input:focus {
  outline: 2px solid white;
}

button {
  cursor: pointer;
  background-color: var(--purple-color);
  padding: 6px 20px;
  font-size: 20px;
  color: white;
}

button:hover {
  background-color: var(--purple-color-hover);
}

#pokemon-container {
  background-color: rgb(210,210,210);
  margin: 10px;
  width: 95%;
  height: 350px;
  display: flex;
  flex-flow: column;
}

#pokemon-name,#pokemon-id {
  color: black;
  padding: 10px;
  display: inline-block;
  font-size: 25px;
}

#weight,#height {
  color: black;
  padding-left: 10px;
  display: inline-block;
  font-size: 15px;
}

#sprite-container {
  flex: 1;
}

img {
  width: 200px;
  height: 200px;
  position: relative;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
}

#types {
  color: black;
  margin-top: 5px;
  width: 100%;
  height: 50px;
}

.type {
  position: relative;
  top: 15%;
  left:2%;
  display: inline-block;
  border-radius: 5px;
  padding-top: 5px;
  padding-bottom: 5px;
  width: 100px;
  font-size: 18px;
  color: white;
  margin-right: 2px;
  margin-left: 2px;
  text-align: center;
}

.normal {
  background-color: var(--normal-color);
}
.fire {
  background-color: var(--fire-color);
}
.water {
  background-color: var(--water-color);
}
.electric {
  background-color: var(--electric-color);
}
.grass {
  background-color: var(--grass-color);
}
.ice {
  background-color: var(--ice-color);
}
.fighting {
  background-color: var(--fighting-color);
}
.poison {
  background-color: var(--poison-color);
}
.ground {
  background-color: var(--ground-color);
}
.flying {
  background-color: var(--flying-color);
}
.psychic {
  background-color: var(--psychic-color);
}
.bug {
  background-color: var(--bug-color);
}
.rock {
  background-color: var(--rock-color);
}
.ghost {
  background-color: var(--ghost-color);
}
.dragon {
  background-color: var(--dragon-color);
}
.dark {
  background-color: var(--dark-color);
}
.steel {
  background-color: var(--steel-color);
}
.fairy {
  background-color: var(--fairy-color);
}

#stats-container {
  margin: auto;
  margin-bottom: 20px;
  width: 90%;
}

.left,.right {
  font-size: 20px;
  color: white;
  background-color: var(--purple-color);
  margin: 5px;
  display:inline-block;
  text-align: center;
  padding-top: 10px;
  padding-bottom: 10px;
}

.left {
  width: 70%;
  
}

.right {
  width: 110;

}

.bold {
  font-weight: 900;
}
/* file: script.js */
const pokemonNameDiv  = document.getElementById("pokemon-name");
const pokemonIdDiv    = document.getElementById("pokemon-id");
const weightDiv       = document.getElementById("weight");
const heightDiv       = document.getElementById("height");
const spriteContainer = document.getElementById("sprite-container");
const typesDiv        = document.getElementById("types");
const hpDiv           = document.getElementById("hp");
const attackDiv       = document.getElementById("attack");
const defenseDiv      = document.getElementById("defense");
const spAttackDiv     = document.getElementById("special-attack");
const spDefenseDiv    = document.getElementById("special-defense");
const speedDiv        = document.getElementById("speed");
const searchInput     = document.getElementById("search-input");
const searchBtn       = document.getElementById("search-button");


const allPokemonLink = "https://pokeapi-proxy.freecodecamp.rocks/api/pokemon";
const testLink = "https://pokeapi-proxy.freecodecamp.rocks/api/pokemon/25/";
let allPokemon;
let allPokemonId;
let allPokemonName;

const fetchData = async () => {
  try {
    const res = await fetch(String(allPokemonLink));
    const json = await res.json();
    allPokemon = json.results;
  } catch (err) {
    console.log(err);
  }
};
fetchData();

const clearPokemonData = () => {
  //searchInput.value           = "";
  pokemonNameDiv.textContent  = "";
  pokemonIdDiv.textContent    = "";
  weightDiv.textContent       = "";
  heightDiv.textContent       = "";
  typesDiv.innerHTML          = "";
  spriteContainer.innerHTML   = "";
  hpDiv.textContent           = "0";
  attackDiv.textContent       = "0";
  defenseDiv.textContent      = "0";
  spAttackDiv.textContent     = "0";
  spDefenseDiv.textContent    = "0";
  speedDiv.textContent        = "0";
};

const getPokemonData = async (pokemon) => {
  // Have to add a bit to the url string for some reason
  const pokemonUrl = pokemon.url.slice(0,4)+'s:'+pokemon.url.slice(5);
  try {
    const res = await fetch(pokemonUrl);
    const pokemonData = await res.json();
    //console.log(pokemonData);
    displayPokemonData(pokemonData);
  } catch (err) {
    console.log(err);
  }
};

const displayPokemonData = (data) => {
  pokemonNameDiv.textContent  = data.name.toUpperCase();
  pokemonIdDiv.textContent    = `#${data.id}`;
  weightDiv.textContent       = `Weight: ${data.weight}`;
  heightDiv.textContent       = `Height: ${data.height}`;
  spriteContainer.innerHTML   = `<img id="sprite" src="${data.sprites.front_default}" />`;

  typesDiv.innerHTML = "";
  data.types.forEach((typeObj)=>{
    const type = typeObj.type.name;
    typesDiv.innerHTML += `
      <div class="type ${type}">${type.toUpperCase()}</div>
    `;
  });
  //console.log(data.types[0])

  hpDiv.textContent           = data.stats[0].base_stat;
  attackDiv.textContent       = data.stats[1].base_stat;
  defenseDiv.textContent      = data.stats[2].base_stat;
  spAttackDiv.textContent     = data.stats[3].base_stat;
  spDefenseDiv.textContent    = data.stats[4].base_stat;
  speedDiv.textContent        = data.stats[5].base_stat;
};

const parseInput = () => {
  const input = searchInput.value.toLowerCase();
  //console.log(input);
  let pokemonIdx;
  for (const pokemon of allPokemon) {
    if (Number(input)===pokemon.id || input===pokemon.name) {
      pokemonIdx = pokemon.id-1;
      //console.log("found the pokemon!")
      //console.log(pokemon)
      break;
    }
  };

  if (pokemonIdx!==undefined) {
    //console.log("fetching pokemon data")
    getPokemonData(allPokemon[pokemonIdx]);
  } else {
    //console.log("did not find a pokemon :(")
    alert("Pokémon not found");
    clearPokemonData();
  }
};

searchBtn.addEventListener("click",parseInput);
searchInput.addEventListener("keydown", (e) => {
  if (e.key === "Enter") {
    parseInput();
  }
});

Your browser information:

User Agent is: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36

Challenge Information:

Build a Pokémon Search App Project - Build a Pokémon Search App

This is coming from parseInput function. Could you explain how the checking for existing pokemons is supposed to work?

Well the idea is that I got through every pokemon in the array “allPokemon” and check whether the input matches either the id of the current pokemen (through a number conversion), or the name of the current pokemon.
If a match is found I set a variable to remember the pokemon and break the loop to stop searching.
Then I have an If condition which calls an async function to get the data for the pokemon if the previous variable was set to something. If the variable was not set to something, i.e. it’s “undefined”, I go to an else clause instead which gives the user an alert that a pokemon was not found for the input.

you can also do a call to the API, it says Pokemon not found if there is no such pokemon

Since allPokemon is filled in async operation, what if the check for specific pokemon happens before fetching all pokemons is completed?

Alright I figured it out, so I was declaring a variable for the list of all pokemon which I fetch data for at the start of the script, but because I didn’t initialize it to anything, the variable is empty when the test runs because it’s so fast that the data hasn’t been fetched yet.
I just replaced:

let allPokemon;

with:

let allPokemon = ; // empty array

and the test passed.

This means though that check for specific pokemon can happen when array is empty, still.

Hello and welcome to the forum!

Fetching all data into an array and then searching through it may not be the best approach for performance and input control.

Instead, you can simply append the user-provided Pokémon ID or name to the API endpoint URL. This way, you can fetch only the data you need.

For example :

`https://pokeapi-proxy.freecodecamp.rocks/api/pokemon/pikachu`

This way, you only retrieve the necessary Pokémon data, improving both performance and input control. If api not response, you can return error message. Also you should parse to input with regex and valid control before data fetch and return necessary error message.

Good luck!

Thanks for the reply, I hadn’t used the catch part of the try/catch function previously so this really didn’t occur to me at all. I was wondering if there would be an easy way to make the parseInput function wait for the fetch to complete before doing anything, but this seems a much simpler solution.