七、DOM 操作
7.1 选择器
// 单个元素
document.getElementById("app"); // 通过 ID
document.querySelector("#app .container"); // CSS 选择器
// 多个元素
document.getElementsByClassName("item"); // 返回 HTMLCollection
document.getElementsByTagName("div"); // 返回 HTMLCollection
document.querySelectorAll(".item"); // 返回 NodeList
// 特殊元素
document.documentElement; // html 元素
document.head; // head 元素
document.body; // body 元素
7.2 元素操作
// 创建元素
const div = document.createElement("div");
const text = document.createTextNode("文本内容");
const fragment = document.createDocumentFragment(); // 性能优化
// 添加元素
parent.appendChild(div); // 追加
parent.insertBefore(div, referenceNode); // 插入到指定节点前
parent.append(div, text); // ES6,可添加多个
parent.prepend(div); // 插入到开头
// 删除元素
element.remove(); // 直接删除
parent.removeChild(child); // 通过父元素删除
// 替换元素
parent.replaceChild(newNode, oldNode);
// 克隆元素
const clone = element.cloneNode(true); // true 深克隆
// 判断包含关系
parent.contains(child); // 是否包含子节点
7.3 属性操作
// 标准属性
element.id = "newId";
element.className = "box active"; // 字符串形式
element.classList.add("hidden"); // 推荐使用
element.classList.remove("active");
element.classList.toggle("visible");
element.classList.contains("box");
// 自定义属性
element.setAttribute("data-id", "123");
const id = element.getAttribute("data-id");
element.hasAttribute("data-id");
element.removeAttribute("data-id");
// 数据集(data-*)
element.dataset.userId = "456";
element.dataset.role = "admin";
console.log(element.dataset); // { userId: "456", role: "admin" }
7.4 样式操作
// 内联样式
element.style.color = "red";
element.style.backgroundColor = "#f0f0f0";
element.style.cssText = "color: red; font-size: 14px;";
// 计算样式
const styles = window.getComputedStyle(element);
const color = styles.getPropertyValue("color");
// 类名操作(推荐)
element.className = "box"; // 替换所有类
element.classList.add("active"); // 添加
element.classList.remove("inactive"); // 删除
element.classList.toggle("visible"); // 切换
// 获取元素尺寸和位置
const rect = element.getBoundingClientRect(); // 相对于视口
console.log(rect.top, rect.left, rect.width, rect.height);
// 滚动相关
window.scrollTo(0, 0); // 滚动到顶部
element.scrollIntoView({ behavior: "smooth" }); // 平滑滚动到元素
7.5 事件处理
// 事件绑定
element.addEventListener("click", handleClick, false);
element.removeEventListener("click", handleClick);
// 事件对象
function handleClick(event) {
event.preventDefault(); // 阻止默认行为
event.stopPropagation(); // 阻止冒泡
event.stopImmediatePropagation(); // 阻止同事件其他监听
console.log(event.type); // 事件类型
console.log(event.target); // 实际触发元素
console.log(event.currentTarget); // 绑定事件元素
}
// 事件委托(利用冒泡)
document.querySelector("ul").addEventListener("click", (e) => {
if (e.target.tagName === "LI") {
console.log("点击了", e.target.textContent);
}
});
// 常见事件类型
// 鼠标事件:click, dblclick, mouseenter, mouseleave, mousemove
// 键盘事件:keydown, keyup, keypress
// 表单事件:submit, change, focus, blur, input
// 窗口事件:load, resize, scroll, beforeunload
// 触摸事件:touchstart, touchmove, touchend
// 自定义事件
const customEvent = new CustomEvent("myEvent", {
detail: { message: "自定义数据" },
bubbles: true,
cancelable: true
});
element.dispatchEvent(customEvent);
7.6 性能优化
// 减少重排和重绘
// 错误做法
for (let i = 0; i < 1000; i++) {
const div = document.createElement("div");
document.body.appendChild(div); // 每次都触发重排
}
// 正确做法:使用 DocumentFragment
const fragment = document.createDocumentFragment();
for (let i = 0; i < 1000; i++) {
const div = document.createElement("div");
fragment.appendChild(div);
}
document.body.appendChild(fragment); // 只触发一次重排
// 批量修改样式
// 错误做法
element.style.width = "100px";
element.style.height = "100px";
element.style.backgroundColor = "red"; // 多次重排
// 正确做法
element.style.cssText = "width:100px; height:100px; background:red;";
// 使用 class 切换
element.classList.add("new-styles"); // 一次重排
// 防抖(Debounce)
function debounce(fn, delay) {
let timer = null;
return function(...args) {
clearTimeout(timer);
timer = setTimeout(() => fn.apply(this, args), delay);
};
}
window.addEventListener("resize", debounce(() => {
console.log("窗口大小改变完成");
}, 300));
// 节流(Throttle)
function throttle(fn, interval) {
let lastTime = 0;
return function(...args) {
const now = Date.now();
if (now - lastTime >= interval) {
lastTime = now;
fn.apply(this, args);
}
};
}
window.addEventListener("scroll", throttle(() => {
console.log("滚动中");
}, 100));
八、BOM 操作
8.1 window 对象
// 窗口尺寸
window.innerWidth; // 视口宽度(含滚动条)
window.innerHeight; // 视口高度
window.outerWidth; // 浏览器窗口宽度
window.outerHeight; // 浏览器窗口高度
// 滚动
window.scrollX; // 水平滚动距离
window.scrollY; // 垂直滚动距离
window.scrollTo(0, 0); // 滚动到指定位置
window.scrollBy(0, 100); // 相对滚动
// 打开/关闭窗口
const newWindow = window.open("https://example.com", "_blank", "width=800,height=600");
newWindow.close();
// 定时器
const timerId = setTimeout(() => {
console.log("延迟执行");
}, 1000);
clearTimeout(timerId);
let count = 0;
const intervalId = setInterval(() => {
console.log("每隔1秒执行", ++count);
if (count === 5) clearInterval(intervalId);
}, 1000);
// requestAnimationFrame(动画)
function animate() {
// 更新动画
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
8.2 navigator 对象
// 浏览器信息
navigator.userAgent; // 用户代理字符串
navigator.platform; // 操作系统
navigator.language; // 浏览器语言
navigator.cookieEnabled; // 是否启用 Cookie
// 设备信息(移动端)
const isMobile = /Android|webOS|iPhone|iPad|iPod/i.test(navigator.userAgent);
// 地理位置
if ("geolocation" in navigator) {
navigator.geolocation.getCurrentPosition(
(position) => {
console.log(position.coords.latitude, position.coords.longitude);
},
(error) => {
console.error(error.message);
}
);
}
// 剪贴板
navigator.clipboard.writeText("要复制的文本")
.then(() => console.log("复制成功"))
.catch(err => console.error("复制失败"));
8.3 location 对象
// URL 信息
console.log(location.href); // 完整 URL
console.log(location.protocol); // "https:"
console.log(location.host); // "example.com:8080"
console.log(location.hostname); // "example.com"
console.log(location.port); // "8080"
console.log(location.pathname); // "/path/page.html"
console.log(location.search); // "?id=123&name=张三"
console.log(location.hash); // "#section1"
// 解析 URL 参数
const params = new URLSearchParams(location.search);
params.get("id"); // "123"
params.get("name"); // "张三"
params.has("id"); // true
// 页面跳转
location.assign("https://example.com"); // 跳转,保留历史记录
location.replace("https://example.com"); // 替换,不保留历史
location.reload(); // 刷新
location.href = "https://example.com"; // 等同于 assign
8.4 history 对象
// 历史记录长度
history.length;
// 前进后退
history.back(); // 后退
history.forward(); // 前进
history.go(-2); // 后退2步
history.go(0); // 刷新
// HTML5 History API(单页应用核心)
history.pushState({ page: 1 }, "标题", "/page/1"); // 添加历史记录
history.replaceState({ page: 2 }, "标题", "/page/2"); // 替换当前记录
// 监听历史变化
window.addEventListener("popstate", (event) => {
console.log("历史记录变化", event.state);
});
// 实际应用:无刷新切换页面
function navigateTo(url) {
history.pushState({}, "", url);
// 更新页面内容(AJAX)
loadContent(url);
}
8.5 sessionStorage 与 localStorage
// localStorage(永久存储,除非手动清除)
localStorage.setItem("key", "value");
const value = localStorage.getItem("key");
localStorage.removeItem("key");
localStorage.clear();
// 存储对象
const user = { name: "张三", age: 25 };
localStorage.setItem("user", JSON.stringify(user));
const storedUser = JSON.parse(localStorage.getItem("user"));
// sessionStorage(会话级别,关闭标签页清除)
sessionStorage.setItem("temp", "临时数据");
// 存储容量检测
function storageAvailable(type) {
try {
const storage = window[type];
const test = "__storage_test__";
storage.setItem(test, test);
storage.removeItem(test);
return true;
} catch (e) {
return false;
}
}
// 监听存储变化(跨标签页通信)
window.addEventListener("storage", (event) => {
console.log("key:", event.key);
console.log("oldValue:", event.oldValue);
console.log("newValue:", event.newValue);
console.log("url:", event.url);
});
九、错误处理
9.1 try...catch...finally
try {
// 可能出错的代码
const result = riskyOperation();
console.log(result);
} catch (error) {
// 错误处理
console.error("错误名称:", error.name);
console.error("错误信息:", error.message);
console.error("调用栈:", error.stack);
} finally {
// 无论是否出错都会执行
console.log("清理资源");
}
// 自定义错误
function divide(a, b) {
if (b === 0) {
throw new Error("除数不能为零");
}
return a / b;
}
try {
divide(10, 0);
} catch (error) {
console.error(error.message);
}
9.2 错误类型
// 内置错误类型
new Error("通用错误");
new SyntaxError("语法错误");
new ReferenceError("引用错误");
new TypeError("类型错误");
new RangeError("范围错误");
new URIError("URI错误");
new EvalError("eval错误");
// 实例
try {
// 引用错误
nonExistentVariable;
} catch (e) {
console.log(e instanceof ReferenceError); // true
}
try {
// 类型错误
null.toString();
} catch (e) {
console.log(e instanceof TypeError); // true
}
// 判断错误类型
try {
// 代码
} catch (error) {
if (error instanceof TypeError) {
console.log("类型错误,执行相应处理");
} else if (error instanceof RangeError) {
console.log("范围错误");
} else {
console.log("其他错误:", error.message);
}
}
9.3 全局错误捕获
// 同步错误捕获
window.addEventListener("error", (event) => {
console.error("全局错误:", event.error);
// 可以发送错误报告到服务器
reportError(event.error);
return true; // 阻止默认错误处理
});
// Promise 未捕获错误
window.addEventListener("unhandledrejection", (event) => {
console.error("未处理的 Promise 拒绝:", event.reason);
event.preventDefault();
});
// 异步错误
process.on("uncaughtException", (error) => {
console.error("Node.js 未捕获异常:", error);
process.exit(1);
});