一、获取 DOM 元素
这是最基础的一组。
1. getElementById
const app = document.getElementById('app')
根据 id 获取元素。
2. getElementsByClassName
const items = document.getElementsByClassName('item')
返回:
HTMLCollection
注意它通常是实时集合(live collection)。
3. getElementsByTagName
const divs = document.getElementsByTagName('div')
获取指定标签。
4. querySelector
const el = document.querySelector('.item')
返回第一个匹配元素。
5. querySelectorAll
const els = document.querySelectorAll('.item')
返回:
NodeList
常用于批量查询。
面试重点
一定要知道:
getElementsByClassName
getElementsByTagName
↓
HTMLCollection
↓
通常是 live
querySelectorAll
↓
NodeList
↓
通常是 static
二、DOM 树关系
这组非常重要,你刚才写 DFS 就用到了。
假设:
<div id="parent">
<div id="child">
<span id="grandchild"></span>
</div>
</div>
parentElement
获取父元素:
child.parentElement
结果:
parent
children
获取元素子节点:
parent.children
结果:
HTMLCollection
├── div#child
重点:
children只包含 Element,不包含文本节点。
childNodes
parent.childNodes
它包含:
-
Element
-
Text
-
Comment
比如 HTML:
<div>
hello
<span></span>
</div>
空格和换行本身也可能形成 Text Node。
所以:
children
和:
childNodes
一定要区分。
firstElementChild
parent.firstElementChild
第一个元素子节点。
lastElementChild
parent.lastElementChild
最后一个元素子节点。
nextElementSibling
child.nextElementSibling
下一个兄弟元素。
previousElementSibling
child.previousElementSibling
上一个兄弟元素。
三、创建、插入、删除 DOM
这组实际开发非常常用。
创建
const div = document.createElement('div')
设置内容
div.textContent = 'hello'
或者:
div.innerHTML = '<span>hello</span>'
两者区别非常重要:
textContent
把内容当普通文本。
innerHTML
把字符串解析成 HTML。
添加子节点
parent.appendChild(child)
append
parent.append(child)
也可以:
parent.append('hello')
prepend
parent.prepend(child)
插到第一个。
before
element.before(newElement)
after
element.after(newElement)
删除
element.remove()
替换
parent.replaceChild(newChild, oldChild)
现代 API:
oldChild.replaceWith(newChild)
四、属性 Attribute
这个很重要,因为:
<div id="app" data-id="123"></div>
这里的:
id
data-id
属于 HTML Attribute。
getAttribute
el.getAttribute('data-id')
setAttribute
el.setAttribute('data-id', '456')
hasAttribute
el.hasAttribute('data-id')
removeAttribute
el.removeAttribute('data-id')
五、class 操作
这个在 Vue/React 原生 DOM 操作里都很常见。
classList
el.classList
它是:
DOMTokenList
添加
el.classList.add('active')
删除
el.classList.remove('active')
判断
el.classList.contains('active')
切换
el.classList.toggle('active')
例如:
el.classList.toggle('active')
有:
active → 删除
没有:
没有 active → 添加
六、style
直接操作内联样式:
el.style.width = '100px'
el.style.height = '200px'
el.style.display = 'none'
也可以:
el.style.cssText = `
width: 100px;
height: 200px;
`
不过实际开发一般不建议大量操作 cssText。
七、获取真实尺寸
这个是学习虚拟列表、ResizeObserver、性能优化特别应该掌握的一组。
getBoundingClientRect()
const rect = el.getBoundingClientRect()
得到:
{
x,
y,
width,
height,
top,
right,
bottom,
left
}
例如:
rect.height
就是元素当前渲染出来的高度。
offsetWidth
el.offsetWidth
通常表示:
content
+ padding
+ border
offsetHeight
el.offsetHeight
同理。
clientWidth
el.clientWidth
通常:
content + padding
不包含 border。
clientHeight
el.clientHeight
scrollWidth
el.scrollWidth
元素内容实际需要的宽度。
例如:
┌──────────────┐
│ 可视区域 │
│ │
└──────────────┘
←──────────────→
500px
但是里面内容有:
1000px
那么:
el.clientWidth // 500
el.scrollWidth // 1000
scrollHeight这个你在聊天虚拟列表里已经用到了:
el.scrollHeight
表示内容的完整滚动高度。
scrollTop
el.scrollTop
当前滚动了多少。
scrollLeft
el.scrollLeft
八、滚动相关 API
scrollTo
el.scrollTo({
top: 1000,
behavior: 'smooth'
})
scrollBy
el.scrollBy({
top: 100
})
表示:
在当前位置基础上再滚 100px。
scrollIntoView
el.scrollIntoView()
让元素进入视口。
也可以:
el.scrollIntoView({
behavior: 'smooth',
block: 'center'
})
九、事件
这个也是 DOM 核心知识。
添加事件
el.addEventListener('click', handler)
删除事件
el.removeEventListener('click', handler)
注意必须是同一个函数引用:
function handler() {}
el.addEventListener('click', handler)
el.removeEventListener('click', handler)
不能:
el.removeEventListener('click', () => {})
因为不是同一个函数。
十、事件对象
el.addEventListener('click', (event) => {
console.log(event)
})
常见:
event.target
event.currentTarget
event.preventDefault()
event.stopPropagation()
target
真正触发事件的元素。
currentTarget
当前绑定事件监听器的元素。
这是面试高频题。
十一、事件传播
DOM 事件:
捕获阶段
↓
target
↓
冒泡阶段
例如:
<div id="parent">
<button id="child">点击</button>
</div>
:
parent.addEventListener('click', () => {
console.log('parent')
})
child.addEventListener('click', () => {
console.log('child')
})
点击 button:
child
parent
可以指定捕获:
parent.addEventListener(
'click',
handler,
true
)
或者:
parent.addEventListener('click', handler, {
capture: true
})
十二、节点操作
这个是面试手写 DOM 题经常出现的。
cloneNode
const copy = element.cloneNode(true)
true:
连后代一起复制。
cloneNode(false)
只复制自己。
contains
parent.contains(child)
判断:
child 是否是 parent 的后代。
matches
element.matches('.foo')
判断当前元素是否匹配 CSS selector。
closest
element.closest('.container')
从当前元素开始向上寻找最近的匹配祖先。
这个实际开发非常常用。
例如事件委托:
container.addEventListener('click', e => {
const button = e.target.closest('button')
if (!button) return
// ...
})
十三、MutationObserver
ResizeObserver
关注:
尺寸变化
const observer = new ResizeObserver(entries => {
console.log(entries)
})
observer.observe(element)
MutationObserver
关注 DOM 结构/属性变化:
const observer = new MutationObserver(mutations => {
console.log(mutations)
})
observer.observe(element, {
childList: true,
attributes: true,
subtree: true
})
简单记:
ResizeObserver
→ 尺寸变了
MutationObserver
→ DOM/属性变了
IntersectionObserver
→ 元素和视口的交叉状态变了
十四、IntersectionObserver
懒加载、无限滚动非常常见。
const observer = new IntersectionObserver(entries => {
entries.forEach(entry => {
if (entry.isIntersecting) {
console.log('进入视口')
}
})
})
observer.observe(element)
例如:
列表
↓
↓
↓
[加载更多 sentinel]
当 sentinel 进入视口:
IntersectionObserver
↓
加载下一页
十五、你现在最应该掌握的 DOM API
如果是为了前端面试,我建议按照这个优先级:
第一梯队:一定熟练
getElementById
querySelector
querySelectorAll
children
childNodes
parentElement
firstElementChild
lastElementChild
nextElementSibling
previousElementSibling
createElement
appendChild
append
prepend
remove
replaceWith
getAttribute
setAttribute
removeAttribute
classList
textContent
innerHTML
addEventListener
removeEventListener
target
currentTarget
preventDefault
stopPropagation
第二梯队:你现在虚拟列表特别需要
getBoundingClientRect
offsetWidth
offsetHeight
clientWidth
clientHeight
scrollWidth
scrollHeight
scrollTop
scrollLeft
scrollTo
scrollBy
scrollIntoView
第三梯队:高级前端非常常用
ResizeObserver
MutationObserver
IntersectionObserver
closest
matches
contains
cloneNode
适合现在阶段的学习顺序
建议按这个顺序:
① DOM 树
↓
children / childNodes / parentElement
↓
nextElementSibling / firstElementChild
② DOM 增删改
↓
createElement / append / remove
↓
innerHTML / textContent
③ CSS / 属性
↓
classList / style
↓
getAttribute / dataset
④ 尺寸与滚动
↓
clientHeight
offsetHeight
scrollHeight
getBoundingClientRect
scrollTop
⑤ 事件
↓
addEventListener
↓
target / currentTarget
↓
捕获 / 冒泡 / 事件委托
⑥ Observer
↓
ResizeObserver
MutationObserver
IntersectionObserver
⑦ 最后再结合
↓
虚拟列表
无限滚动
懒加载
聊天流式渲染
尤其是 children / childNodes、clientHeight / offsetHeight / scrollHeight、target / currentTarget、三种 Observer。