JavaScript addEventListener对reply按钮失效archive按钮正常原因求解
问题根源
你碰到的是innerHTML +=操作的典型副作用:
当你对一个DOM元素执行innerHTML += 内容时,浏览器会先把该元素下所有现有DOM节点序列化为HTML字符串,拼接上新内容后,再把整段字符串重新解析为全新的DOM节点替换原有内容。原来绑定在旧DOM节点上的事件监听器会随着旧节点被销毁而完全丢失。
看你代码的执行顺序就能精准定位问题:
- 先把发件人、主题等信息通过
innerHTML +=写入容器 - 然后创建Reply按钮,添加到容器,绑定点击事件
- 接着你又执行了
document.querySelector('#ind-email-view').innerHTML += 邮件正文这行代码,这一步直接把刚加进去的带事件的Reply按钮销毁了,替换成了没有绑定任何事件的新Reply按钮 - 最后创建Archive按钮,添加到容器之后没有再执行
innerHTML操作,所以它的事件保留可以正常触发
修复方案
最简单的改法是把所有纯文本的innerHTML操作都放到创建按钮之前,调整代码顺序即可:
function get_email(email_id) { const emailView = document.querySelector('#ind-email-view'); // 先清空容器 emailView.innerHTML = ''; fetch(`/emails/${email_id}`) .then(response => response.json()) .then(email => { // 标记已读的请求不变 fetch(`/emails/${email.id}`, { method: 'PUT', body: JSON.stringify({ read: true }) }); // 把所有innerHTML拼接操作全部先做完 let html = ` <b>From: </b>${email.sender}<br> <b>To: </b>${email.recipients}<br> <b>Subject: </b>${email.subject}<br> <b>Timestamp: </b>${email.timestamp}<br><hr> <br> <br>${email.body}<br><br> `; emailView.innerHTML = html; // 所有innerHTML操作完成后再统一创建按钮、绑定事件 var reply = document.createElement('button'); reply.innerHTML = "Reply"; emailView.appendChild(reply); reply.addEventListener('click', () => { console.log('button clicked') }); var archive = document.createElement('button'); archive.innerHTML = "Archive"; emailView.appendChild(archive) archive.addEventListener('click', () => { archive_email(email); }); }); }
另外还有两种可选优化方案:
- 不用
innerHTML +=拼接内容,改用createElement、createTextNode、appendChild等原生DOM操作方法添加文本内容,不会影响已存在的节点 - 改用事件委托,把点击事件绑定在
#ind-email-view容器上,通过事件冒泡判断点击的是哪个按钮,完全不受DOM节点替换的影响
内容的提问来源于stack exchange,提问作者markd227
相关产品推荐
相关产品推荐

