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

使用insertAdjacentHTML渲染列表时removeChild()仅对首个子项生效问题

修复列表项删除功能异常问题

问题根源

  1. 事件绑定错误:每次循环中使用document.querySelectorAll('.task-item')[0]和document.querySelectorAll('.clear-item')[0]始终获取页面中第一个匹配的元素,导致所有删除点击事件都绑定到了第一个列表项和删除图标上——这就是只有顶部项能删除、其他项无响应的原因。
  2. 无效DOM操作:第一个项被删除后,绑定的事件仍试图操作已被移除的DOM节点,因此抛出Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node错误。
  3. 标签语法错误:代码中<i>标签存在多余的闭合标签</i>,需修正。

解决方案:推荐使用事件委托(更高效)

事件委托只需给父容器绑定一次事件,即可处理所有子元素的点击,避免重复绑定,同时支持动态新增的列表项。

修改后的代码:

const renderList = (activities) => {
  const display = document.getElementById('task-list-display');
  // 清空容器,避免重复渲染(按需保留)
  display.innerHTML = '';
  
  activities.forEach((activity) => {
    display.insertAdjacentHTML('beforeend', ` 
    <li class="task-item">
      <div class="chk-descr">
        <input 
          data-a1="${activity.index}"
          type="checkbox"
          name="completed"
          class="completed"
        />
        <p data-b1="${activity.index}" class="description" contenteditable="true">${activity.description}</p>
      </div>
      <i class="clear-item fa fa-trash" data-index="${activity.index}"></i>
    </li> 
    `);
  });

  // 事件委托:父容器监听点击,判断是否点击删除按钮
  display.addEventListener('click', (e) => {
    if (e.target.classList.contains('clear-item')) {
      // 找到当前删除按钮所属的列表项
      const listItem = e.target.closest('.task-item');
      if (listItem) {
        // 直接移除列表项
        listItem.remove();
        // 如需更新数据源activities,可添加以下逻辑:
        // activities = activities.filter(item => item.index !== parseInt(e.target.dataset.index));
      }
    }
  });
};

关键改动说明:

  • 移除重复的id="task-item":ID必须唯一,循环生成重复ID不符合规范。
  • 修正<i>标签闭合错误。
  • 给删除图标添加data-index:方便后续关联对应活动项(如需更新数据源)。
  • 使用closest('.task-item'):快速定位删除按钮所属的列表项,无需复杂DOM查询。
  • 调用listItem.remove():直接删除元素自身,比removeChild更简洁可靠。

备选方案:绑定单个项的事件

如果不想用事件委托,可在每次插入列表项后,直接获取刚添加的元素并绑定事件:

const renderList = (activities) => {
  const display = document.getElementById('task-list-display');
  display.innerHTML = '';
  
  activities.forEach((activity) => {
    display.insertAdjacentHTML('beforeend', ` 
    <li class="task-item">
      <div class="chk-descr">
        <input 
          data-a1="${activity.index}"
          type="checkbox"
          name="completed"
          class="completed"
        />
        <p data-b1="${activity.index}" class="description" contenteditable="true">${activity.description}</p>
      </div>
      <i class="clear-item fa fa-trash"></i>
    </li> 
    `);
    // 获取刚插入的最后一个列表项
    const listItem = display.lastElementChild;
    // 在当前列表项内查找删除按钮
    const clearItem = listItem.querySelector('.clear-item');
    clearItem.addEventListener('click', () => {
      listItem.remove();
      // 如需更新数据源,在此处处理
    });
  });
};

关键改动说明:

  • 使用display.lastElementChild获取刚插入的列表项,而非查询整个文档的第一个元素。
  • 每个删除按钮仅绑定到对应列表项的事件,避免事件绑定混乱。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 14:43:14