DOM
如今 Vue 和 React 框架應(yīng)該廣泛,封裝了 DOM。但 DOM 操作一直都是前端工程師的基礎(chǔ)崔列,必備知識(shí)。只會(huì) Vue 不懂 DOM 操作的前端程序員不會(huì)長(zhǎng)久旺遮。
題目
- DOM 屬于那種數(shù)據(jù)結(jié)構(gòu)赵讯?
- DOM 操作常用的 API 有哪些?
- attr 和 property 的區(qū)別?
- 如果一次性插入多個(gè) DOM 節(jié)點(diǎn)耿眉,考慮性能边翼?
知識(shí)點(diǎn)
- DOM 本質(zhì)
- DOM 節(jié)點(diǎn)操作
- DOM 結(jié)構(gòu)操作
- DOM 性能
DOM 的本質(zhì)
DOM( Document Object Model ) 的本質(zhì)就是從 HTML 文件中解析構(gòu)件出來(lái)的樹(shù)。
DOM (Document Object Model) 譯為文檔對(duì)象模型鸣剪,是 HTML 和 XML 文檔的編程接口组底。
HTML DOM 定義了訪問(wèn)和操作 HTML 文檔的標(biāo)準(zhǔn)方法。
DOM 以樹(shù)結(jié)構(gòu)表達(dá) HTML 文檔筐骇。
DOM 節(jié)點(diǎn)操作
獲取 DOM 節(jié)點(diǎn)
// 通過(guò) ID 獲取
const div1 = document.getElementById('div1')
console.log('div1', div1)
// 通過(guò) Tag Name 獲取
const divList = document.getElementsByTagName('div') // 集合
console.log('divList.length', divList.length)
console.log('divList[1]', divList[1])
// 通過(guò) Class Name 獲取
const containerList = document.getElementsByClassName('container') // 集合
console.log('containerList.length', containerList.length)
console.log('containerList[1]', containerList[1])
// 通過(guò) CSS 選擇器來(lái)獲取
const pList = document.querySelectorAll('p')
console.log('pList', pList)
const p1 = pList[0]
節(jié)點(diǎn)中的 property
// 獲取節(jié)點(diǎn)
const pList = document.querySelectorAll('p')
const p = pList[0]
console.log(p.style.width) // 獲取樣式
p.style.width = '100px' // 修改樣式
console.log(p.className) // 獲取 class
p.className = 'p1' // 修改 class
// 獲取 nodeName 和 nodeType
console.log(p.nodeName)
console.log(p.nodeType)
property 修改的是 js 變量(DOM節(jié)點(diǎn)對(duì)象)的屬性
節(jié)點(diǎn)中的 attribute
// 獲取節(jié)點(diǎn)
const pList = document.querySelectorAll('p')
const p = pList[0]
p.setAttribute('data-name', 'my-data')
console.log( p.getAttribute('data-name') ) // my-data
p.setAttribute('style', 'font-size:30px;')
console.log( p.getAttribute('style') ) // font-size:30px;
所謂的 attribute 是直接作用到標(biāo)簽上面的屬性
property 和 attribute 的區(qū)別
- property:修改對(duì)象屬性债鸡,不會(huì)體現(xiàn)到 html 結(jié)構(gòu)中
- attribute:修改 html 屬性,會(huì)改變 html 結(jié)構(gòu)
- 然而兩者都可能引起 DOM 重新渲染(一般情況下盡量用 property 操作)
DOM 結(jié)構(gòu)操作
- 新增插入節(jié)點(diǎn)
- 獲取子元素铛纬,獲取父元素
- 刪除子節(jié)點(diǎn)
下面 Demo 的 HTML 結(jié)構(gòu)
<body>
<div id="div1" class="container">
<p id="p1">一段文字 1</p>
<p>一段文字 2</p>
<p>一段文字 3</p>
</div>
<div id="div2">
<img src="https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png"/>
</div>
<ul id="list">
</ul>
</body>
新增插入節(jié)點(diǎn)
// 獲取 DOM 元素
const div1 = document.getElementById('div1')
const div2 = document.getElementById('div2')
// 新建節(jié)點(diǎn)
const newP = document.createElement('p')
newP.innerHTML = 'this is newP'
// 插入節(jié)點(diǎn)
div1.appendChild(newP)
// 移動(dòng)節(jié)點(diǎn)
const p1 = document.getElementById('p1')
div2.appendChild(p1)
獲取子元素厌均,獲取父元素
// 獲取父元素
const div1 = document.getElementById('div1')
const parent = div1.parentNode
// 獲取子元素列表
const div1 = document.getElementById('div1')
const child = div1.childNodes
// 但是
console.log(child) // NodeList(7) [ #text, p#p1, #text, p, #text, p, #text ]
// 打印結(jié)果顯示有 7 個(gè)元素,然而實(shí)際上 div1 下只有 3 個(gè) p 標(biāo)簽
// #text 的為文本元素
// text 元素 和 p 元素的 nodeType 不同
console.log(child[0].nodeType) // #text 顯示為 3
console.log(child[1].nodeType) // p 顯示為 1
// 把 nodeList 變成數(shù)組告唆,然后通過(guò) filter 函數(shù)過(guò)濾 p 標(biāo)簽
const div1ChildNodesP = Array.prototype.slice.call(div1.childNodes).filter(child => {
if (child.nodeType === 1) {
return true
}
return false
})
console.log('div1ChildNodesP', div1ChildNodesP) // [ p#p1, p, p ]
刪除子節(jié)點(diǎn)
const div1 = document.getElementById('div1')
const child = div1.childNodes
div1.removeChild( child[0] )
DOM 性能
- DOM操作非常 “昂貴”(耗費(fèi)性能)棺弊,應(yīng)避免頻繁的 DOM 操作
- 可以對(duì) DOM 做緩存,從而減少 DOM 操作
- 將頻繁操作改為一次性操作
DOM 查詢做緩存
// 不緩存 DOM 查詢結(jié)果
for (let i = 0; i < document.getElementsByTagName('p').length; i++) {
// 每次循環(huán)都會(huì)重新計(jì)算 length 擒悬, 頻繁進(jìn)行 DOM 查詢DK!茄螃!
}
// 緩存 DOM 查詢結(jié)果
const pList = document.getElementsByTagName('p')
const length = pList.length
for (let i = 0; i < length; i++) {
// 緩存 length 缝驳,只進(jìn)行一次 DOM 查詢 :)
}
將頻繁操作改為一次性操作
const list = document.getElementById('list')
// 創(chuàng)建一個(gè)文檔片段,此時(shí)還沒(méi)有插入到 DOM 結(jié)構(gòu)中
const frag = document.createDocumentFragment()
for (let i = 0; i < 20; i++) {
const li = document.createElement('li')
li.innerHTML = `List item ${i}`
// 先插入文檔片段中
frag.appendChild(li)
}
// 都完成之后归苍,再統(tǒng)一插入到 DOM 結(jié)構(gòu)中
list.appendChild(frag)
總結(jié)
DOM 本質(zhì)就是從 HTML 文件中解析構(gòu)件出來(lái)的樹(shù)結(jié)構(gòu)用狱。常用 API 有節(jié)點(diǎn)操作、結(jié)構(gòu)操作拼弃、attribute(修改 html 屬性) 和 property(修改對(duì)象屬性)夏伊,一般盡量使用 property。性能方面應(yīng)該盡可能減少不必要的 DOM 操作吻氧,可以做查詢緩存或合并操作溺忧。