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

JavaScript动态操作HTML后querySelectorAll失效及数据覆盖问题求助

CRUD客户列表问题排查与修复

问题根源

  1. 数组数据被覆盖:每次调用cacheSelectors时都会执行this.clients = [],清空客户数组。在displayClients循环渲染时反复调用该方法,导致新添加的客户数据被重置,最终只能保留最后一条。
  2. 动态删除按钮事件失效:初始绑定事件时,动态生成的删除按钮还未创建,querySelectorAll无法获取到元素;重复调用bindEvents不仅会重复绑定事件,还会触发数组清空的问题。

修复步骤

1. 固定客户数组初始化位置

将this.clients = []从cacheSelectors移到init方法中,仅在初始化时创建空数组,避免后续缓存选择器时清空数据:

init: function () {
  this.clients = [] // 移至此处,仅初始化一次
  this.cacheSelectors()
  this.bindEvents()
},

cacheSelectors: function () {
  this.addButton = document.querySelector('#addButton')
  this.modal = document.querySelector('#modal')
  this.saveButton = document.querySelector('#saveButton')
  this.tbody = document.querySelector('#tbody')
  this.companyInput = document.querySelector('#companyName')
  this.contactInput = document.querySelector('#contactName')
  this.countryInput = document.querySelector('#countryName')
  this.inputs = document.querySelectorAll('input')
  this.removeButtons = document.querySelectorAll('.deleteButton')
  // 移除this.clients = []
},

2. 用事件委托处理动态按钮

不再单独给每个删除按钮绑定事件,而是把点击事件绑定到父元素<tbody>,利用事件冒泡识别点击的是删除按钮,兼容动态生成的元素:

bindEvents: function () {
  const self = this

  this.addButton.addEventListener('click', this.Events.addButton_click.bind(this))

  // 修复closeModal的作用域问题
  window.onclick = function(event) {
    if (event.target == self.modal) {
      self.modal.style.display = "none";
    }
  }

  this.saveButton.addEventListener('click', this.Events.saveClient_click.bind(this))

  // 事件委托处理删除按钮点击
  this.tbody.addEventListener('click', function(event) {
    const deleteBtn = event.target.closest('.deleteButton')
    if (deleteBtn) {
      // 获取对应客户ID
      const row = deleteBtn.closest('tr')
      const clientId = parseInt(row.querySelector('td:first-child').textContent)
      self.Events.removeButton_click.call(self, clientId)
    }
  })
},

3. 优化displayClients方法

移除循环内的cacheSelectors和bindEvents调用,同时优化HTML拼接方式,减少DOM操作次数:

displayClients: function () {
  const rows = this.clients.map((client) => `
    <tr>
      <td>${client.id}</td>
      <td>${client.company}</td>
      <td>${client.contact}</td>
      <td>${client.country}</td>
      <td>
        <div><i class="fa-solid fa-pen"></i></div>
        <div class="deleteButton"><i class="fa-solid fa-trash"></i></div>
      </td>
    </tr>
  `).join('')
  this.tbody.innerHTML = rows
  console.log(this.clients)
},

4. 补充删除逻辑

在Events.removeButton_click中实现客户删除逻辑:

removeButton_click: function (clientId) {
  this.clients = this.clients.filter(client => client.id !== clientId)
  this.displayClients()
  alert(`已删除ID为${clientId}的客户`)
} 

修改后的完整代码

const Main = {
  init: function () {
    this.clients = []
    this.cacheSelectors()
    this.bindEvents()
  },

  cacheSelectors: function () {
    this.addButton = document.querySelector('#addButton')
    this.modal = document.querySelector('#modal')
    this.saveButton = document.querySelector('#saveButton')
    this.tbody = document.querySelector('#tbody')
    this.companyInput = document.querySelector('#companyName')
    this.contactInput = document.querySelector('#contactName')
    this.countryInput = document.querySelector('#countryName')
    this.inputs = document.querySelectorAll('input')
    this.removeButtons = document.querySelectorAll('.deleteButton')
  },

  bindEvents: function () {
    const self = this

    this.addButton.addEventListener('click', this.Events.addButton_click.bind(this))

    window.onclick = function(event) {
      if (event.target == self.modal) {
        self.modal.style.display = "none";
      }
    }

    this.saveButton.addEventListener('click', this.Events.saveClient_click.bind(this))

    this.tbody.addEventListener('click', function(event) {
      const deleteBtn = event.target.closest('.deleteButton')
      if (deleteBtn) {
        const row = deleteBtn.closest('tr')
        const clientId = parseInt(row.querySelector('td:first-child').textContent)
        self.Events.removeButton_click.call(self, clientId)
      }
    })
  },

  Events: {
    addButton_click:function () {
      this.modal.style.display = 'block'
      this.inputs.forEach(input => {
        input.value = ''
      })
    },

    saveClient_click: function () {
      const companyName = this.companyInput.value
      const contactName = this.contactInput.value
      const countryName = this.countryInput.value

      const addClient = {
        id: (this.clients.length + 1),
        company: companyName,
        contact: contactName,
        country: countryName
      }

      if (companyName && contactName && countryName) {
        this.clients.push(addClient)
        this.modal.style.display = "none";
      } else {
        alert('All fields are required')
      }
      
      this.displayClients()
    },

    removeButton_click: function (clientId) {
      this.clients = this.clients.filter(client => client.id !== clientId)
      this.displayClients()
      alert(`已删除ID为${clientId}的客户`)
    } 
  },

  displayClients: function () {
    const rows = this.clients.map((client) => `
      <tr>
        <td>${client.id}</td>
        <td>${client.company}</td>
        <td>${client.contact}</td>
        <td>${client.country}</td>
        <td>
          <div><i class="fa-solid fa-pen"></i></div>
          <div class="deleteButton"><i class="fa-solid fa-trash"></i></div>
        </td>
      </tr>
    `).join('')
    this.tbody.innerHTML = rows
    console.log(this.clients)
  },
}

Main.init()

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 13:45:16