使用OMDB API的页面需点击两次提交才渲染HTML的问题求助
问题:首次点击搜索按钮无法渲染电影内容,需点击两次才生效
我正在制作一个练习用网站,通过OMDB API获取电影数据,用户搜索关键词后在页面展示对应的HTML内容。目前遇到问题:用户需要点击两次搜索按钮,页面才会渲染出电影HTML。API已正常响应并填充fullMovies数组,但首次点击时movieItemHTML函数生成的内容无法展示,第二次点击才生效。我猜测这与async/await的异步逻辑有关,但耗时3小时仍未排查出问题。
原代码
const form = document.getElementById('form'); const movieItem = document.querySelector('.movies'); const APIKEY = 'a3f1f778'; const exploring = document.querySelector('.exploring'); const searchInput = document.getElementById('query'); const movies = []; const fullMovies = []; form.addEventListener('submit', (e) => { e.preventDefault(); getMovies(); // console.log(fullMovies); }); const getMovies = async () => { const url = `http://www.omdbapi.com/?s=${searchInput.value}&apikey=${APIKEY}`; const res = await fetch(url); const movieData = await res.json(); movieData.Search.forEach((e) => movies.push(e)); searchMovieDetails(); }; const searchMovieDetails = () => { movies.forEach(async (e) => { const res = await fetch( `http://www.omdbapi.com/?t=${e.Title}&apikey=${APIKEY}` ); const data = await res.json(); fullMovies.push(data); }); movieItemHTML(); }; function movieItemHTML() { exploring.style.display = 'none'; let html = ''; fullMovies.forEach((e) => { html = ` <div class="movie-item"> <img class="poster" src=${e.Poster}/> <div class="movie-desc"> <div class="title-rating"> <h3>${e.Title}</h3> <img src="./images/star.png" class="star"/> <p class="rating"> ${e.imdbRating}</p> </div> <div class="time-kind-btn"> <p>${e.Runtime}</p> <p>${e.Genre}</p> <a class="watchlist"><img src="./images/addBtn.png"> Watchlist</a> </div> <div> <p class="plot">${e.Plot}</p> </div> </div> </div> `; movieItem.innerHTML += html; }); }
问题分析
searchMovieDetails中,forEach循环遍历异步函数时,不会等待每个异步请求完成就直接执行后续的movieItemHTML,此时fullMovies数组还没有被填充数据,所以首次点击没有内容渲染。- 全局数组
movies和fullMovies未在每次搜索前清空,第二次点击时,第一次的异步请求刚好完成,数组积累了数据,因此能渲染出内容,但会导致旧数据叠加。
修复方案
1. 改造异步逻辑,等待所有详情请求完成
把forEach替换为Promise.all,确保所有电影详情请求都返回后再调用渲染函数,同时将searchMovieDetails改为异步函数。
2. 每次搜索清空全局数组
在getMovies开头清空movies和fullMovies,避免旧数据干扰新搜索结果。
3. 渲染前清空容器内容
在movieItemHTML开头清空movieItem的innerHTML,防止多次搜索内容叠加。
修改后的完整代码
const form = document.getElementById('form'); const movieItem = document.querySelector('.movies'); const APIKEY = 'a3f1f778'; const exploring = document.querySelector('.exploring'); const searchInput = document.getElementById('query'); let movies = []; let fullMovies = []; form.addEventListener('submit', (e) => { e.preventDefault(); getMovies(); }); const getMovies = async () => { // 每次搜索清空数组 movies = []; fullMovies = []; const url = `http://www.omdbapi.com/?s=${searchInput.value}&apikey=${APIKEY}`; const res = await fetch(url); const movieData = await res.json(); if (!movieData.Search) return; // 处理无搜索结果的情况 movies.push(...movieData.Search); await searchMovieDetails(); // 等待详情请求完成 }; const searchMovieDetails = async () => { // 使用Promise.all等待所有异步请求完成 const detailPromises = movies.map(async (movie) => { const res = await fetch(`http://www.omdbapi.com/?t=${movie.Title}&apikey=${APIKEY}`); return await res.json(); }); fullMovies = await Promise.all(detailPromises); movieItemHTML(); }; function movieItemHTML() { exploring.style.display = 'none'; // 清空容器内容,避免叠加 movieItem.innerHTML = ''; let html = ''; fullMovies.forEach((e) => { html += ` <div class="movie-item"> <img class="poster" src="${e.Poster}"/> <div class="movie-desc"> <div class="title-rating"> <h3>${e.Title}</h3> <img src="./images/star.png" class="star"/> <p class="rating"> ${e.imdbRating}</p> </div> <div class="time-kind-btn"> <p>${e.Runtime}</p> <p>${e.Genre}</p> <a class="watchlist"><img src="./images/addBtn.png"> Watchlist</a> </div> <div> <p class="plot">${e.Plot}</p> </div> </div> </div> `; }); movieItem.innerHTML = html; }
内容的提问来源于stack exchange,提问作者Biobrolly
相关产品推荐
相关产品推荐

