Skip to content

DOM 选择器

DOM(文档对象模型)是 JavaScript 操作网页的接口。选择器是操作 DOM 的第一步。

🎯 什么是 DOM#

DOM 将 HTML 文档表示为树形结构,每个节点都是对象,JavaScript 可以对其进行操作。

<!DOCTYPE html>
<html>
<head>
<title>页面标题</title>
</head>
<body>
<div id="app">
<h1 class="title">标题</h1>
<p>段落</p>
</div>
</body>
</html>
// DOM 树结构
// document
// └── html
// ├── head
// │ └── title
// └── body
// └── div#app
// ├── h1.title
// └── p

基础选择器#

getElementById#

通过 ID 获取单个元素(最快的选择器):

// <div id="app">内容</div>
const app = document.getElementById('app')
console.log(app) // <div id="app">内容</div>
// 不存在时返回 null
const notExist = document.getElementById('not-exist')
console.log(notExist) // null
// ID 应该唯一,如果有重复只返回第一个

getElementsByClassName#

通过类名获取元素集合(HTMLCollection):

// <div class="item">1</div>
// <div class="item">2</div>
// <div class="item active">3</div>
const items = document.getElementsByClassName('item')
console.log(items.length) // 3
console.log(items[0]) // <div class="item">1</div>
// 多个类名(空格分隔,且关系)
const activeItems = document.getElementsByClassName('item active')
console.log(activeItems.length) // 1
// HTMLCollection 是实时的
const newItem = document.createElement('div')
newItem.className = 'item'
document.body.appendChild(newItem)
console.log(items.length) // 4(自动更新)

getElementsByTagName#

通过标签名获取元素集合:

// 获取所有 div
const divs = document.getElementsByTagName('div')
// 获取所有元素
const all = document.getElementsByTagName('*')
// 可以在元素上调用
const container = document.getElementById('container')
const innerDivs = container.getElementsByTagName('div')

getElementsByName#

通过 name ��性获取元素(常用于表单):

// <input type="radio" name="gender" value="male">
// <input type="radio" name="gender" value="female">
const radios = document.getElementsByName('gender')
console.log(radios.length) // 2
// 获取选中的值
for (const radio of radios) {
if (radio.checked) {
console.log(radio.value)
}
}

现代选择器#

querySelector#

使用 CSS 选择器获取第一个匹配元素:

// 通过 ID
const app = document.querySelector('#app')
// 通过类名
const firstItem = document.querySelector('.item')
// 通过标签
const firstDiv = document.querySelector('div')
// 组合选择器
const activeItem = document.querySelector('.item.active')
const firstChild = document.querySelector('#list > li:first-child')
const link = document.querySelector('a[href^="https"]')
// 复杂选择器
const element = document.querySelector(
'div.container > ul.list li:nth-child(2) a'
)
// 不存在返回 null
const notExist = document.querySelector('.not-exist')
console.log(notExist) // null

querySelectorAll#

获取所有匹配的元素(NodeList):

// 获取所有匹配元素
const items = document.querySelectorAll('.item')
console.log(items.length)
// NodeList 支持 forEach
items.forEach((item) => {
console.log(item.textContent)
})
// 🔶 注意:querySelectorAll 返回的是静态 NodeList
const staticList = document.querySelectorAll('.item')
document.body.appendChild(createItem())
console.log(staticList.length) // 不变
// 对比:getElementsByClassName 返回实时 HTMLCollection
const liveList = document.getElementsByClassName('item')
document.body.appendChild(createItem())
console.log(liveList.length) // +1

选择器性能#

// 性能从高到低:
// 1. getElementById - 最快
document.getElementById('app')
// 2. getElementsByClassName - 很快
document.getElementsByClassName('item')
// 3. getElementsByTagName - 很快
document.getElementsByTagName('div')
// 4. querySelector - 较快
document.querySelector('#app')
// 5. querySelectorAll - 较慢(需要遍历)
document.querySelectorAll('.item')
// 实践建议:
// - ID 选择用 getElementById
// - 简单类名用 getElementsByClassName
// - 复杂选择器用 querySelector/querySelectorAll

遍历 DOM#

父子关系#

// <div id="parent">
// 文本节点
// <span id="child">子元素</span>
// <!-- 注释 -->
// </div>
const parent = document.getElementById('parent')
const child = document.getElementById('child')
// 子节点(包含所有类型)
console.log(parent.childNodes) // NodeList(5):文本、span、文本、注释、文本
// 子元素(只有元素���点)
console.log(parent.children) // HTMLCollection(1):span
// 第一个/最后一个子节点
console.log(parent.firstChild) // 文本节点
console.log(parent.lastChild) // 文本节点
// 第一个/最后一个子元素
console.log(parent.firstElementChild) // <span>
console.log(parent.lastElementChild) // <span>
// 父节点
console.log(child.parentNode) // <div id="parent">
console.log(child.parentElement) // <div id="parent">
// 子元素数量
console.log(parent.childElementCount) // 1

兄弟关系#

