You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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容器里追加新卡片,没有清空之前的旧卡片,导致重复生成。

具体修复步骤

  1. 清空容器后再渲染新卡片
    修改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);
    }
}
  1. 修复初始加载的错误过滤逻辑
    初始数据加载时的过滤代码有误:restaurants = array.filter((restaurant) => restaurants.id !== restaurant.id);,此时restaurants尚未完成赋值,且逻辑会过滤掉所有数据,直接删除该行即可:
restaurantService.getAll().then((array) => {
    restaurants = array;
    // 删除错误的过滤行
    showRestaurants(restaurants);
});
  1. 优化搜索事件的输入值获取
    搜索事件中直接使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 14:25:15