使用PokeAPI制作的宝可梦图鉴列表顺序混乱问题求助
宝可梦图鉴列表顺序混乱问题排查
我用HTML、CSS和JavaScript制作了一个宝可梦图鉴,通过PokeAPI获取数据生成宝可梦列表,但显示时列表顺序混乱,刷新页面后顺序还会发生变化。以下是我的代码及页面情况:
document.addEventListener('DOMContentLoaded', function () { const pokemonList = document.querySelector('.pokemon-list'); const pokemonDetails = document.querySelector('.pokemon-details'); function fetchPokemonDetails(id) { fetch(`https://pokeapi.co/api/v2/pokemon/${id}`) .then(response => response.json()) .then(data => displayPokemonDetails(data)); } function displayPokemonDetails(pokemon) { pokemonDetails.innerHTML = ` <h2>${pokemon.name}</h2> <img src="${pokemon.sprites.front_default}" alt="${pokemon.name}"> <p>Height: ${pokemon.height} dm</p> <p>Weight: ${pokemon.weight} hg</p> `; pokemonDetails.style.display = 'block'; } function displayPokemon(pokemon) { const pokemonCard = document.createElement('div'); pokemonCard.classList.add('pokemon'); pokemonCard.dataset.id = pokemon.id; pokemonCard.innerHTML = ` <img src="https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/${pokemon.id}.png" alt="${pokemon.name}"> <p>${pokemon.name}</p> `; pokemonCard.addEventListener('click', function () { const id = this.dataset.id; fetchPokemonDetails(id); }); pokemonList.appendChild(pokemonCard); } for (let i = 1; i <= 1016; i++) { fetch(`https://pokeapi.co/api/v2/pokemon/${i}`) .then(response => response.json()) .then(data => displayPokemon(data)); } pokemonList.sort((a, b) => a.id - b.id); });
- 列表初始显示状态:

- 刷新后显示状态:

问题原因
- 异步请求的无序性:循环中发起的1016个
fetch请求是异步的,API响应返回时间不固定,哪个请求先拿到数据就先渲染对应的宝可梦卡片,导致列表顺序混乱,且每次刷新的响应顺序不同,列表也随之变化。 - 无效的排序操作:最后一行的
pokemonList.sort(...)完全无效——pokemonList是DOM容器元素,本身没有sort方法;而且这行代码在请求发起后立刻执行,此时所有请求都未返回,DOM中还没有宝可梦卡片,根本无法排序。
解决方案
方案1:批量获取数据后排序渲染(推荐)
先收集所有请求的Promise,用Promise.all等待所有数据返回,按ID排序后再统一渲染,保证顺序正确:
document.addEventListener('DOMContentLoaded', function () { const pokemonList = document.querySelector('.pokemon-list'); const pokemonDetails = document.querySelector('.pokemon-details'); function fetchPokemonDetails(id) { fetch(`https://pokeapi.co/api/v2/pokemon/${id}`) .then(response => response.json()) .then(data => displayPokemonDetails(data)); } function displayPokemonDetails(pokemon) { pokemonDetails.innerHTML = ` <h2>${pokemon.name}</h2> <img src="${pokemon.sprites.front_default}" alt="${pokemon.name}"> <p>Height: ${pokemon.height} dm</p> <p>Weight: ${pokemon.weight} hg</p> `; pokemonDetails.style.display = 'block'; } function displayPokemon(pokemon) { const pokemonCard = document.createElement('div'); pokemonCard.classList.add('pokemon'); pokemonCard.dataset.id = pokemon.id; pokemonCard.innerHTML = ` <img src="https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/${pokemon.id}.png" alt="${pokemon.name}"> <p>${pokemon.name}</p> `; pokemonCard.addEventListener('click', function () { const id = this.dataset.id; fetchPokemonDetails(id); }); pokemonList.appendChild(pokemonCard); } // 收集所有请求的Promise const pokemonPromises = []; for (let i = 1; i <= 1016; i++) { pokemonPromises.push(fetch(`https://pokeapi.co/api/v2/pokemon/${i}`).then(res => res.json())); } // 等待所有数据返回后排序渲染 Promise.all(pokemonPromises) .then(pokemons => { pokemons.sort((a, b) => a.id - b.id); pokemons.forEach(pokemon => displayPokemon(pokemon)); }) .catch(err => console.error('数据获取失败:', err)); });
方案2:按顺序发起请求
用async/await逐个发起请求,保证请求和渲染的顺序与ID一致(适合小批量数据,大批量可能耗时较长):
document.addEventListener('DOMContentLoaded', async function () { const pokemonList = document.querySelector('.pokemon-list'); const pokemonDetails = document.querySelector('.pokemon-details'); function fetchPokemonDetails(id) { fetch(`https://pokeapi.co/api/v2/pokemon/${id}`) .then(response => response.json()) .then(data => displayPokemonDetails(data)); } function displayPokemonDetails(pokemon) { pokemonDetails.innerHTML = ` <h2>${pokemon.name}</h2> <img src="${pokemon.sprites.front_default}" alt="${pokemon.name}"> <p>Height: ${pokemon.height} dm</p> <p>Weight: ${pokemon.weight} hg</p> `; pokemonDetails.style.display = 'block'; } function displayPokemon(pokemon) { const pokemonCard = document.createElement('div'); pokemonCard.classList.add('pokemon'); pokemonCard.dataset.id = pokemon.id; pokemonCard.innerHTML = ` <img src="https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/${pokemon.id}.png" alt="${pokemon.name}"> <p>${pokemon.name}</p> `; pokemonCard.addEventListener('click', function () { const id = this.dataset.id; fetchPokemonDetails(id); }); pokemonList.appendChild(pokemonCard); } // 按顺序逐个请求并渲染 try { for (let i = 1; i <= 1016; i++) { const response = await fetch(`https://pokeapi.co/api/v2/pokemon/${i}`); const data = await response.json(); displayPokemon(data); } } catch (err) { console.error('数据获取失败:', err); } });
补充建议
- 方案1一次性发起大量请求可能触发API限流,建议分批次请求(比如每次请求20个,完成后再请求下一批)。
- 可以使用PokeAPI的列表接口
https://pokeapi.co/api/v2/pokemon?limit=1016先获取所有宝可梦的基础信息(含ID和详情URL),再批量请求详情,更规范且能避免手动循环ID的潜在问题。
内容的提问来源于stack exchange,提问作者KAIJUU
相关产品推荐
相关产品推荐

