removeChild()功能正常但抛出参数非Node类型错误排查
错误根源
你遇到的报错是删除按钮重复绑定事件+重复ID+单本书生成多个删除按钮三个问题共同导致的:
- 违反HTML ID唯一性规范:你给所有删除按钮都设置了相同的id
delete-btn,每次新增书籍后调用document.querySelectorAll('#delete-btn')会选中页面上所有历史生成的删除按钮,而非仅新创建的按钮。 - 事件重复绑定:每新增一本书,你就会遍历全量删除按钮,给每个按钮再追加一次相同的点击回调。比如添加3本书后,第一本书的删除按钮上会绑定3次完全相同的删除逻辑。点击按钮时第一次回调执行成功删除对应DOM节点,后续回调执行时目标节点已经被移除,
document.getElementById()返回null,将null作为参数传入removeChild()就会触发「参数不是Node类型」的报错,这也是为什么你看到删除功能正常但控制台抛错。 - 冗余按钮生成:你把删除按钮的创建逻辑写在了遍历书籍属性的
for...in循环内部,一本书有多少个属性(作者、标题、页数、阅读状态共4个),就会在卡片里生成多少个叠放在同一位置的删除按钮,进一步放大了重复绑定的问题。
修复方法
- 把删除按钮的创建逻辑移到属性遍历的
for循环外部,保证单本书卡片只生成1个删除按钮 - 用class代替重复id标记删除按钮
- 删除全局遍历删除按钮绑定事件的逻辑,改为创建新按钮时直接给当前按钮绑定单次事件
- 增加节点存在性判断,极端场景下也不会抛错
修复后的核心JS代码如下:
const books = []; const newBook = document.getElementById('new-book'); const form = document.querySelector('form'); const formButton = document.getElementById('form-button'); const content = document.querySelector('.content'); newBook.addEventListener('click', e => { toggleForm(); }) function toggleForm() { form.classList.toggle('form-hidden'); form.classList.toggle('form-visible'); } formButton.addEventListener('click', e => { const obj = {}; const inputs = Array.from(document.querySelectorAll('form input')); inputs.forEach(input => { obj[input.name] = input.value; }) books.push(obj); inputs.forEach(input => { input.value = ''; }) toggleForm(); createBooks(); }) function createBooks() { const bookDiv = document.createElement('div'); bookDiv.className = 'book'; const bookIndex = books.length-1; // 遍历生成书籍属性 for (const key in books[bookIndex]) { const bookGrid = document.createElement('div'); bookGrid.className = 'book-grid'; const h3 = document.createElement('h1'); h3.textContent = `${key}:`; const p = document.createElement('h2'); p.textContent = books[bookIndex][key]; h3.className = 'long'; p.className = 'long'; bookGrid.append(h3, p); bookDiv.appendChild(bookGrid); } // 循环外生成单个删除按钮 const btn = document.createElement('button'); btn.style.top = '0.3rem'; btn.style.right = '.5rem'; btn.style.position= 'absolute'; btn.textContent = 'Delete'; btn.className = 'button-1 delete-btn'; btn.style.fontSize = '1rem'; btn.setAttribute('data-key', `${bookIndex}`); bookDiv.appendChild(btn); bookDiv.setAttribute('id', `${bookIndex}`); bookDiv.style.position='relative'; content.appendChild(bookDiv); // 仅给当前新生成的按钮绑定单次删除事件 btn.addEventListener('click', () => { const targetKey = parseInt(btn.getAttribute('data-key')); const targetNode = document.getElementById(targetKey); if (targetNode) { content.removeChild(targetNode); books.splice(targetKey, 1); // 同步删除数组内对应数据,避免索引错乱 } }) }
内容的提问来源于stack exchange,提问作者user17833823
相关产品推荐
相关产品推荐

