20道DOM相关前端面试题
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 选择器引擎,效率高)。