JavaScript筆記——常見DOM知識

前言

本篇文章以介紹常見的DOM節點知識、DOM元素操做方法爲目的,其中也對一些比較容易忽略的問題進行簡要說明。才疏學淺,若有紕漏之處或建議歡迎留下評論。css

Node節點

首先,簡單看看Node節點。有三個屬性我的認爲比較須要注意,nodeType、nodeName、nodeValue。html

  1. nodeType——節點類型
    返回的是一個整數,表面節點的類型。包括元素節點(1)、文本節點(3)、註釋節點(8)等。詳見Node.nodeType
  2. nodeName——節點名稱
    返回節點名稱。
    元素節點的 nodeName 與標籤名相同
    文本節點的 nodeName 始終是 #text
    文檔節點的 nodeName 始終是 #document
    詳見Node.nodeName
  3. nodeValue——節點值
    元素節點的 nodeValue 是 null
    文本節點的 nodeValue 是文本自己
    詳見Node.nodeValue

Node節點間的關係

節點關係
這個圖是來自《Javascript高級程序設計》一書中的Node節點間的關係圖譜,比較清晰地介紹了節點之間的關係。node

  • parentNode: 父節點
  • childNodes: 全部子節點
  • firstNode: 第一個子節點
  • lastNode: 最後一個子節點
  • previousSibling: 前一個兄弟節點
  • nextSibling: 下一個兄弟節點

特別注意上述屬性獲取的並不僅是元素節點,也會包含文本節點等。因此進行操做時須要進行元素類型判斷過濾。
此外,還有一些方式能夠得到相關的元素節點。數組

  • Node.parentElement
  • ParentNode.children(IE6-8返回的元素可能會包含註釋節點)
  • ParentNode.childElemnetCount
  • ParentNode.firstElementChild
  • ParentNode.lastElementChild
  • NonDocumentTypeChildNode.previousElementSibling
  • NonDocumentTypeChildNode.nextElementSibling

DOM元素獲取方法

方法 簡述 兼容性
getElementById('id') 經過id獲取 -
getElementsByTagName('p') 經過標籤名獲取 -
getElementsByClassName('class') 經過class獲取 IE>= 9
getElementsByName('name') 經過name屬性獲取 -
querySelector() 返回匹配選擇器的第一個元素 IE >= 8
querySelectorAll() 返回匹配選擇器的全部元素 IE >=8

特別注意:querySelectorAll()與其餘方法獲取的DOM元素是不一樣的,它返回的是靜態的
NodeList 對象,其餘返回的是動態的 HTMLCollection 對象。靜態意味着不會隨着DOM結構的變換而改變。舉例以下:瀏覽器

// html
<ul id="list">
  <li>1</li>
  <li>2</li>
  <li>3</li>
  <li>4</li>
  <li>5</li>
  <li>6</li>
</ul>

//js
let list = document.getElementById('list'),
    child1 = document.getElementsByTagName('li'),
    child2 = document.querySelectorAll('li')
console.log(child1.length) // 6
console.log(child2.length) // 6
let ele = document.createElement('li')
ele.innerHTML = 7
list.appendChild(ele)
console.log(child1.length) // 7
console.log(child2.length) // 6

因此,在使用getElementsByTagName、getElementsByClassName、getElementsByName方法時要特別注意循環處理DOM節點的狀況。app

建立DOM節點

createElement() 建立一個元素節點
createTextNode() 建立一個文本節點
createAttribute() 建立一個屬性節點(用setAttribute方法更加方便)
createDocumentFragment() 建立一個文檔片斷(適合在批量操做DOM元素時使用,詳見後面章節的例子)wordpress

