模态框切换笔记时视图显示内容与DOM实际值不一致问题
问题现象
- 页面支持用户添加、查看笔记,点击单条笔记时会唤起
modal container展示笔记完整内容。定位笔记的逻辑为:通过closest方法找到当前点击的笔记元素,再将笔记内容、ID传入模态框容器。 - 首次打开任意笔记(例如笔记A)时,模态框可正常展示笔记A的内容与ID;更新该笔记(向firebase发送更新指令)后,笔记内容可正常同步更新,功能运行无异常。
- 异常复现路径:更新完笔记A后再打开笔记B,模态框ID会正确显示为笔记B的ID,但内容异常显示为笔记A的内容。在浏览器调试器Element面板检查确认,DOM中模态框容器内的内容和ID都已更新为正确值,但网页视图仍然渲染笔记A的内容。
- 补充现象:刷新页面后模态框即可正确加载对应笔记的值,初步推测异常与笔记列表更新逻辑有关。

相关代码
因代码依赖firebase登录态,无法直接在线运行,此处仅提供模态框相关逻辑供参考:
<body> <div class="modal-container"> <div id="background"></div> <div class="modal"> <button class="modal__btn">×</button> <button class="modal__btn_b">Save</button> <textarea id="modal__body">iis fugit possimus minus, repellendus dolores aliquid?</textarea> <textarea class="note__id">id should be here</textarea> </div> </div> <textarea id="todo-input" placeholder="Create a new todo..."></textarea> <button onclick="addItem(event)">Add</button> <div class="todo-items-wrapper"> <div class="todo-items"> <div class="todo-item"> <div class="check"> <div class="check-mark checked"></div> </div> </div> </div> </div> </body> <script type="text/javascript"> const todoitems = document.querySelector('.todo-items'); const modalContainer = document.querySelector('.modal-container'); function updateItem(notecontent, noteidd) { let text = notecontent; let newItem = firebase.firestore().collection("users").doc(firebase.auth().currentUser.uid).collection('notes').doc(noteidd).update({ content: text, createdAt: firebase.firestore.Timestamp.fromDate(new Date()) }) } function getItems() { var uid = null; firebase.auth().onAuthStateChanged(function (user) { if (user) { uid = user.uid; firebase.firestore().collection("users").doc(firebase.auth().currentUser.uid).collection('notes').orderBy("createdAt", "desc").onSnapshot((snapshot) => { let items = []; snapshot.docs.forEach((doc) => { items.push({ id: doc.id, ...doc.data() }) }) generateItems(items); }) } }) } function generateItems(items) { todoitems.innerHTML = "" items.forEach((item) => { const itemsHTML = document.createElement('div'); itemsHTML.classList.add('item'); itemsHTML.innerHTML = ` <span class="span__id">${item.id}</span> <span class="note__body">${item.content}</span> <div class="note__btns"> <button class="note__btn note__view">View Detail</button> <button class="note__btn note__delete">Delete Note</button> </div> `; todoitems.appendChild(itemsHTML) }) } window.onload = getItems(); todoitems.addEventListener('click', (e) => { if (e.target.classList.contains('note__view')) { const currentNote = e.target.closest('.item'); const currentID = currentNote.querySelector('.span__id').textContent; const currentBody = currentNote.querySelector('.note__body').textContent; activateNoteModal(currentBody, currentID); } }) function activateNoteModal(bodyy, noteid) { const modalBody = document.getElementById('modal__body'); modalBody.textContent = bodyy; const note_id = document.querySelector('.note__id'); note_id.textContent = noteid; modalContainer.classList.add('active'); } const modalBtn = document.querySelector('.modal__btn').addEventListener('click', () => { modalContainer.classList.remove('active'); }) const modalSaveBtn = document.querySelector('.modal__btn_b').addEventListener('click', (e) => { e.preventDefault(); const notecontent = document.getElementById('modal__body').value; const noteidd = document.querySelector('.note__id').value updateItem(notecontent, noteidd); }) </script>
问题根因与修复方案
问题和事件委托、列表重渲染无关,核心错误出在activateNoteModal函数对<textarea>元素的赋值方式上:
<textarea>的DOM子文本节点仅作为初始默认值存在,一旦用户对textarea做过输入交互、或者通过JS修改过它的value属性,浏览器就会以value属性的值作为渲染依据,此时再修改textContent/子节点文本,只会更新DOM结构,不会改变页面上实际渲染的内容。- 该场景中,打开笔记A后,保存操作读取了
modal__body的value属性提交更新,这个过程已经让textarea的value和子文本节点脱钩;后续打开笔记B时,给modalBody.textContent赋值只改了DOM文本,没有改value,所以页面上仍然显示之前的笔记A内容。而存放ID的.note__idtextarea从来没有被交互/修改过value,所以修改textContent可以正常同步显示,才会出现「ID正确、内容错误」的现象。
修复方式非常简单,把activateNoteModal函数里对两个textarea的赋值从textContent改成value即可:
function activateNoteModal(bodyy, noteid) { const modalBody = document.getElementById('modal__body'); // 改为给value赋值 modalBody.value = bodyy; const note_id = document.querySelector('.note__id'); // 改为给value赋值 note_id.value = noteid; modalContainer.classList.add('active'); }
其他用户给出的参考建议
用户Ahmed给出过调整事件绑定方式的建议,该方案无法解决当前问题,仅作为参考留存:
初始建议版本:
todoitems.querySelectorAll('.note__btns').forEach(btn => btn.addEventListener('click', e => { if (e.target.classList.contains('note__view')){ const currentNote = e.target.closest('.item'); const currentID = currentNote.querySelector('.span__id').textContent; const currentBody = currentNote.querySelector('.note__body').textContent; // 其余逻辑 activateNoteModal(currentBody, currentID); } }));
修正后的版本:
document.querySelectorAll('.note__btns').forEach(btn => btn.addEventListener('click', (e) => { const currentNote = e.target.closest('.item'); const currentID = currentNote.querySelector('.span__id').textContent; const currentBody = currentNote.querySelector('.note__body').textContent; // 其余逻辑 activateNoteModal(currentBody, currentID); }));
注:该方案存在两个明显问题:一是addEventListner拼写错误,正确写法为addEventListener;二是列表每次重渲染后都需要重新绑定事件,否则新生成的笔记项不会触发点击逻辑,可靠性不如原有的事件委托写法。
内容的提问来源于stack exchange,提问作者alex
相关产品推荐
相关产品推荐

