目錄
1.從JS基礎到JS-Web-API
JS基礎知識,規定語法(ECMA262標準)
JS Web API,網頁操作的API(W3C標準)
前者是後者的基礎,兩者結合才能真正實際應用
JS基礎知識有哪些?
變量的類型和計算、原型和原型鏈、作用域和閉包(還有異步,異步是借用JS Web API去實現的)
JS Web API有哪些?
DOM、BOM、事件綁定、ajax、存儲
2.DOM的本質
DOM的本質就是從HTML解析出來的一棵樹,是樹形的數據結構。如下
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div><p>this is p</p></div>
</body>
</html>
3.DOM節點操作
獲取DOM節點
dom.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>dom 演示</title>
<style>
.container {
border: 1px solid #ccc;
}
.red {
color: red;
}
</style>
</head>
<body>
<div id="div1" class="container">
<p id="p1">一段文字 1</p>
<p>一段文字 2</p>
<p>一段文字 3</p>
</div>
<div id="div2">
<img src="https://img3.mukewang.com/5a9fc8070001a82402060220-100-100.jpg"/>
</div>
<ul id="list">
</ul>
<script src="./dom-1.js"></script>
</body>
</html>
dom-1.js
const div1 = document.getElementById('div1')
console.log('div1\n', div1)
const divList = document.getElementsByTagName('div') // 集合
console.log('divList.length', divList.length)
console.log('divList[1]', divList[1])
運行結果
dom-1.js
const pList = document.querySelectorAll('p')
console.log('pList', pList)
控制檯結果
0號元素的p#p1是p元素有id="p1"
這些都比較基本,菜鳥教程和官方文檔一大堆
用JS操作DOM節點的property、attribute
dom-1.js
const pList = document.querySelectorAll('p')
const p1 = pList[0]
// property 形式
p1.style.width = '100px'
console.log( p1.style.width )
p1.className = 'red'
console.log( p1.className )
console.log(p1.nodeName)
console.log(p1.nodeType) // 1
運行結果
dom-1.js
// attribute形式
p1.setAttribute('data-name', 'imooc')
// p1.data-name="imooc"; // 這是錯誤的,不能和操作property形式替換使用
console.log( p1.getAttribute('data-name') )
// property形式
p1.style.fontSize="50px"; // 這是正確的,可以和操作property形式替換使用
// p1.setAttribute('style', 'font-size: 50px;')
console.log( p1.getAttribute('style') )
運行結果
所以最後的比較結論就是
property和attribute形式都可以修改節點的屬性,但是對於新增或刪除的自定義屬性,能在html的dom樹結構上體現出來的,就必須要用到attribute形式了。
這兩種方式我個人都不喜歡。
// attribute
p1.setAttribute('data-name', 'imooc')
// p1.data-name="imooc"; // 這是錯誤的,不能和操作property形式替換使用
console.log( p1.getAttribute('data-name') )
// p1.removeAttribute('mytest') // 必須attribute形式刪除
// property
p1.removeAttribute('font-size') // property和attribute形式刪除都可以,這裏刪除後getAttribute爲null,沒有font-size屬性了
// p1.style.fontSize=""; // 和上面一句效果一樣
// p1.style.fontSize="50px"; // 這是正確的,可以和操作property形式替換使用,新增font-size並設置50px
// p1.setAttribute('style', 'font-size: 50px;')
console.log( p1.getAttribute('style') )
如果使用property這種方式要給HTMLElement添加多個行內樣式時,需要顯式的書寫多次
這種方式是一種低效而又冗餘,甚至是難於維護的方式。事實上如果需要通過使用JavaScript的API給HTMLElement同時添加多個樣式,除了給元素添加一個類名(後面會介紹)之外,還可以使用.style.cssText = ''這種方式或者使用.setAttribute('style', '')方式:
// 使用
bodyEle.style.cssText = 'background-color: red; color: green; font-size: 1rem'
// 或使用
bodyEle.setAttribute('style', 'font-size: 1rem;color: green; background-color: yellow')
請注意:不管使用上面哪種方式,都將完全重置HTMLElement元素的內聯樣式,因此需要在參數中包含所有需要的樣式(甚至是以前未更改的樣式)。
上面是老師的講解,我個人習慣cssText的方式,爲什麼呢?爲了減少迴流次數,你要是設置一次背景色,重繪一次,再設置font-Size又要回流一次...屬性多了就很影響性能。
隨着瀏覽器的發展,我們可以使用JavaScript的一些新的API來達到同樣的效果,比如Object.assign()給HTMLelement.style一次性添加多個行內樣式
Object.assign(bodyEle.style, {
backgroundColor: '#f36',
margin: '20px',
border: '1rem solid green'
})
4.DOM結構操作
將dom.html引入的dom-1.js改爲dom-2.js
新增節點
dom-2.js
const div1 = document.getElementById('div1')
const div2 = document.getElementById('div2')
// 新建節點
const newP = document.createElement('p')
newP.innerHTML = 'this is newP'
// 插入節點
div1.appendChild(newP)
運行結果
移動節點
dom-2.js
const div1 = document.getElementById('div1')
const div2 = document.getElementById('div2')
// 新建節點
const newP = document.createElement('p')
newP.innerHTML = 'this is newP'
// 插入節點
div1.appendChild(newP)
// 移動節點
const p1 = document.getElementById('p1')
div2.appendChild(p1)
// 獲取父元素
console.log( p1.parentNode )
運行結果
直接獲取節點再添加就是移動
獲取子元素列表
dom-2.js
const div1 = document.getElementById('div1')
const div2 = document.getElementById('div2')
// 新建節點
const newP = document.createElement('p')
newP.innerHTML = 'this is newP'
// 插入節點
div1.appendChild(newP)
// 移動節點
const p1 = document.getElementById('p1')
div2.appendChild(p1)
// 獲取父元素
console.log( p1.parentNode )
// 獲取子元素列表
const div1ChildNodes = div1.childNodes
console.log( div1.childNodes )
const div1ChildNodesP = Array.prototype.slice.call(div1.childNodes).filter(child => {
if (child.nodeType === 1) {
return true
}
return false
})
console.log('div1ChildNodesP', div1ChildNodesP)
運行結果
過濾filter之後只剩下p元素,filter()
方法創建一個新數組, 其包含通過所提供函數實現的測試的所有元素。
刪除元素
刪除上面過濾後的一個數組元素
div1.removeChild( div1ChildNodesP[0] )
結果
5.DOM性能
DOM操作非常“昂貴”,我們需要避免頻繁的DOM操作
對DOM查詢做緩存操作
將頻繁操作改爲一次性操作
在console中敲一下
在執行最後list.apendChild(frag)之前,frag片段還在內存中,沒有渲染,執行了之後就渲染在頁面上
綜上所述,DOM性能就是要記得做緩存和合並的處理,合併就是不要添加一次渲染一次。一次性添加到fragment再添加在DOM節點,這樣只會渲染一次。
關注、留言,我們一起學習。
===============Talk is cheap, show me the code================