<template>
  <option>
    <img>
    <span></span>
  </option>
</template>

<select>
  <button>
    <selectedcontent></selectedcontent>
  </button>
  <div id="container" class="container"></div>
</select>

<dl>
  <dt>name</dt>
  <dd id="name"></dd>
</dl>
<dl>
  <dt>genus</dt>
  <dd id="genus"></dd>
</dl>
<dl>
  <dt>type</dt>
  <dd id="type"></dd>
</dl>
<dl>
  <dt>height</dt>
  <dd id="height"></dd>
</dl>
<dl>
  <dt>weight</dt>
  <dd id="weight"></dd>
</dl>
<dl>
  <dt>cry</dt>
  <dd>
    <audio id="cry" controls></audio>
  </dd>
</dl>
:root {
  --radius: 0.5em;
  --space: 1em;
}

select, ::picker(select) {
  appearance: base-select;
  border-radius: var(--radius);
}

select::picker-icon {
  display: none;
}

select:open {
  background-color: lightgray;
}

selectedcontent {
  display: inline-flex;
  flex-direction: column;

  img {
    width: 4em;
  }
}

option::checkmark {
  content: attr(data-type);
}

option:checked {
  background-color: ivory;
}

.container {
  display: grid;
  grid-template: 1fr 1fr / 1fr 1fr 1fr;
  padding: var(--space);
  gap: var(--space);
  
  option {
    display: inline-flex;
    flex-direction: column;
    border-radius: var(--radius);
    
    img {
      width: 4em;
    }
  }
}
const POKEMON_API = 'https://pokeapi.co/api/v2/pokemon';
const POKEMON_SPICIES_API = 'https://pokeapi.co/api/v2/pokemon-species';
const IMAGE_PATH = 'https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork';

const TYPES = {
  normal: '🐾',
  fire: '🔥',
  water: '💧',
  electric: '⚡',
  grass: '🌿',
  ice: '❄️',
  fighting: '🥊',
  poison: '☠️',
  ground: '🌋',
  flying: '🦅',
  psychic: '🔮',
  bug: '🐛',
  rock: '🪨',
  ghost: '👻',
  dragon: '🐉',
  dark: '🌑',
  steel: '🔩',
  fairy: '🧚'
};

async function getPokemons(limit) {
  const response = await fetch(`${POKEMON_API}?limit=${limit}`);
  const json = await response.json();
  
  return json;
}

async function getPokemon(name) {
  const response = await fetch(`${POKEMON_API}/${name}`);
  const json = await response.json();
  
  return json;
}

async function getPokemonSpecies(name) { 
  const response = await fetch(`${POKEMON_SPICIES_API}/${name}`);
  const json = await response.json();
  
  return json;
}

window.addEventListener('load', async () => {
  const cacheObject = JSON.parse(localStorage.getItem('cache')) ?? {};
  const {results: pokemons} = await getPokemons(151);
  
  const template = document.querySelector('template');
  const select = document.querySelector('select');
  const container = select.querySelector('#container');
  const name = document.querySelector('#name');
  const genus = document.querySelector('#genus');
  const type = document.querySelector('#type');
  const height = document.querySelector('#height');
  const weight = document.querySelector('#weight');
  const cry = document.querySelector('#cry');
  
  pokemons.forEach((pokemon, index) => {
    const option = document.importNode(template.content, true);
    const img = option.querySelector('img');
    img.src = `${IMAGE_PATH}/${index + 1}.png`;
    img.alt = pokemon.name;
    const span = option.querySelector('span');
    span.textContent = pokemon.name;
    
    container.append(option);
  });

  select.addEventListener('change', async e => {
    const {language} = window.navigator;
    const data = cacheObject[select.value] ?? {
      ...await getPokemon(select.value),
      ...await getPokemonSpecies(select.value)
    };
    
    if (!cacheObject[select.value]) {
      console.debug(`data is not cached: ${select.value}`);
      const {names, genera, types} = data;
 
      cacheObject[select.value] = {
        names,
        genera,
        types
      };
      
      try {
        localStorage.setItem('cache', JSON.stringify(cacheObject));
      } catch (error) {
        console.error(error);
      }
    }

    name.textContent = data.names.find(item => item.language.name === language)?.name ?? pokemon.name;
    genus.textContent = data.genera.find(item => item.language.name === language)?.genus ?? '';
    type.textContent = data.types.map(({type}) => TYPES[type.name]).join(' ');
    cry.src = data.cries.latest;
    height.textContent = `${data.height / 10}m`;
    weight.textContent = `${data.weight / 10}kg`;
  });
});

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.