DOM 相關面試題及答案
-
什么是 DOM?DOM 樹的結構是怎樣的?
DOM(文檔對象模型,Document Object Model)是 HTML/XML 文檔的編程接口,將文檔結構化為樹形節點集合,允許程序動態訪問和修改文檔內容、結構和樣式。
DOM 樹結構:以
document
為根節點,包含html
、head
、body
等元素節點,元素內的文本為文本節點,屬性為屬性節點,節點間存在父子、兄弟關系(如body
是html
的子節點,div
和p
可能是兄弟節點)。 -
DOM 節點有哪些類型?如何區分它們?
常見 DOM 節點類型:
① 元素節點(Node.ELEMENT_NODE
,值為 1,如<div>
);
② 文本節點(Node.TEXT_NODE
,值為 3,如標簽內的文本);
③ 屬性節點(Node.ATTRIBUTE_NODE
,值為 2,元素的屬性);
④ 注釋節點(Node.COMMENT_NODE
,值為 8,<!-- 注釋 -->
);
⑤ 文檔節點(Node.DOCUMENT_NODE
,值為 9,即document
)。區分方式:通過節點的
nodeType
屬性判斷(如element.nodeType === 1
表示元素節點)。 -
如何獲取 DOM 元素?請列舉至少 5 種方法。
-
document.getElementById('id')
:通過 ID 獲取唯一元素(效率高)。 -
document.getElementsByClassName('class')
:通過類名獲取元素集合(HTMLCollection,動態更新)。 -
document.getElementsByTagName('tag')
:通過標簽名獲取元素集合(HTMLCollection)。 -
document.querySelector(selector)
:通過 CSS 選擇器獲取第一個匹配元素。 -
document.querySelectorAll(selector)
:通過 CSS 選擇器獲取所有匹配元素(NodeList,靜態集合)。 -
document.getElementsByName('name')
:通過name
屬性獲取元素集合(如表單元素)。 -
元素節點方法:
parentNode.children
(獲取子元素)、element.querySelector()
(獲取子元素)。
- 什么是 DOM 的節點屬性
nodeValue
和textContent
?它們有什么區別?
-
nodeValue
:返回或設置節點的值,僅對文本節點、注釋節點有效(元素節點的nodeValue
為null
)。 -
textContent
:返回或設置元素及其所有后代的文本內容(忽略 HTML 標簽,僅保留文本)。區別:
① 適用節點:nodeValue
針對文本 / 注釋節點,textContent
針對元素節點;
② 范圍:textContent
包含所有后代文本,nodeValue
僅當前節點文本。示例:
<div>hello <span>world</span></div>
中,div.textContent
為"hello world"
,div.firstChild.nodeValue
為"hello "
。
- 如何創建、添加和刪除 DOM 節點?
-
創建節點:
-
①
document.createElement(tagName)
:創建元素節點(如document.createElement('div')
); -
②
document.createTextNode(text)
:創建文本節點; -
③
document.createComment(comment)
:創建注釋節點。 -
添加節點:
-
①
parentNode.appendChild(child)
:添加到父節點末尾; -
②
parentNode.insertBefore(newNode, referenceNode)
:插入到參考節點之前; -
③
element.replaceChild(newNode, oldNode)
:替換子節點。 -
刪除節點:
parentNode.removeChild(child)
:從父節點中刪除子節點(需先獲取父節點);或child.remove()
(直接刪除自身,IE 不支持)。
-
什么是 DOM 的事件流?它包含哪些階段?
DOM 事件流指事件從產生到處理的完整過程,分為三個階段:
① 事件捕獲階段:事件從window
向下傳播到目標元素;
② 目標階段:事件到達目標元素;
③ 事件冒泡階段:事件從目標元素向上傳播到window
。示例:點擊
div
內部的span
,捕獲階段為window → document → html → body → div
,目標階段為span
,冒泡階段為span → div → body → html → document → window
。 -
如何阻止事件冒泡和事件默認行為?
-
阻止事件冒泡:
-
① 標準瀏覽器:
event.stopPropagation()
; -
② IE8 及以下:
event.cancelBubble = true
。 -
阻止默認行為(如鏈接跳轉、表單提交):
-
① 標準瀏覽器:
event.preventDefault()
; -
② IE8 及以下:
event.returnValue = false
; -
③ 函數中返回
false
(僅在onxxx
屬性中有效,如<a href="#" onclick="return false">
)。注意:
event.stopImmediatePropagation()
可同時阻止冒泡和當前元素后續事件監聽器執行。
-
什么是事件委托?它的原理和優勢是什么?
事件委托指將子元素的事件監聽器綁定到父元素,利用事件冒泡觸發父元素的監聽器,再通過
event.target
判斷具體子元素。原理:事件冒泡機制(子元素事件會向上傳播到父元素)。
優勢:
① 減少事件監聽器數量(尤其列表等動態元素),優化性能;
② 自動支持動態添加的子元素(無需重新綁定事件)。示例:
ul.addEventListener('click', (e) => {if (e.target.tagName === 'LI') { // 判斷點擊的是liconsole.log(e.target.textContent);}});
event.target
和event.currentTarget
的區別是什么?
-
event.target
:觸發事件的具體元素(事件源,如點擊列表中的某個li
,target
是該li
)。 -
event.currentTarget
:綁定事件監聽器的元素(如事件綁定在ul
上,currentTarget
是ul
)。區別:
target
是實際觸發事件的元素,currentTarget
是事件綁定的元素(this
在監聽器中等于currentTarget
)。
- 如何獲取元素的樣式?
element.style
和getComputedStyle
有什么區別?
-
element.style
:獲取或設置元素的內聯樣式(style
屬性中的樣式),僅能獲取內聯樣式,返回值帶單位(如"10px"
)。 -
getComputedStyle(element)
:獲取元素的計算樣式(包含內聯、內部、外部樣式,最終渲染的樣式),返回CSSStyleDeclaration
對象,IE8 及以下用element.currentStyle
。區別:
① 范圍:style
僅內聯,getComputedStyle
包含所有樣式;
② 可寫性:style
可讀寫,getComputedStyle
只讀;
③ 偽元素:getComputedStyle
可獲取偽元素樣式(如getComputedStyle(div, '::before')
)。
- 什么是 DOM 重繪(Repaint)和回流(Reflow)?如何減少它們的發生?
-
重繪:元素樣式改變但不影響布局(如
color
、background
),瀏覽器重新繪制元素,開銷較小。 -
回流(重排):元素布局改變(如
width
、position
、添加子元素),瀏覽器重新計算布局,開銷較大(可能觸發多個元素的重繪)。減少方法:
① 合并樣式修改(如用class
替換多個style
屬性);
② 操作脫離文檔流的元素(如display: none
后修改,再顯示);
③ 使用documentFragment
批量添加節點;
④ 避免頻繁讀取offsetWidth
等布局屬性(緩存結果);
⑤ 用transform
和opacity
實現動畫(僅觸發合成,無回流)。
- 如何獲取元素的偏移量、客戶區大小和滾動偏移量?
-
偏移量(相對于 offsetParent):
-
①
element.offsetTop
:上邊緣距離; -
②
element.offsetLeft
:左邊緣距離; -
③
element.offsetWidth
:寬度(含邊框、內邊距); -
④
element.offsetHeight
:高度(含邊框、內邊距)。 -
客戶區大小(元素可視區域):
-
①
element.clientWidth
:寬度(含內邊距,不含邊框、滾動條); -
②
element.clientHeight
:高度(同上); -
③
element.clientTop
:上邊框寬度; -
④
element.clientLeft
:左邊框寬度。 -
滾動偏移量:
-
①
element.scrollTop
:元素內容向上滾動的距離; -
②
element.scrollLeft
:元素內容向左滾動的距離; -
③
element.scrollWidth
:內容總寬度(含不可見部分); -
④
element.scrollHeight
:內容總高度(含不可見部分)。
-
documentFragment
的作用是什么?它有什么優勢?documentFragment
是輕量級文檔對象,用于臨時存儲 DOM 節點,不屬于 DOM 樹。作用:批量操作 DOM 時,先將節點添加到
documentFragment
,再一次性插入 DOM 樹,減少回流次數。優勢:
① 減少回流(多次添加節點變為一次);
② 不影響頁面渲染(未插入 DOM 樹時不可見)。示例:
const fragment = document.createDocumentFragment();for (let i = 0; i < 100; i++) {const li = document.createElement('li');fragment.appendChild(li);}ul.appendChild(fragment); // 僅觸發一次回流
- 如何判斷一個元素是否包含另一個元素?
-
element.contains(otherElement)
:返回布爾值,判斷otherElement
是否為element
的后代(包括自身)。 -
比較
node.parentNode
:遞歸向上查找父節點,判斷是否等于目標元素。示例:
document.body.contains(div)
→ 判斷div
是否在body
內。
-
什么是 DOM 遍歷?常用的 DOM 遍歷方法有哪些?
DOM 遍歷指按一定順序訪問 DOM 樹中的節點。
常用方法:
①parentNode
:獲取父節點;
②childNodes
:獲取所有子節點(NodeList,含文本、注釋節點);
③children
:獲取所有子元素節點(HTMLCollection);
④firstChild
/lastChild
:第一個 / 最后一個子節點;
⑤firstElementChild
/lastElementChild
:第一個 / 最后一個子元素;
⑥nextSibling
/previousSibling
:下一個 / 上一個兄弟節點;
⑦nextElementSibling
/previousElementSibling
:下一個 / 上一個兄弟元素。 -
如何克隆 DOM 節點?
cloneNode
的參數有什么作用?使用
element.cloneNode(deep)
克隆節點,參數deep
為布爾值:
①deep = true
:深度克隆,復制節點及其所有后代;
②deep = false
:淺克隆,僅復制節點本身,不包含后代。注意:
① 克隆節點不包含事件監聽器(除非用addEventListener
綁定且瀏覽器支持);
② 克隆的元素沒有父節點,需手動添加到 DOM 樹;
③ ID 屬性會被復制,可能導致文檔中 ID 重復(需手動修改)。 -
什么是 DOMParser 和 XMLSerializer?它們的作用是什么?
作用:在不操作頁面 DOM 的情況下,處理字符串形式的 HTML/XML(如動態生成 DOM 結構)。
-
DOMParser
:將 XML 或 HTML 字符串解析為 DOM 文檔。示例:
const parser = new DOMParser();const doc = parser.parseFromString('\<div>hello\</div>', 'text/html');const div = doc.body.firstChild;
-
XMLSerializer
:將 DOM 節點序列化為 XML 或 HTML 字符串。示例:
const serializer = new XMLSerializer();const htmlString = serializer.serializeToString(div); // '\<div>hello\</div>'
- 如何檢測 DOM 節點的可見性?
-
方法 1:檢查
offsetParent
是否為null
(隱藏元素的offsetParent
通常為null
,但position: fixed
元素例外)。 -
方法 2:通過計算樣式判斷:
function isVisible(element) {const style = getComputedStyle(element);return style.display !== 'none' && style.visibility !== 'hidden' && style.opacity !== '0';}
- 方法 3:使用
IntersectionObserver
監測元素是否進入視口(可見于頁面)。
-
什么是 DOM Level 0、Level 1、Level 2、Level 3?它們的主要區別是什么?
DOM 標準分為多個級別,逐步擴展功能:
-
Level 0:非官方標準,指早期瀏覽器支持的基本 DOM 操作(如
element.innerHTML
、onclick
事件)。 -
Level 1:1998 年發布,分為 Core(核心,處理 XML)和 HTML(擴展 HTML),定義基本節點操作(如
getElementById
)。 -
Level 2:2000 年發布,新增事件模型(捕獲 / 冒泡、
addEventListener
)、樣式操作(getComputedStyle
)、遍歷 API 等。 -
Level 3:2004 年發布,新增 XPath 支持、事件類型擴展(如鍵盤事件)、加載 / 保存模塊等。
現代瀏覽器主要支持 Level 2 和 Level 3 的核心功能。
- 如何優化 DOM 操作的性能?
-
減少回流重繪:
-
① 批量修改樣式(用
class
或脫離文檔流操作); -
② 緩存布局屬性(如
const width = element.offsetWidth
); -
③ 使用
transform
/opacity
實現動畫。 -
減少 DOM 查詢:
-
① 緩存查詢結果(
const div = document.querySelector('div')
); -
② 避免在循環中查詢 DOM。
-
批量添加節點:
-
① 使用
documentFragment
; -
② 先
element.innerHTML
拼接字符串,再一次性渲染。 -
避免深層嵌套:簡化 DOM 結構,減少遍歷層級。
-
使用高效選擇器:優先用
getElementById
、querySelector
(基于 CSS 選擇器引擎,效率高)。