DOM元素內容屬性獲取

  1. 元素內容的獲取
    這裏有幾個容易混淆的屬性,innerHTML、outerHTML、innerText、outerText、textContent,都是能夠獲取元素內容。區別以下:性能

    屬性 描述 兼容性
    innerHTML 返回HTML文本,存在XSS攻擊的問題。
    outerHTML 返回內容包含元素及其後代的HTML文本。
    textContent 返回元素全部文本內容,包括隱藏元素的文本,包括<style>、<script>不會返回HTML文本,避免直接設置HTML文本。 IE9+
    innerText 返回文本內容,受CSS樣式影響,會觸發DOM重排,不包括隱藏元素的文本,不包括<style>、<script>,避免直接設置HTML文本。
    outerText 非標準屬性。獲取時返回與innerText相同內容,設置時刪除當前節點替換爲給定文本。
  2. 元素屬性
    Element.attributes(): 引用MDN官網的描述spa

    返回該元素全部屬性節點的一個實時集合。該集合是一個 NamedNodeMap 對象,不是一個數組,因此它沒有數組的方法,其包含的屬性節點的索引順序隨瀏覽器不一樣而不一樣。更確切地說,attributes 是字符串形式的名(name)/值(value)對,每一對名/值對對應一個屬性節點。設計

    ele.getAttribute(attributeName) 獲取屬性
    ele.setAttribute(name, value) 設置屬性
    HTMLElement.dataset: 獲取data-*屬性集

  3. 元素樣式
    HTMLElement.style 返回元素的內聯樣式(沒錯,樣式表的屬性會被忽略)
    單個樣式的設置:ele.style.color='#000'
    多個樣式的設置:

    • 依次設置
    • ele.style.cssText='color: blue'
    • ele.setAttribute('style', 'color: blue')

    獲取元素樣式信息:

    • window.getComputedStyle(ele).color
    • window.getComputedStyle(ele).getPropertyValue('color')
      getComputedStyle方法接受第二個參數爲僞元素,如'::after'。關於getComputedStyle詳細介紹能夠看看張鑫旭大神的獲取元素CSS值之getComputedStyle方法一文。
  4. 元素類名
    className 獲取或設置元素的類名。
    classList 只讀,返回元素的類屬性的實時 DOMTokenList集合。但可使用 add() 和 remove() 方法修改。也有相似jQuery的toggle方法,可是兼容性較差。

插入DOM元素

方法 簡述
node.appendChild(newNode) 向node節點插入一個新節點newNode
node.insertBefore(newNode, tarNode) 在node節點的tarNode子節點前插入一個新節點newNode
node.replaceChild(newNode, tarNode) 替換node節點的tarNode子節點爲新節點newNode
node.removeChild(tarNode) 移除node節點的tarNode子節點
node.cloneNode(flag) 複製節點,flag: true 深複製;flag: false 淺複製

此處深複製爲複製節點及其整個子樹,淺複製則僅複製節點自己。

DOM操做的性能問題

頻繁進行DOM操做其實會形成屢次重排Reflow,影響性能。舉個常見的例子,在id爲container的元素中添加5個按鈕,每一個按鈕的文案是相應序號,點擊打印輸出對應序號。解決辦法有如下幾種:

  1. 依次建立button元素,使用appenChild添加到列表中
    固然,這個方法是最不推薦的,由於屢次對DOM進行操做,會形成屢次頁面重排,性能太差。

    let container = document.getElementById('container')
    for(let i = 1; i <= 5; i++) {
        let btn = document.createElement('button'),
            text = document.createTextNode(i)
        btn.appendChild(text)
        btn.addEventListener('click', () => {
            console.log(i)
        })
        container.appendChild(btn)
    }
  2. 利用DocumentFragment
    引用MDN官網關於DocumentFragment的介紹:

    DocumentFragment 接口表示一個沒有父級文件的最小文檔對象。它被當作一個輕量版本的 Document 使用,用於存儲已排好版的或還沒有打理好格式的XML片斷。最大的區別是由於DocumentFragment不是真實DOM樹的其中一部分,它的變化不會引發DOM樹的從新渲染的操做(reflow) ,或者致使性能影響的問題出現。

    沒錯,利用DocumentFragment咱們可以避免方法1中屢次操做DOM的問題,性能獲得提高。

    let container = document.getElementById('container'),
        fragment = document.createDocumentFragment()
    for(let i = 1; i <= 5; i++) {
        let btn = document.createElement('button'),
            text = document.createTextNode(i)
        btn.appendChild(text)
        btn.addEventListener('click', () => {
            console.log(i)
        })
        fragment.appendChild(btn)
    }
    container.appendChild(fragment)
  3. 利用字符串拼接
    使用字符串拼接的方法插入DOM元素是效率最高的。而且,這裏將事件綁定到了父元素上,一方面可使用動態添加元素的事件,另外一方面當須要在大量元素上綁定事件時,這種方法更加優雅而且節省內存。

    let container = document.getElementById('container'),
        btns = '';
    for(let i = 1; i <= 5; i++) {
        let btn = `<button class="btn_num">${i}</button>`
        btns += btn
    }
    container.addEventListener('click', (event) => {
        let target = event.target
        if(target.className == 'btn_num') {
            console.log(target.innerHTML)
        }
    })
    container.innerHTML = btns
相關文章
相關標籤/搜索