1. 程式人生 > >JavaScript 操作 DOM 總結

JavaScript 操作 DOM 總結

基本概念

DOM 是 JavaScript 操作網頁的介面,全稱為“文件物件模型”(Document Object Model)。它的作用是將網頁轉為一個 JavaScript 物件,從而可以用指令碼進行各種操作(比如增刪內容)。

瀏覽器會根據 DOM 模型,將結構化文件(比如 HTML 和 XML)解析成一系列的節點,再由這些節點組成一個樹狀結構(DOM Tree)。所有的節點和最終的樹狀結構,都有規範的對外介面。

節點型別

DOM 的最小組成單位叫做節點(node)。文件的樹形結構(DOM 樹),就是由各種不同型別的節點組成。每個節點可以看作是文件樹的一片葉子。

節點的型別有七種。

  • Document
    :整個文件樹的頂層節點
  • DocumentTypedoctype標籤(比如<!DOCTYPE html>
  • Element:網頁的各種HTML標籤(比如<body><a>等)
  • Attribute:網頁元素的屬性(比如class="right"
  • Text:標籤之間或標籤包含的文字
  • Comment:註釋
  • DocumentFragment:文件的片段

瀏覽器提供一個原生的節點物件Node,上面這七種節點都繼承了Node,因此具有一些共同的屬性和方法。

如何確定節點型別

Node有一個屬性nodeType表示Node的型別,不同節點的nodeType

屬性值和對應的常量如下

  • 文件節點(document):9,對應常量Node.DOCUMENT_NODE
  • 元素節點(element):1,對應常量Node.ELEMENT_NODE
  • 屬性節點(attr):2,對應常量Node.ATTRIBUTE_NODE
  • 文字節點(text):3,對應常量Node.TEXT_NODE
  • 文件片斷節點(DocumentFragment):11,對應常量Node.DOCUMENT_FRAGMENT_NODE
  • 文件型別節點(DocumentType):10,對應常量Node.DOCUMENT_TYPE_NODE
  • 註釋節點(Comment):8,對應常量Node.COMMENT_NODE

確定節點型別時,使用nodeType屬性是常用方法。

var node = document.documentElement.firstChild;
if (node.nodeType === Node.ELEMENT_NODE) {
  console.log('該節點是元素節點');
}

這些Node型別中,我們最常用的就是 documentelementattribute這幾種型別。

參考: https://wangdoc.com/javascript/dom/node.html

建立元素

createElement

document.createElement`方法用來生成元素節點,並返回該節點。

var div = document.createElement('div')

使用createElement要注意:通過createElement建立的元素並不屬於html文件,它只是創建出來,並未新增到html文件中,要呼叫appendChild或insertBefore等方法將其新增到HTML文件樹中。

createTextNode

createTextNode用來建立一個文字節點,用法如下

var newDiv = document.createElement('div');
var newContent = document.createTextNode('Hello');
newDiv.appendChild(newContent);
// <div>Hello</div>

上面程式碼新建一個div節點和一個文字節點,然後將文字節點插入div節點。

這個方法可以確保返回的節點,被瀏覽器當作文字渲染,而不是當作 HTML 程式碼渲染。因此,可以用來展示使用者的輸入,避免 XSS 攻擊。

var div = document.createElement('div');
div.appendChild(document.createTextNode('<span>Foo & bar</span>'));
console.log(div.innerHTML)
// &lt;span&gt;Foo &amp; bar&lt;/span&gt;

insertAdjacentElement

Element.insertAdjacentElement方法在相對於當前元素的指定位置,插入一個新的節點。該方法返回被插入的節點,如果插入失敗,返回null

element.insertAdjacentElement(position, element);

Element.insertAdjacentElement方法一共可以接受兩個引數,第一個引數是一個字串,表示插入的位置,第二個引數是將要插入的節點。第一個引數只可以取如下的值。

  • beforebegin:當前元素之前
  • afterbegin:當前元素內部的第一個子節點前面
  • beforeend:當前元素內部的最後一個子節點後面
  • afterend:當前元素之後
// HTML 程式碼:<body><div>some text</div></body>
var body = document.querySelector('body')
var p1 = document.createElement('p')
body.insertAdjacentElement('afterbegin', p1)
// 執行程式碼之後
// <body><p></p><div>some text</div></body>

insertAdjacentHTML, insertAdjacentText

Element.insertAdjacentHTML方法用於將一個 HTML 字串,解析生成 DOM 結構,插入相對於當前節點的指定位置。

element.insertAdjacentHTML(position, text);

該方法接受兩個引數,第一個是一個表示指定位置的字串,第二個是待解析的 HTML 字串。position引數的值與 insertAdjacentElementposition 取值相同

// HTML 程式碼:<div id="one">one</div>
var d1 = document.getElementById('one');
d1.insertAdjacentHTML('afterend', '<div id="two">two</div>');
// 執行後的 HTML 程式碼:
// <div id="one">one</div><div id="two">two</div>

該方法只是在現有的 DOM 結構裡面插入節點,這使得它的執行速度比innerHTML方法快得多。

注意,該方法不會轉義 HTML 字串,這導致它不能用來插入使用者輸入的內容,否則會有安全風險。

Element.insertAdjacentText方法在相對於當前節點的指定位置,插入一個文字節點,用法與Element.insertAdjacentHTML方法完全一致。

// HTML 程式碼:<div id="one">one</div>
var d1 = document.getElementById('one');
d1.insertAdjacentText('afterend', 'two');
// 執行後的 HTML 程式碼:
// <div id="one">one</div>two

修改元素

appendChild

appendChild()方法接受一個節點物件作為引數,將其作為最後一個子節點,插入當前節點。該方法的返回值就是插入文件的子節點。

var p = document.createElement('p');
document.body.appendChild(p);

insertBefore

insertBefore方法用於將某個節點插入父節點內部的指定位置。insertBefore方法接受兩個引數,第一個引數是所要插入的節點newNode,第二個引數是父節點parentNode內部的一個子節點referenceNodenewNode將插在referenceNode這個子節點的前面。返回值是插入的新節點newNode

var p = document.createElement('p');
document.body.insertBefore(p, document.body.firstChild);
// 上面程式碼中,新建一個<p>節點,插在document.body.firstChild的前面,也就是成為document.body的第一個子節點。

removeChild

removeChild方法接受一個子節點作為引數,用於從當前節點移除該子節點。返回值是移除的子節點。

var divA = document.getElementById('A');
divA.parentNode.removeChild(divA);

replaceChild

replaceChild方法用於將一個新的節點,替換當前節點的某一個子節點。replaceChild方法接受兩個引數,第一個引數newChild是用來替換的新節點,第二個引數oldChild是將要替換走的子節點。返回值是替換走的那個節點oldChild

var divA = document.getElementById('divA');
var newSpan = document.createElement('span');
newSpan.textContent = 'Hello World!';
divA.parentNode.replaceChild(newSpan, divA);
// 將divA 替換成 newSpan

查詢節點

獲取單個節點

  • document.getElementById

    根據元素id返回元素,返回值是Element型別,如果不存在該元素,則返回null。
    使用這個介面有幾點要注意:
    (1)元素的Id是大小寫敏感的,一定要寫對元素的id
    (2)HTML文件中可能存在多個id相同的元素,則返回第一個元素
    (3)只從文件中進行搜尋元素,如果建立了一個元素並指定id,但並沒有新增到文件中,則這個元素是不會被查詢到的

    // HTML程式碼為
    // <span id="myspan">Hello</span>
    var span = document.getElementById('myspan');
    span.id // "myspan"
    span.tagName // "SPAN"
  • document.querySelector

    Element.querySelector方法接受 CSS 選擇器作為引數,返回父元素的第一個匹配的子元素。如果沒有找到匹配的子元素,就返回null

    // 查詢元素使用 document.querySelector() 函式
    // 這個函式的引數是一個選擇器(和 CSS 選擇器一樣)
    // 選擇器語法和 CSS 選擇器一樣, 現在只用 3 個基礎選擇器
    // 元素選擇器
    var body = document.querySelector('body')
    // class 選擇器, 用的是 .類名
    var form = document.querySelector('.login-form')
    // id 選擇器, 用的是   #id
    var loginButton = document.querySelector('#id-button-login')
    // log 出來看看
    
    // 選擇多個元素使用函式 querySelectorAll
    var buttons = document.querySelectorAll('.radio-button')
    // 還可以接受任何複雜的 CSS 選擇器
    document.body.querySelector("style[type='text/css'], style:not([type])");
    
    // 查詢到的元素還可以繼續用 querySelector
    var ul = document.querySelector('.ul')
    ul.querySelector('li')
    

獲取多個節點

  • document.getElementsByTagName

  • document.getElementsByClassName

  • document.getElementsByName

  • document.querySelectorAll

獲取父節點

parentElement 和 parentNode

獲取所有的後代節點

children屬性返回一個HTMLCollection例項,成員是當前節點的所有元素子節點。

childNodes屬性返回一個類似陣列的物件(NodeList集合),成員包括當前節點的所有子節點,注意,除了元素節點,childNodes屬性的返回值還包括文字節點和註釋節點。

children 和 childNodes 最大的區別就是:children 不會把空白節點算進去。

獲取兄弟節點

  • previousSibling,nextSibling,previousElementSibling,nextElementSibling
  • 空白節點的坑,previousSibling,nextSibling會把空白節點算進去

操作 CSS

style

// 在單個語句中設定多個樣式
elt.style.cssText = "color: blue; border: 1px solid black"; 
// 或者
elt.setAttribute("style", "color:red; border: 1px solid blue;");

// 設定特定樣式,同時保持其他內聯樣式值不變
elt.style.color = "blue";

classList

var element = document.querySelector('.active')
if (element != null) {
     // 使用 classList 可以訪問一個元素的所有 class
     // remove 可以刪除一個 class
     element.classList.remove("active")
}

element.classList.add('active') // 新增 active樣式
element.classList.contains('active') //判斷是否包含 active 樣式
element.classList.toogle('active') // 如果存在 active 樣式就刪除,否則就新增

獲取元素的位置

獲取 DOM 元素相對於文件的位置,可以直接使用 offsetTop
獲取 DOM 元素相對於視口的位置,可以使用 getBoundingClientRect()
獲取 SVG 元素或行內元素的 CSS 盒子(比如用來做文字高亮時),可以使用 getClientRects();
獲取絕對定位元素、偽元素的渲染後 CSS 屬性,可以使用 getComputedStyle()

參考連結

獲取網頁的總寬高

document.body.clientWidth
document.body.clientHeight

獲取視口(瀏覽器可見區域)的寬高

document.documentElement.clientWidth
document.documentElement.clientHeight