Web API基础-BOM与浏览器环境
# Web API基础-BOM与浏览器环境
BOM(Browser Object Model,浏览器对象模型)提供了与浏览器交互的API,包括窗口控制、导航操作、屏幕信息、历史记录等。本文将系统介绍BOM的各个对象及其实际应用。
# 一、Window对象
Window对象是浏览器的顶层对象,既是JavaScript访问浏览器窗口的接口,也是ECMAScript规定的Global对象。
# 1.1 Window对象的双重身份
// 1. 作为全局对象
var globalVar = 'global';
console.log(window.globalVar); // "global"
function globalFunc() {
console.log('全局函数');
}
window.globalFunc(); // "全局函数"
// 2. 作为浏览器窗口对象
console.log(window.innerWidth); // 窗口内部宽度
console.log(window.innerHeight); // 窗口内部高度
console.log(window.outerWidth); // 窗口外部宽度
console.log(window.outerHeight); // 窗口外部高度
# 1.2 窗口尺寸
// 视口尺寸(不含滚动条)
console.log(window.innerWidth);
console.log(window.innerHeight);
// 窗口外部尺寸
console.log(window.outerWidth);
console.log(window.outerHeight);
// 屏幕尺寸
console.log(window.screen.width);
console.log(window.screen.height);
// 可用屏幕尺寸(不含任务栏)
console.log(window.screen.availWidth);
console.log(window.screen.availHeight);
// 监听窗口大小变化
window.addEventListener('resize', function() {
console.log('窗口尺寸改变:', window.innerWidth, window.innerHeight);
});
# 1.3 窗口位置
// 窗口相对于屏幕的位置
console.log(window.screenX); // 或 window.screenLeft
console.log(window.screenY); // 或 window.screenTop
// 移动窗口(可能被浏览器阻止)
window.moveTo(100, 100); // 移动到绝对位置
window.moveBy(50, 50); // 相对移动
// 调整窗口大小(可能被浏览器阻止)
window.resizeTo(800, 600); // 调整到指定大小
window.resizeBy(100, 50); // 相对调整
# 1.4 打开和关闭窗口
// 打开新窗口
const newWindow = window.open(
'https://example.com', // URL
'_blank', // 目标(_blank, _self, _parent, _top, 或窗口名)
'width=800,height=600,left=100,top=100' // 特性字符串
);
// 检查窗口是否被阻止
if (newWindow) {
console.log('窗口打开成功');
// 关闭窗口
newWindow.close();
} else {
console.log('弹窗被阻止');
}
// 关闭当前窗口(只能关闭window.open打开的窗口)
window.close();
// 检查窗口是否关闭
console.log(newWindow.closed); // true/false
# 1.5 窗口关系
// 顶层窗口
console.log(window.top); // 最顶层窗口
// 父窗口
console.log(window.parent); // 父窗口(iframe场景)
// 当前窗口
console.log(window.self); // 当前窗口
console.log(window.window); // 当前窗口
// 检查是否在iframe中
if (window.self !== window.top) {
console.log('页面在iframe中');
}
// 框架数组(iframe)
console.log(window.frames); // 所有框架
console.log(window.frames.length); // 框架数量
console.log(window.frames[0]); // 第一个框架
# 二、Location对象
Location对象包含当前页面的URL信息,并提供导航方法。
# 2.1 URL属性
// 假设URL: https://example.com:8080/path/page.html?id=123&name=test#section
const loc = window.location;
console.log(loc.href); // 完整URL
// "https://example.com:8080/path/page.html?id=123&name=test#section"
console.log(loc.protocol); // 协议
// "https:"
console.log(loc.host); // 主机名和端口
// "example.com:8080"
console.log(loc.hostname); // 主机名
// "example.com"
console.log(loc.port); // 端口
// "8080"
console.log(loc.pathname); // 路径
// "/path/page.html"
console.log(loc.search); // 查询字符串
// "?id=123&name=test"
console.log(loc.hash); // 哈希
// "#section"
console.log(loc.origin); // 源(协议+主机+端口)
// "https://example.com:8080"
# 2.2 URL操作
// 解析查询字符串
const params = new URLSearchParams(location.search);
console.log(params.get('id')); // "123"
console.log(params.get('name')); // "test"
console.log(params.has('id')); // true
// 遍历参数
for (const [key, value] of params) {
console.log(`${key}: ${value}`);
}
// 修改URL(不会触发页面加载)
const url = new URL(location.href);
url.searchParams.set('page', '2');
url.searchParams.delete('id');
console.log(url.toString());
# 2.3 页面导航
// 跳转页面
location.href = 'https://example.com'; // 设置href
location.assign('https://example.com'); // 同上
location.replace('https://example.com'); // 替换当前页(不产生历史记录)
// 重新加载页面
location.reload(); // 重新加载(可能使用缓存)
location.reload(true); // 强制从服务器重新加载
// 只修改hash(不重新加载页面)
location.hash = '#newSection';
// 只修改search
location.search = '?page=2';
# 2.4 URL跳转最佳实践
// ✓ 推荐:使用location.href
window.location.href = '/new-page';
// ✓ 推荐:使用location.assign
window.location.assign('/new-page');
// ✓ 推荐:替换历史记录(用户无法返回)
window.location.replace('/new-page');
// ✗ 避免:直接赋值location
window.location = '/new-page'; // 不推荐
// 外部链接在新窗口打开
window.open('https://external.com', '_blank');
# 三、Navigator对象
Navigator对象包含浏览器的信息。
# 3.1 浏览器信息
const nav = window.navigator;
// 用户代理字符串
console.log(nav.userAgent);
// "Mozilla/5.0 (Windows NT 10.0; Win64; x64) ..."
// 浏览器名称(已废弃,总是返回"Netscape")
console.log(nav.appName);
// 浏览器版本
console.log(nav.appVersion);
// 浏览器厂商
console.log(nav.vendor);
// 平台
console.log(nav.platform);
// "Win32", "MacIntel", "Linux x86_64" 等
// 语言
console.log(nav.language); // "zh-CN"
console.log(nav.languages); // ["zh-CN", "zh", "en"]
// 在线状态
console.log(nav.onLine); // true/false
// 监听在线状态变化
window.addEventListener('online', () => {
console.log('网络连接恢复');
});
window.addEventListener('offline', () => {
console.log('网络连接断开');
});
# 3.2 浏览器特性检测
// CPU核心数
console.log(navigator.hardwareConcurrency); // 4, 8 等
// 内存大小(GB)
console.log(navigator.deviceMemory); // 8, 16 等
// 最大触摸点数
console.log(navigator.maxTouchPoints); // 0 (非触摸设备), 10 等
// Cookie是否启用
console.log(navigator.cookieEnabled); // true/false
// 请勿跟踪
console.log(navigator.doNotTrack); // "1", "0", null
// 用户代理数据(现代API)
if ('userAgentData' in navigator) {
navigator.userAgentData.getHighEntropyValues([
'platform',
'platformVersion',
'model'
]).then(data => {
console.log(data);
});
}
# 3.3 设备检测
// 检测是否为移动设备
function isMobile() {
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
navigator.userAgent
);
}
// 检测操作系统
function getOS() {
const ua = navigator.userAgent;
if (ua.indexOf('Win') !== -1) return 'Windows';
if (ua.indexOf('Mac') !== -1) return 'macOS';
if (ua.indexOf('Linux') !== -1) return 'Linux';
if (ua.indexOf('Android') !== -1) return 'Android';
if (ua.indexOf('iOS') !== -1) return 'iOS';
return 'Unknown';
}
// 检测浏览器
function getBrowser() {
const ua = navigator.userAgent;
if (ua.indexOf('Chrome') !== -1) return 'Chrome';
if (ua.indexOf('Safari') !== -1) return 'Safari';
if (ua.indexOf('Firefox') !== -1) return 'Firefox';
if (ua.indexOf('Edge') !== -1) return 'Edge';
return 'Unknown';
}
# 3.4 剪贴板API
// 写入剪贴板
async function copyToClipboard(text) {
try {
await navigator.clipboard.writeText(text);
console.log('复制成功');
} catch (err) {
console.error('复制失败:', err);
}
}
// 读取剪贴板
async function pasteFromClipboard() {
try {
const text = await navigator.clipboard.readText();
console.log('粘贴内容:', text);
return text;
} catch (err) {
console.error('读取剪贴板失败:', err);
}
}
// 使用示例
document.querySelector('.copy-btn').addEventListener('click', () => {
copyToClipboard('要复制的文本');
});
# 3.5 分享API
// Web Share API
async function shareContent() {
if (navigator.share) {
try {
await navigator.share({
title: '分享标题',
text: '分享描述',
url: 'https://example.com'
});
console.log('分享成功');
} catch (err) {
console.error('分享失败:', err);
}
} else {
console.log('浏览器不支持Web Share API');
}
}
# 四、History对象
History对象管理浏览器的会话历史记录。
# 4.1 历史记录导航
// 前进/后退
history.back(); // 后退一页
history.forward(); // 前进一页
history.go(-1); // 后退一页
history.go(1); // 前进一页
history.go(0); // 刷新页面
// 历史记录数量
console.log(history.length);
# 4.2 History API(单页应用)
// pushState:添加历史记录(不刷新页面)
history.pushState(
{ page: 1 }, // 状态对象
'Page 1', // 标题(大多数浏览器忽略)
'/page1' // URL
);
// replaceState:替换当前历史记录
history.replaceState(
{ page: 2 },
'Page 2',
'/page2'
);
// 获取当前状态
console.log(history.state); // { page: 2 }
// 监听历史记录变化(用户点击前进/后退)
window.addEventListener('popstate', function(e) {
console.log('历史记录改变:', e.state);
// 根据状态更新页面内容
if (e.state && e.state.page) {
loadPage(e.state.page);
}
});
# 4.3 单页应用路由示例
class SimpleRouter {
constructor() {
this.routes = {};
// 监听URL变化
window.addEventListener('popstate', (e) => {
this.handleRoute(location.pathname);
});
// 拦截链接点击
document.addEventListener('click', (e) => {
if (e.target.matches('a[data-link]')) {
e.preventDefault();
this.navigate(e.target.href);
}
});
}
// 注册路由
route(path, handler) {
this.routes[path] = handler;
}
// 导航到新路由
navigate(url) {
history.pushState(null, '', url);
this.handleRoute(new URL(url).pathname);
}
// 处理路由
handleRoute(path) {
const handler = this.routes[path];
if (handler) {
handler();
} else {
console.log('404: 路由不存在');
}
}
}
// 使用示例
const router = new SimpleRouter();
router.route('/', () => {
console.log('首页');
});
router.route('/about', () => {
console.log('关于页面');
});
// HTML中
// <a href="/" data-link>首页</a>
// <a href="/about" data-link>关于</a>
# 五、Screen对象
Screen对象包含屏幕信息。
# 5.1 屏幕尺寸
const screen = window.screen;
// 屏幕宽高
console.log(screen.width); // 屏幕宽度
console.log(screen.height); // 屏幕高度
// 可用宽高(不含任务栏等)
console.log(screen.availWidth);
console.log(screen.availHeight);
// 颜色深度
console.log(screen.colorDepth); // 24, 32 等
console.log(screen.pixelDepth); // 通常与colorDepth相同
// 屏幕方向
console.log(screen.orientation);
console.log(screen.orientation.type); // "portrait-primary", "landscape-primary" 等
console.log(screen.orientation.angle); // 0, 90, 180, 270
// 监听屏幕方向变化
screen.orientation.addEventListener('change', () => {
console.log('屏幕方向:', screen.orientation.type);
});
# 5.2 响应式设计辅助
// 判断屏幕类型
function getScreenType() {
const width = screen.width;
if (width < 768) return 'mobile';
if (width < 1024) return 'tablet';
return 'desktop';
}
// 判断是否高清屏
function isRetinaScreen() {
return window.devicePixelRatio > 1;
}
console.log('设备像素比:', window.devicePixelRatio); // 1, 2, 3 等
# 六、定时器
# 6.1 setTimeout和setInterval
// setTimeout:延迟执行一次
const timeoutId = setTimeout(() => {
console.log('延迟1秒执行');
}, 1000);
// 取消定时器
clearTimeout(timeoutId);
// setInterval:重复执行
const intervalId = setInterval(() => {
console.log('每秒执行');
}, 1000);
// 取消定时器
clearInterval(intervalId);
// 传递参数
setTimeout((name, age) => {
console.log(`Hello ${name}, ${age}岁`);
}, 1000, '张三', 25);
# 6.2 requestAnimationFrame
// 用于动画,每帧调用一次(通常60fps)
function animate() {
// 动画逻辑
element.style.left = element.offsetLeft + 1 + 'px';
// 继续下一帧
requestAnimationFrame(animate);
}
const animationId = requestAnimationFrame(animate);
// 取消动画
cancelAnimationFrame(animationId);
// 实际应用示例
function smoothScroll(target) {
const start = window.scrollY;
const distance = target - start;
const duration = 500;
let startTime = null;
function animation(currentTime) {
if (!startTime) startTime = currentTime;
const elapsed = currentTime - startTime;
const progress = Math.min(elapsed / duration, 1);
window.scrollTo(0, start + distance * progress);
if (progress < 1) {
requestAnimationFrame(animation);
}
}
requestAnimationFrame(animation);
}
# 6.3 requestIdleCallback
// 在浏览器空闲时执行低优先级任务
requestIdleCallback((deadline) => {
// deadline.timeRemaining() 返回剩余空闲时间
while (deadline.timeRemaining() > 0 && tasks.length > 0) {
const task = tasks.shift();
task();
}
// 还有任务则继续
if (tasks.length > 0) {
requestIdleCallback(callback);
}
}, { timeout: 2000 }); // 最多等待2秒
# 七、对话框
# 7.1 原生对话框
// 警告框
alert('这是一条警告信息');
// 确认框
const result = confirm('确定要删除吗?');
if (result) {
console.log('用户点击确定');
} else {
console.log('用户点击取消');
}
// 输入框
const name = prompt('请输入你的名字:', '默认值');
if (name !== null) {
console.log('用户输入:', name);
} else {
console.log('用户取消');
}
// 打印
window.print();
# 7.2 自定义对话框(推荐)
// 使用HTML dialog元素
const dialog = document.querySelector('dialog');
// 打开对话框
dialog.showModal(); // 模态对话框
dialog.show(); // 非模态对话框
// 关闭对话框
dialog.close();
dialog.close('returnValue'); // 带返回值
// 监听关闭事件
dialog.addEventListener('close', () => {
console.log('对话框关闭,返回值:', dialog.returnValue);
});
# 八、综合示例
# 九、最佳实践
# 9.1 Window对象
- 避免污染全局命名空间,使用模块化或IIFE
- 谨慎使用
window.open,可能被浏览器阻止 - 监听
resize事件时使用节流优化性能
# 9.2 Location对象
- 使用
location.href或location.assign跳转 - 使用
URLSearchParams操作查询参数 - 使用
location.replace避免产生历史记录
# 9.3 Navigator对象
- 不要完全依赖
userAgent检测,优先使用特性检测 - 使用现代API时检查兼容性
- 注意剪贴板API需要HTTPS和用户权限
# 9.4 History对象
- 使用
pushState实现单页应用路由 - 监听
popstate事件响应前进/后退 - 状态对象应该是可序列化的
# 9.5 定时器
- 动画使用
requestAnimationFrame而不是setInterval - 组件销毁时清除定时器
- 避免在定时器中执行耗时操作
# 十、总结
BOM提供了丰富的浏览器交互能力:
- Window对象:窗口控制、尺寸、位置
- Location对象:URL信息、页面导航
- Navigator对象:浏览器信息、设备检测
- History对象:历史记录、单页应用路由
- Screen对象:屏幕信息、方向
- 定时器:延迟执行、动画帧
合理使用BOM API,可以构建功能丰富、用户体验良好的Web应用。
祝你变得更强!
编辑 (opens new window)
上次更新: 2025/11/28