修改 DOM:从 createElement 到渲染函数

下面是能用的迷你待办清单:点条目切换完成、点 × 删除;后面几节还会对它做实验。

[迷你控制台]

一、渲染函数模式:数据变了,整块重建

let tasks = [
  { text: '写周报', done: true },
  { text: '修登录 bug', done: false }
]

function renderTasks() {
  todo.replaceChildren()              // 先清空(比 innerHTML = '' 更现代)
  tasks.forEach((task, index) => {
    const li = document.createElement('li')     // 造
    li.className = task.done ? 'item done' : 'item'
    li.dataset.index = index                    // 把「第几项」记在 DOM 上
    const txt = document.createElement('span')
    txt.className = 'txt'
    txt.textContent = task.text                 // 用户输入永远走 textContent
    li.append(txt)                              // 拼
    todo.append(li)                             // 挂
  })
}

todo.addEventListener('click', (e) => {
  const li = e.target.closest('.item')          // 事件委托:点在谁身上都能找回 li
  if (!li) return
  const index = Number(li.dataset.index)
  tasks[index].done = !tasks[index].done        // 改数据,然后重建
  renderTasks()
})

二、textContent 与 innerHTML:写文本还是解析 HTML

el.textContent = '<b>加粗</b>'   // 原样当纯文本:页面上就是这串字符
el.innerHTML  = '<b>加粗</b>'   // 交给浏览器解析:真的变成加粗文字

// 铁律:挂「用户输入」永远走 textContent;
// innerHTML 只用于你完全掌控的模板字符串(否则就是 XSS 入口)

三、classList 与 dataset:直接改节点,不重建

li.classList.add('flag')        // 加
li.classList.remove('flag')     // 删
li.classList.toggle('flag')     // 有则删、无则加,返回最终是否含有
li.classList.contains('flag')   // 判断 → true / false

li.dataset.index = 2            // 写 data-index="2"
li.dataset.index                // 读回来,只拿到字符串

四、remove:直接摘掉一个节点

li.remove()              // 从 DOM 树里摘掉自己
list.replaceChildren()   // 清空全部子节点
// 注意:这类「直接删节点」绕过了数据——
// 下一次 renderTasks() 重建,删掉的项会「复活」。数据才是真相源。