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>
// 不存在时返回 nullconst 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) // 3console.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#
通过标签名获取元素集合:
// 获取所有 divconst 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 选择器获取第一个匹配元素:
// 通过 IDconst 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')
// 不存在返回 nullconst notExist = document.querySelector('.not-exist')console.log(notExist) // nullquerySelectorAll#
获取所有匹配的元素(NodeList):
// 获取所有匹配元素const items = document.querySelectorAll('.item')console.log(items.length)
// NodeList 支持 forEachitems.forEach((item) => { console.log(item.textContent)})
// 🔶 注意:querySelectorAll 返回的是静态 NodeListconst staticList = document.querySelectorAll('.item')document.body.appendChild(createItem())console.log(staticList.length) // 不变
// 对比:getElementsByClassName 返回实时 HTMLCollectionconst 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)) // trueconsole.log(parent.contains(parent)) // true(包含自身)console.log(parent.contains(other)) // falsematches 和 相关方法#
matches#
检查元素是否匹配选择器:
const element = document.querySelector('.item.active')
console.log(element.matches('.item')) // trueconsole.log(element.matches('.active')) // trueconsole.log(element.matches('.item.active')) // trueconsole.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 和 headconsole.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实用技巧#
转换为数组#
// HTMLCollection 和 NodeList 不是真正的数组const items = document.getElementsByClassName('item')
const arr1 = Array.from(items)
// 方法2:展开运算符const arr2 = [...items]
// 方法3:Array.prototype.sliceconst 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:DOMContentLoadeddocument.addEventListener('DOMContentLoaded', () => { const app = document.getElementById('app') // DOM 已加载完成})
// 方法2:将脚本放在 body 末尾
// 方法3:defer 属性// <script defer src="app.js"></script>
// 方法4:检查 readyStateif (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)总结#
| 方法 | 返回值 | 实时性 | 速度 |
|---|---|---|---|
| getElementById | Element/null | - | 最快 |
| getElementsByClassName | HTMLCollection | 实时 | 快 |
| getElementsByTagName | HTMLCollection | 实时 | 快 |
| querySelector | Element/null | - | 较快 |
| querySelectorAll | NodeList | 静态 | 较慢 |
| 遍历属性 | 包含文本/注释 |
|---|---|
| childNodes | 是 |
| children | 否 |
| firstChild | 是 |
| firstElementChild | 否 |
| nextSibling | 是 |
| nextElementSibling | 否 |
核心要点:
- ID 选择用
getElementById,最快 - 复杂选择用
querySelector/querySelectorAll getElementsBy*返回实时集合querySelectorAll返回静态集合- 使用
closest向上查找祖先元素