📖 核心概念
- DOM树结构与节点操作
- 元素选择与遍历
- 事件模型:捕获、目标、冒泡
- 事件委托与性能优化
- 自定义事件
- MutationObserver与IntersectionObserver
💻 代码实现
DOM操作实战
✅ javascript
// 高效DOM操作
class DOMHelper {
static create(tag, attrs = {}, children = []) {
const el = document.createElement(tag);
Object.entries(attrs).forEach(([key, val]) => {
if (key === 'className') el.className = val;
else if (key === 'style' && typeof val === 'object') {
Object.assign(el.style, val);
}
else if (key.startsWith('on')) {
el.addEventListener(key.slice(2).toLowerCase(), val);
}
else if (key === 'dataset') {
Object.entries(val).forEach(([k, v]) => el.dataset[k] = v);
}
else el.setAttribute(key, val);
});
children.forEach(child => {
el.appendChild(typeof child === 'string'
? document.createTextNode(child) : child);
});
return el;
}
}
// 使用DocumentFragment批量操作
function renderList(items) {
const fragment = document.createDocumentFragment();
items.forEach(item => {
const li = DOMHelper.create('li', { className: 'item' }, [item]);
fragment.appendChild(li);
});
document.getElementById('list').appendChild(fragment);
}
// IntersectionObserver - 懒加载
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
img.classList.add('loaded');
observer.unobserve(img);
}
});
}, { rootMargin: '200px' });
document.querySelectorAll('img[data-src]')
.forEach(img => observer.observe(img));
// MutationObserver - 监听DOM变化
const mutObs = new MutationObserver(mutations => {
mutations.forEach(m => {
if (m.type === 'childList') {
console.log('子节点变化:', m.addedNodes, m.removedNodes);
}
});
});
mutObs.observe(document.body, { childList: true, subtree: true });
事件系统
✅ javascript
// 事件委托 - 高效处理列表点击
document.getElementById('todo-list').addEventListener('click', (e) => {
const target = e.target.closest('[data-action]');
if (!target) return;
const action = target.dataset.action;
const id = target.closest('[data-id]').dataset.id;
switch (action) {
case 'delete': deleteTodo(id); break;
case 'toggle': toggleTodo(id); break;
case 'edit': editTodo(id); break;
}
});
// 自定义事件系统
class EventBus {
#listeners = new Map();
on(event, callback, options = {}) {
if (!this.#listeners.has(event)) {
this.#listeners.set(event, new Set());
}
this.#listeners.get(event).add({ callback, ...options });
return () => this.off(event, callback); // 返回取消函数
}
off(event, callback) {
this.#listeners.get(event)?.delete(
...this.#listeners.get(event)?.filter(l => l.callback === callback) || []
);
}
emit(event, detail) {
this.#listeners.get(event)?.forEach(({ callback, once }) => {
callback(detail);
if (once) this.off(event, callback);
});
}
once(event, callback) {
return this.on(event, callback, { once: true });
}
}
// 使用
const bus = new EventBus();
bus.on('user:login', (user) => console.log('登录:', user));
bus.emit('user:login', { name: '张三' });
// 防抖与节流
function debounce(fn, ms) {
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => fn(...args), ms);
};
}
function throttle(fn, ms) {
let last = 0;
return (...args) => {
const now = Date.now();
if (now - last >= ms) {
last = now;
fn(...args);
}
};
}
// 实际应用
const searchInput = document.getElementById('search');
searchInput.addEventListener('input', debounce((e) => {
fetchResults(e.target.value);
}, 300));
window.addEventListener('scroll', throttle(() => {
updateScrollPosition();
}, 100));
🔍 DOM操作性能要点
频繁的DOM操作是前端性能问题的常见根源。核心优化原则:减少DOM访问次数、批量修改、使用DocumentFragment。在React等框架中,虚拟DOM自动处理了这些问题,但理解底层原理有助于写出更高效的代码。
🎯 练习任务
- 1 实现一个虚拟滚动列表(渲染10000条数据不卡顿)
- 2 用事件委托重构一个有100个按钮的列表的事件绑定
- 3 实现一个自定义拖拽系统(dragstart/drag/mousemove)
- 4 用IntersectionObserver实现滚动动画触发器
🏆 成就解锁
DOM操控者 — 精通DOM操作与事件处理,构建流畅的页面交互