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

如何从db.json删除动态创建卡片及绑定对应删除按钮

问题分析与解决方案

核心问题原因

  1. 空NodeList问题:你在axios.get异步请求完成前就执行了document.querySelectorAll查询操作,此时动态卡片还未创建,自然获取不到任何元素,导致后续的事件绑定代码完全不执行。
  2. 删除按钮绑定错误:即使能拿到按钮,用循环索引i+1作为删除的ID逻辑错误——索引和卡片实际ID并不总是匹配(比如删除中间卡片后,剩余卡片的索引和ID会错位),而且新增卡片重新渲染后,旧的事件监听会残留,引发重复执行问题。

解决方案步骤

1. 渲染前清空容器

每次调用getData渲染卡片前,先清空mainWrapper内容,避免重复生成卡片:

const getData = (data: Card[]) => {
  // 清空容器,避免重复渲染
  mainWrapper.innerHTML = '';
  
  data.forEach(obj => {
    // ... 原有创建卡片的代码
  });
}

2. 直接为每个删除按钮绑定对应事件

在创建删除按钮时,直接利用当前卡片的obj.id绑定点击事件,确保每个按钮都对应正确的卡片:

// DELETE BUTTON
const deleteBtn = document.createElement("button");
deleteBtn.classList.add("delete__btn");
deleteBtn.classList.add(`js-delete`);
deleteBtn.innerHTML = "DELETE";
// 直接绑定事件,使用当前卡片的ID
deleteBtn.addEventListener('click', () => {
  axios.delete(`http://localhost:3004/cards/${obj.id}`).then(({ data }) => {
    getData(data);
  });
});
btnSection.appendChild(deleteBtn);

3. 移除无效的事件绑定代码

删除原来的以下代码,因为它们在DOM元素生成前执行,完全无效:

let deleteButtons = document.querySelectorAll<HTMLButtonElement>(`.js-delete`)
let editButtons = document.querySelectorAll<HTMLButtonElement>('.js-edit')
let cards = document.querySelectorAll('.js-card')

for (let i = 0; i < deleteButtons.length; i++) {
  deleteButtons[i].addEventListener('click', () => {
    axios.delete<Card[]>(`http://localhost:3004/cards/${i +1}`).then(({ data }) => {
      getData(data)
    });
  });
}

修改后的完整TypeScript代码

import axios from 'axios';

const mainWrapper = document.querySelector('.js-main-wrapper') as HTMLDivElement

type Card = {
  id: number,
  title: string,
  image: string,
  text: string
}

const getData = (data: Card[]) => {
  // 清空容器,避免重复渲染
  mainWrapper.innerHTML = '';
  
  data.forEach(obj => {
    // CARD
    const card = document.createElement("div");
    card.classList.add("card");
    card.classList.add("js-card");
    mainWrapper.appendChild(card);
  
    // IMG-SECTION
    const imgSection = document.createElement("div");
    imgSection.classList.add("img__section");
    card.appendChild(imgSection);
  
    // TEXT-SECTION
    const txtSection = document.createElement("div");
    txtSection.classList.add("text__section");
    card.appendChild(txtSection);
  
    // BUTTON SECTION
    const btnSection = document.createElement("div");
    btnSection.classList.add("btn__section");
    card.appendChild(btnSection);
  
    // IMAGE
    const image = document.createElement("img") as HTMLImageElement
    image.classList.add("image");
    image.src = `${obj.image}`
    imgSection.appendChild(image);
    
    // TITLE
    const title = document.createElement("h4");
    title.classList.add("title");
    title.innerHTML = `${obj.title}`;
    txtSection.appendChild(title);
    
    // TEXT
    const text = document.createElement("p");
    text.classList.add("text");
    text.innerHTML = `${obj.text}`;
    txtSection.appendChild(text);
  
    // EDIT BUTTON
    const editBtn = document.createElement("button");
    editBtn.classList.add("edit__btn");
    editBtn.classList.add("js-edit");
    editBtn.innerHTML = "EDIT";
    btnSection.appendChild(editBtn);
  
    // DELETE BUTTON
    const deleteBtn = document.createElement("button");
    deleteBtn.classList.add("delete__btn");
    deleteBtn.classList.add(`js-delete`);
    deleteBtn.innerHTML = "DELETE";
    // 绑定对应卡片的删除事件
    deleteBtn.addEventListener('click', () => {
      axios.delete(`http://localhost:3004/cards/${obj.id}`).then(({ data }) => {
        getData(data);
      });
    });
    btnSection.appendChild(deleteBtn);
  });
}

axios.get<Card[]>('http://localhost:3004/cards').then(({ data }) => {
  getData(data);
})

const addTitle = document.querySelector('.js-add-title') as HTMLInputElement
const addText = document.querySelector('.js-add-text') as HTMLInputElement
const addBtn = document.querySelector('.js-add-btn')

// ADD NEW
addBtn?.addEventListener('click', (e) => {
  // 阻止表单默认提交行为,避免页面刷新
  e.preventDefault();
  axios.post<Card[]>('http://localhost:3004/cards', {
    title: addTitle.value,
    text: addText.value,
    image: "https://picsum.photos/200",
  }).then(({ data }) => {
     getData(data);
     // 新增后清空输入框
     addTitle.value = '';
     addText.value = '';
  });
});

额外补充:新增功能里添加e.preventDefault()阻止表单默认提交导致的页面刷新,同时新增后清空输入框提升用户体验。

内容的提问来源于stack exchange,提问作者Zixxy7

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 02:15:26