JavaScript搜索框异常:搜索时餐厅卡片重复显示问题求助
问题:搜索餐厅时卡片重复生成
实现的搜索框需要匹配餐厅名称与描述,但每次触发搜索操作,页面中的餐厅卡片都会重复生成。问题出在负责创建卡片的showRestaurants函数,不清楚如何修复。
相关代码
JavaScript代码
import { RestaurantService } from './restaurant-service.class.js'; import '../node_modules/bootstrap/dist/css/bootstrap.css'; import css from '../styles.css'; import RestaurantTemplate from '../templates/restaurant.handlebars'; let restaurants = []; const restaurantService = new RestaurantService(); const weekdays = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa']; const currentDay = new Date().getDay(); restaurantService.getAll().then((array) => { restaurants = array; restaurants = array.filter((restaurant) => restaurants.id !== restaurant.id); showRestaurants(restaurants); }); function createCard(titleName, descriptionText, openDays, cuisineText, phone, image, id) { const objectCard = { restaurantName: titleName, restaurantDescription: descriptionText, restaurantOpenDays: openDays.map(day => weekdays[day]).join(', '), restaurantOpen: openDays.includes(String(currentDay)), restaurantCuisine: cuisineText, restaurantPhoneNumber: phone, restaurantImageSource: image, }; const htmlCard = RestaurantTemplate(objectCard); const container = document.createElement('div'); container.className = 'col'; container.innerHTML = htmlCard; container.getElementsByTagName('button')[0].addEventListener('click', () => { if (window.confirm('Are you sure to delete this card?')) { restaurantService.delete(id).then(() => { container.remove(); }); } }); document.getElementById('placesContainer').appendChild(container); } function showRestaurants(restaurants) { for (let index = 0; index < restaurants.length; index++) { createCard(restaurants[index].name, restaurants[index].description, restaurants[index].daysOpen, restaurants[index].cuisine, restaurants[index].phone, restaurants[index].image ,restaurants[index].id); } } // Search box but actually duplicates the cards that find document.getElementById('search').addEventListener('keyup', e => { const searchRestaurant = restaurants.filter(restaurant => restaurant.name.toLowerCase().includes(document.getElementById('search').value.toLowerCase()) || restaurant.description.toLowerCase().includes(document.getElementById('search').value.toLowerCase())); showRestaurants(searchRestaurant); });
HTML代码
<html> <head> <title>Exercise 3 | Home</title> <meta charset="UTF-8"> </head> <body> <nav class="navbar navbar-expand navbar-dark bg-dark"> <div class="container-fluid"> <a class="navbar-brand" href="#">FoodScore</a> <ul class="navbar-nav me-auto mb-lg-0"> <li class="nav-item"> <a class="nav-link active" href="index.html">Home</a> </li> <li class="nav-item"> <a class="nav-link" href="new-restaurant.html">New restaurant</a> </li> </ul> </div> </nav> <div class="container"> <!--Optional--> <nav class="navbar navbar-light bg-light justify-content-between mt-3"> <form class="container-fluid"> <input class="form-control" type="text" name="search" id="search" placeholder="Search" aria-label="Search"> </form> </nav> <div id="placesContainer" class="mb-4 mt-2 row row-cols-1 row-cols-md-2 row-cols-xl-3 g-4"> <!-- <div class="col"> <div class="card h-100 shadow"> <img class="card-img-top" src="IMAGE_BASE64"> <div class="card-body"> <button class="btn btn-danger btn-sm float-end">Delete</button> <h4 class="card-title">Restaurant Name</h4> <p class="card-text">Restaurant Description</p> <div class="card-text"> <small class="text-muted"> <strong>Opens: </strong>Mo, Tu, We, Th, Fr, Sa, Su </small> <span class="badge ms-2 bg-success">Open</span> </div> <div class="card-text"> <small class="text-muted"> <strong>Phone: </strong>999999999 </small> </div> </div> <div class="card-footer"> <small class="text-muted">Cuisine style</small> </div> </div> </div> --> </div> </div> </body> </html>
Handlebars模板
<div class="card h-100 shadow"> <img class="card-img-top" src="{{restaurantImageSource}}"> <div class="card-body"> <button class="btn btn-danger btn-sm float-end">Delete</button> <h4 class="card-title">{{restaurantName}}</h4> <p class="card-text">{{restaurantDescription}}</p> <div class="card-text"> <small class="text-muted"> <strong>Opens: </strong>{{restaurantOpenDays}} </small> {{#if restaurantOpen}} <span class="badge ms-2 bg-success">Open</span> {{else}} <span class="badge ms-2 bg-danger">Closed</span> {{/if}} </div> <div class="card-text"> <small class="text-muted"> <strong>Phone: </strong>{{restaurantPhoneNumber}} </small> </div> </div> <div class="card-footer"> <small class="text-muted">{{restaurantCuisine}}</small> </div> </div>
我曾尝试编写类似showRestaurants的过滤函数,但认为没必要,因为搜索框的监听事件中已使用includes方法完成过滤。
解决方案
核心问题
每次调用showRestaurants时,都是直接往placesContainer容器里追加新卡片,没有清空之前的旧卡片,导致重复生成。
具体修复步骤
- 清空容器后再渲染新卡片
修改showRestaurants函数,在生成卡片前先清空容器内容:
function showRestaurants(restaurants) { // 先清空容器内的旧卡片 const container = document.getElementById('placesContainer'); container.innerHTML = ''; for (let index = 0; index < restaurants.length; index++) { createCard(restaurants[index].name, restaurants[index].description, restaurants[index].daysOpen, restaurants[index].cuisine, restaurants[index].phone, restaurants[index].image ,restaurants[index].id); } }
- 修复初始加载的错误过滤逻辑
初始数据加载时的过滤代码有误:restaurants = array.filter((restaurant) => restaurants.id !== restaurant.id);,此时restaurants尚未完成赋值,且逻辑会过滤掉所有数据,直接删除该行即可:
restaurantService.getAll().then((array) => { restaurants = array; // 删除错误的过滤行 showRestaurants(restaurants); });
- 优化搜索事件的输入值获取
搜索事件中直接使用e.target.value获取输入内容,无需重复通过ID查询DOM,代码更简洁:
document.getElementById('search').addEventListener('keyup', e => { const searchValue = e.target.value.toLowerCase(); const searchRestaurant = restaurants.filter(restaurant => restaurant.name.toLowerCase().includes(searchValue) || restaurant.description.toLowerCase().includes(searchValue)); showRestaurants(searchRestaurant); });
修改完成后,每次搜索都会先清空旧卡片,再渲染过滤后的结果,即可解决重复生成的问题。
内容的提问来源于stack exchange,提问作者Aryus
相关产品推荐
相关产品推荐

