JavaScript 操作 DOM 總結
基本概念
DOM 是 JavaScript 操作網頁的介面,全稱為“文件物件模型”(Document Object Model)。它的作用是將網頁轉為一個 JavaScript 物件,從而可以用指令碼進行各種操作(比如增刪內容)。
瀏覽器會根據 DOM 模型,將結構化文件(比如 HTML 和 XML)解析成一系列的節點,再由這些節點組成一個樹狀結構(DOM Tree)。所有的節點和最終的樹狀結構,都有規範的對外介面。
節點型別
DOM 的最小組成單位叫做節點(node)。文件的樹形結構(DOM 樹),就是由各種不同型別的節點組成。每個節點可以看作是文件樹的一片葉子。
節點的型別有七種。
Document
DocumentType
:doctype
標籤(比如<!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
型別中,我們最常用的就是 document
,element
,attribute
這幾種型別。
參考: 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)
// <span>Foo & bar</span>
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
引數的值與 insertAdjacentElement
的 position
取值相同
// 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
內部的一個子節點referenceNode
。newNode
將插在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