JavaScript DOM操作
什么是DOM?
DOM(Document Object Model,文档对象模型)是JavaScript操作HTML和XML文档的编程接口。它将文档解析为一个由节点和对象组成的树结构,允许JavaScript通过这个树结构来访问、修改、添加和删除文档的内容、结构和样式。
DOM树结构
HTML文档被解析为一个树形结构,称为DOM树:
└── Document
└── html
├── head
│ ├── title
│ ├── meta
│ └── link
└── body
├── h1
├── p
└── div
├── ul
│ ├── li
│ └── li
└── imgDOM节点类型
DOM中有多种节点类型,最常见的包括:
- Document:整个文档的根节点
- Element:HTML元素节点(如
<div>,<p>等) - Text:文本节点
- Attribute:元素的属性节点
- Comment:注释节点
元素选择
1. 通过ID选择元素
使用getElementById()方法,返回匹配指定ID的第一个元素。
const element = document.getElementById('myElement');
console.log(element);2. 通过类名选择元素
使用getElementsByClassName()方法,返回匹配指定类名的所有元素的集合。
const elements = document.getElementsByClassName('myClass');
console.log(elements); // HTMLCollection
console.log(elements[0]); // 第一个匹配元素3. 通过标签名选择元素
使用getElementsByTagName()方法,返回匹配指定标签名的所有元素的集合。
const paragraphs = document.getElementsByTagName('p');
console.log(paragraphs.length); // 文档中段落的数量4. 通过CSS选择器选择元素
querySelector()
返回匹配指定CSS选择器的第一个元素。
const element = document.querySelector('.myClass');
const nestedElement = document.querySelector('#container .item');querySelectorAll()
返回匹配指定CSS选择器的所有元素的NodeList。
const elements = document.querySelectorAll('.myClass');
elements.forEach(element => {
console.log(element);
});元素操作
1. 内容操作
innerHTML
获取或设置元素的HTML内容。
const element = document.getElementById('myElement');
// 获取HTML内容
console.log(element.innerHTML);
// 设置HTML内容
element.innerHTML = '<h2>新标题</h2><p>新内容</p>';textContent
获取或设置元素的文本内容,忽略HTML标签。
const element = document.getElementById('myElement');
// 获取文本内容
console.log(element.textContent);
// 设置文本内容
element.textContent = '这是新的文本内容';innerText
获取或设置元素的可见文本内容,考虑样式和布局。
const element = document.getElementById('myElement');
console.log(element.innerText);
element.innerText = '这是可见的文本内容';2. 属性操作
获取属性
const element = document.getElementById('myElement');
const value = element.getAttribute('data-value');
console.log(value);设置属性
const element = document.getElementById('myElement');
element.setAttribute('data-value', 'new-value');
element.setAttribute('class', 'new-class');移除属性
const element = document.getElementById('myElement');
element.removeAttribute('data-value');检查属性是否存在
const element = document.getElementById('myElement');
const hasAttribute = element.hasAttribute('data-value');
console.log(hasAttribute);3. 样式操作
内联样式
const element = document.getElementById('myElement');
// 设置样式
element.style.color = 'red';
element.style.fontSize = '20px';
element.style.backgroundColor = '#f0f0f0';
// 获取样式
console.log(element.style.color); // red类名操作
const element = document.getElementById('myElement');
// 检查类名是否存在
console.log(element.classList.contains('myClass'));
// 添加类名
element.classList.add('newClass');
element.classList.add('class1', 'class2');
// 移除类名
element.classList.remove('oldClass');
element.classList.remove('class1', 'class2');
// 切换类名(存在则移除,不存在则添加)
element.classList.toggle('active');
// 替换类名
element.classList.replace('oldClass', 'newClass');创建和插入元素
1. 创建元素
// 创建元素
const newElement = document.createElement('div');
// 设置属性
newElement.id = 'newElement';
newElement.className = 'new-class';
newElement.textContent = '这是新创建的元素';2. 插入元素
appendChild()
在元素末尾添加子元素。
const parent = document.getElementById('parent');
const child = document.createElement('div');
child.textContent = '新子元素';
parent.appendChild(child);prepend()
在元素开头添加子元素。
const parent = document.getElementById('parent');
const child = document.createElement('div');
child.textContent = '新子元素(开头)';
parent.prepend(child);insertBefore()
在指定元素之前插入新元素。
const parent = document.getElementById('parent');
const referenceElement = document.getElementById('reference');
const newElement = document.createElement('div');
newElement.textContent = '插入的新元素';
parent.insertBefore(newElement, referenceElement);after() 和 before()
在元素之后或之前插入兄弟元素。
const element = document.getElementById('myElement');
const newElement = document.createElement('div');
newElement.textContent = '兄弟元素';
// 在元素之后插入
element.after(newElement);
// 在元素之前插入
element.before(newElement);删除和替换元素
1. 删除元素
const element = document.getElementById('myElement');
element.remove();
// 或者通过父元素删除
const parent = element.parentNode;
parent.removeChild(element);2. 替换元素
const oldElement = document.getElementById('oldElement');
const newElement = document.createElement('div');
newElement.textContent = '新元素';
oldElement.replaceWith(newElement);
// 或者通过父元素替换
const parent = oldElement.parentNode;
parent.replaceChild(newElement, oldElement);事件处理
1. 事件类型
常见的DOM事件类型包括:
- 鼠标事件:click, mouseover, mouseout, mousedown, mouseup, mousemove
- 键盘事件:keydown, keyup, keypress
- 表单事件:submit, input, change, focus, blur
- 窗口事件:load, resize, scroll, unload
2. 添加事件监听器
const button = document.getElementById('myButton');
// 添加点击事件监听器
button.addEventListener('click', function(event) {
console.log('按钮被点击了!');
console.log('事件对象:', event);
console.log('事件目标:', event.target);
});
// 使用箭头函数
button.addEventListener('click', (event) => {
console.log('按钮被点击了(箭头函数)!');
});3. 移除事件监听器
const button = document.getElementById('myButton');
function handleClick() {
console.log('按钮被点击了!');
}
// 添加事件监听器
button.addEventListener('click', handleClick);
// 移除事件监听器
button.removeEventListener('click', handleClick);4. 事件冒泡和捕获
事件传播有三个阶段:
- 捕获阶段:事件从document向下传播到目标元素
- 目标阶段:事件到达目标元素
- 冒泡阶段:事件从目标元素向上传播到document
const parent = document.getElementById('parent');
const child = document.getElementById('child');
// 捕获阶段触发
parent.addEventListener('click', () => {
console.log('父元素捕获');
}, true);
// 冒泡阶段触发
parent.addEventListener('click', () => {
console.log('父元素冒泡');
}, false); // 默认为false
child.addEventListener('click', (event) => {
console.log('子元素点击');
// 阻止事件冒泡
event.stopPropagation();
});5. 事件委托
利用事件冒泡,将事件监听器添加到父元素,而不是每个子元素。
const list = document.getElementById('myList');
list.addEventListener('click', (event) => {
if (event.target.tagName === 'LI') {
console.log('列表项被点击了:', event.target.textContent);
// 处理列表项点击事件
}
});DOM遍历
1. 父节点和子节点
const element = document.getElementById('myElement');
// 获取父节点
const parent = element.parentNode;
// 获取父元素节点
const parentElement = element.parentElement;
// 获取所有子节点
const childNodes = element.childNodes;
// 获取所有子元素节点
const children = element.children;
// 获取第一个子节点
const firstChild = element.firstChild;
// 获取第一个子元素节点
const firstElementChild = element.firstElementChild;
// 获取最后一个子节点
const lastChild = element.lastChild;
// 获取最后一个子元素节点
const lastElementChild = element.lastElementChild;2. 兄弟节点
const element = document.getElementById('myElement');
// 获取前一个兄弟节点
const previousSibling = element.previousSibling;
// 获取前一个兄弟元素节点
const previousElementSibling = element.previousElementSibling;
// 获取后一个兄弟节点
const nextSibling = element.nextSibling;
// 获取后一个兄弟元素节点
const nextElementSibling = element.nextElementSibling;DOM操作最佳实践
- 减少DOM操作次数:DOM操作是昂贵的,尽量减少操作次数
- 使用文档片段:对于大量DOM插入,使用DocumentFragment来减少重排和重绘
- 使用事件委托:对于多个相似元素,使用事件委托提高性能
- 避免频繁访问样式:将样式计算结果缓存
- 使用classList代替直接修改className:classList提供了更方便、更安全的CSS类操作方法
- 使用textContent代替innerHTML:当只需要操作文本时,textContent更安全、性能更好
- 及时移除事件监听器:避免内存泄漏
- 使用CSS过渡和动画:比JavaScript动画性能更好
- 使用requestAnimationFrame:对于JavaScript动画,使用requestAnimationFrame可以获得更平滑的效果
- 优化选择器:使用更高效的选择器,如ID选择器比复杂的CSS选择器更快
示例:创建一个待办事项列表
// HTML结构:<div id="todo-app"></div>
const todoApp = document.getElementById('todo-app');
// 创建应用结构
const appHTML = `
<h1>待办事项</h1>
<div class="todo-input">
<input type="text" id="todo-input" placeholder="添加新的待办事项...">
<button id="add-todo">添加</button>
</div>
<ul id="todo-list"></ul>
`;
todoApp.innerHTML = appHTML;
// 获取元素
const input = document.getElementById('todo-input');
const addButton = document.getElementById('add-todo');
const todoList = document.getElementById('todo-list');
// 添加待办事项的函数
function addTodo() {
const text = input.value.trim();
if (text === '') return;
// 创建新的待办事项
const todoItem = document.createElement('li');
todoItem.className = 'todo-item';
todoItem.innerHTML = `
<span>${text}</span>
<button class="delete-todo">删除</button>
`;
// 添加到列表
todoList.appendChild(todoItem);
// 清空输入
input.value = '';
}
// 添加点击事件监听器
addButton.addEventListener('click', addTodo);
// 添加回车事件监听器
input.addEventListener('keypress', (event) => {
if (event.key === 'Enter') {
addTodo();
}
});
// 使用事件委托处理删除事件
todoList.addEventListener('click', (event) => {
if (event.target.classList.contains('delete-todo')) {
const todoItem = event.target.closest('.todo-item');
todoItem.remove();
}
});总结
DOM操作是JavaScript在浏览器中与HTML文档交互的核心方式。通过掌握DOM选择、操作、事件处理等技术,可以创建动态、交互式的网页应用。
主要内容包括:
- DOM树结构和节点类型
- 多种元素选择方法
- 元素内容和样式的操作
- 创建、插入、删除和替换元素
- 事件处理机制,包括事件冒泡、捕获和委托
- DOM遍历方法
- DOM操作的最佳实践
在实际开发中,合理使用DOM操作技术,可以提高应用性能和用户体验。
练习
- 创建一个简单的网页,包含一个按钮和一个计数器,点击按钮计数器加1
- 实现一个图片轮播组件,包含前进、后退和自动播放功能
- 创建一个表单验证功能,实时检查输入内容的有效性
- 实现一个可拖拽的元素,使用鼠标事件
- 创建一个无限滚动列表,当滚动到页面底部时加载更多内容
- 实现一个模态框(Modal)组件,包含打开、关闭和遮罩层功能