什么是 DOM?常见 DOM 操作有哪些?

DOM(Document Object Model)

DOM 是 HTML/XML 文档的编程接口,将文档表示为树形结构,JavaScript 可以通过 DOM API 操作页面内容。

document
  └── html
       ├── head
       │    ├── title
       │    └── meta
       └── body
            ├── div
            │    └── p
            └── script

获取元素

// 现代 API
document.querySelector('#id')        // CSS 选择器,返回第一个
document.querySelectorAll('.class')  // 返回 NodeList

// 传统 API
document.getElementById('id')
document.getElementsByClassName('class')  // HTMLCollection(动态)
document.getElementsByTagName('div')      // HTMLCollection(动态)

创建和添加

// 创建
const div = document.createElement('div')
div.textContent = 'Hello'
div.className = 'box'
div.setAttribute('data-id', '1')

// 添加
document.body.appendChild(div)           // 追加到末尾
document.body.insertBefore(div, refEl)   // 在指定元素前插入
document.body.append(div, 'text')        // 支持多参数和文本

// 现代 API
parentEl.prepend(newEl)    // 开头插入
parentEl.before(newEl)     // 元素前插入
parentEl.after(newEl)      // 元素后插入

删除和替换

// 删除
el.remove()                      // 现代 API
parentEl.removeChild(el)         // 传统 API

// 替换
parentEl.replaceChild(newEl, oldEl)
oldEl.replaceWith(newEl)         // 现代 API

修改内容和属性

// 内容
el.textContent = '纯文本'         // 安全(自动转义)
el.innerHTML = '<b>HTML</b>'     // 可解析 HTML(注意 XSS)
el.innerText = '可见文本'         // 触发重排

// 属性
el.id = 'myId'
el.className = 'box active'
el.classList.add('active')        // 添加 class
el.classList.remove('active')     // 移除 class
el.classList.toggle('active')     // 切换 class
el.classList.contains('active')   // 是否包含

// dataset
el.dataset.userId = '123'        // data-user-id="123"

// 样式
el.style.color = 'red'
el.style.setProperty('--custom', 'value')

遍历

el.parentNode        // 父节点
el.parentElement     // 父元素节点
el.children          // 子元素集合
el.firstChild        // 第一个子节点(可能是文本节点)
el.firstElementChild // 第一个元素子节点
el.nextElementSibling // 下一个兄弟元素

性能优化

1. 使用 DocumentFragment 批量操作

const fragment = document.createDocumentFragment()
items.forEach(item => {
  const li = document.createElement('li')
  li.textContent = item
  fragment.appendChild(li)
})
ul.appendChild(fragment) // 只触发一次重排

2. cloneNode 替代 createElement

const template = document.createElement('div')
template.className = 'item'
template.innerHTML = '<span></span>'

const clone = template.cloneNode(true) // 深拷贝
clone.querySelector('span').textContent = 'Hello'

3. innerHTML vs createElement
innerHTML 解析 HTML 字符串,大量元素时比 createElement 快,但有 XSS 风险。

MutationObserver(监听 DOM 变化)

const observer = new MutationObserver((mutations) => {
  mutations.forEach(m => console.log(m.type))
})
observer.observe(el, { childList: true, subtree: true, attributes: true })

同分类其他题目