// <ul>
// <li id="first">1</li>
// <li id="second">2</li>
// <li id="third">3</li>
// </ul>
const second = document.getElementById('second')
// 下一个/上一个兄弟节点
console.log(second.nextSibling) // 可能是文本节点
console.log(second.previousSibling) // 可能是文本节点
// 下一个/上一个兄弟元素
console.log(second.nextElementSibling) // <li id="third">
console.log(second.previousElementSibling) // <li id="first">

closest#

向上查找匹配的祖先元素:

// <div class="container">
// <div class="wrapper">
// <button id="btn">点击</button>
// </div>
// </div>
const btn = document.getElementById('btn')
// 查找最近的匹配祖先
console.log(btn.closest('.wrapper')) // <div class="wrapper">
console.log(btn.closest('.container')) // <div class="container">
console.log(btn.closest('div')) // <div class="wrapper">
console.log(btn.closest('.not-exist')) // null
// 实际应用:事件委托中找到目标元素
document.addEventListener('click', (e) => {
const item = e.target.closest('.list-item')
if (item) {
console.log('点击了列表项:', item)
}
})

contains#

检查是否包含某个节点:

const parent = document.getElementById('parent')
const child = document.getElementById('child')
const other = document.getElementById('other')
console.log(parent.contains(child)) // true
console.log(parent.contains(parent)) // true(包含自身)
console.log(parent.contains(other)) // false

matches 和 相关方法#

matches#

检查元素是否匹配选择器:

const element = document.querySelector('.item.active')
console.log(element.matches('.item')) // true
console.log(element.matches('.active')) // true
console.log(element.matches('.item.active')) // true
console.log(element.matches('#app .item')) // 取决于 DOM 结构
console.log(element.matches('.other')) // false
// 实际应用
function handleClick(e) {
if (e.target.matches('button.submit')) {
// 处理提交按钮点击
}
if (e.target.matches('a[href^="http"]')) {
// 处理外部链接点击
}
}

特殊选择器#

document 属性#

// 文档元素
console.log(document.documentElement) // <html>
// body 和 head
console.log(document.body) // <body>
console.log(document.head) // <head>
// 所有表单
console.log(document.forms) // HTMLCollection
// 所有链接
console.log(document.links) // HTMLCollection
// 所有图片
console.log(document.images) // HTMLCollection
// 当前活动元素
console.log(document.activeElement) // 当前聚焦的元素

表单元素#

// <form id="myForm">
// <input name="username">
// <input name="password">
// </form>
const form = document.getElementById('myForm')
// 通过 name 访问表单元素
console.log(form.username) // <input name="username">
console.log(form.password) // <input name="password">
// 所有表单元素
console.log(form.elements) // HTMLFormControlsCollection

实用技巧#

转换为数组#

Array.from
// HTMLCollection 和 NodeList 不是真正的数组
const items = document.getElementsByClassName('item')
const arr1 = Array.from(items)
// 方法2:展开运算符
const arr2 = [...items]
// 方法3:Array.prototype.slice
const arr3 = Array.prototype.slice.call(items)
// 现在可以使用数组方法
arr1.filter((item) => item.classList.contains('active'))
arr1.map((item) => item.textContent)

链式选择#

// 在特定元素内查找
const container = document.getElementById('container')
const items = container.querySelectorAll('.item')
const firstLink = container.querySelector('a')
// 组合使用
const activeItem = document.getElementById('list').querySelector('.item.active')

检查元素存在#

// 使用可选链
const text = document.querySelector('.item')?.textContent ?? '默认'
// 使用条件判断
const element = document.querySelector('.item')
if (element) {
element.style.color = 'red'
}
// 使用 try-catch(不推荐)
try {
document.querySelector('.item').style.color = 'red'
} catch (e) {
console.log('元素不存在')
}

等待 DOM 加载#

// 方法1:DOMContentLoaded
document.addEventListener('DOMContentLoaded', () => {
const app = document.getElementById('app')
// DOM 已加载完成
})
// 方法2:将脚本放在 body 末尾
// 方法3:defer 属性
// <script defer src="app.js"></script>
// 方法4:检查 readyState
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init)
} else {
init()
}

常见模式#

获取所有指定元素#

// 获取所有复选框的值
function getCheckedValues(name) {
return [...document.querySelectorAll(`input[name="${name}"]:checked`)].map(
(input) => input.value
)
}
// 使用
const selectedOptions = getCheckedValues('options')

查找特定条件的元素#

// 查找包含特定文本的元素
function findByText(selector, text) {
return [...document.querySelectorAll(selector)].find((el) =>
el.textContent.includes(text)
)
}
const item = findByText('.menu-item', '设置')

安全的选择器函数#

function $(selector, context = document) {
return context.querySelector(selector)
}
function $$(selector, context = document) {
return [...context.querySelectorAll(selector)]
}
// 使用
const app = $('#app')
const items = $$('.item')
const innerItems = $$('.inner-item', app)

总结#

方法返回值实时性速度
getElementByIdElement/null-最快
getElementsByClassNameHTMLCollection实时
getElementsByTagNameHTMLCollection实时
querySelectorElement/null-较快
querySelectorAllNodeList静态较慢
遍历属性包含文本/注释
childNodes
children
firstChild
firstElementChild
nextSibling
nextElementSibling

核心要点