轩辕李的博客 轩辕李的博客
首页
  • Java
  • Spring
  • 其他语言
  • 工具
  • JavaScript
  • TypeScript
  • Node.js
  • Vue.js
  • 前端工程化
  • 浏览器与Web API
  • 分布式
  • 代码质量管理
  • 基础
  • 操作系统
  • 计算机网络
  • 编程范式
  • 安全
  • 中间件
  • 心得
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

轩辕李

勇猛精进,星辰大海
首页
  • Java
  • Spring
  • 其他语言
  • 工具
  • JavaScript
  • TypeScript
  • Node.js
  • Vue.js
  • 前端工程化
  • 浏览器与Web API
  • 分布式
  • 代码质量管理
  • 基础
  • 操作系统
  • 计算机网络
  • 编程范式
  • 安全
  • 中间件
  • 心得
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • JavaScript

  • TypeScript

  • Node.js

  • Vue.js

  • 工程化

  • 浏览器与Web API

    • HTML基础-语义化标签与文档结构
    • HTML基础-文本与排版标签
    • HTML基础-列表与表格
    • HTML表单-Input类型详解
    • HTML表单-表单元素与验证
    • HTML交互-多媒体元素
    • HTML工程化-模板与组件化
    • HTML工程化-性能优化
    • CSS基础-选择器与优先级
    • CSS基础-盒模型与布局基础
    • CSS基础-单位与颜色系统
    • CSS基础-文本与字体
    • CSS基础-背景、列表与表格样式
    • CSS布局-Flexbox完全指南
    • CSS布局-Grid网格布局
    • CSS布局-响应式设计实践
    • CSS进阶-动画与过渡
    • CSS进阶-渐变与阴影效果
    • CSS进阶-Transform与3D变换
    • CSS进阶-滤镜与混合模式
    • 现代CSS-CSS预处理器对比
    • 现代CSS-CSS-in-JS方案
    • 现代CSS-原子化CSS与Tailwind
    • CSS工程化-架构与规范
    • CSS工程化-性能优化
    • CSS工程化-PostCSS实战指南
    • Web API基础-DOM操作完全指南
    • Web API基础-事件处理与委托
    • Web API基础-BOM与浏览器环境
      • 一、Window对象
        • 1.1 Window对象的双重身份
        • 1.2 窗口尺寸
        • 1.3 窗口位置
        • 1.4 打开和关闭窗口
        • 1.5 窗口关系
      • 二、Location对象
        • 2.1 URL属性
        • 2.2 URL操作
        • 2.3 页面导航
        • 2.4 URL跳转最佳实践
      • 三、Navigator对象
        • 3.1 浏览器信息
        • 3.2 浏览器特性检测
        • 3.3 设备检测
        • 3.4 剪贴板API
        • 3.5 分享API
      • 四、History对象
        • 4.1 历史记录导航
        • 4.2 History API(单页应用)
        • 4.3 单页应用路由示例
      • 五、Screen对象
        • 5.1 屏幕尺寸
        • 5.2 响应式设计辅助
      • 六、定时器
        • 6.1 setTimeout和setInterval
        • 6.2 requestAnimationFrame
        • 6.3 requestIdleCallback
      • 七、对话框
        • 7.1 原生对话框
        • 7.2 自定义对话框(推荐)
      • 八、综合示例
      • 九、最佳实践
        • 9.1 Window对象
        • 9.2 Location对象
        • 9.3 Navigator对象
        • 9.4 History对象
        • 9.5 定时器
      • 十、总结
    • Web API存储-客户端存储方案
    • Web API网络-HTTP请求详解
    • Web API网络-实时通信方案
    • Web API交互-用户体验增强
    • HTML&CSS历代版本新特性
  • 前端
  • 浏览器与Web API
轩辕李
2019-09-01
目录

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);
});

# 八、综合示例

<html>
  <div id="bom-demo-z7a8b">
    <h3>BOM综合示例</h3>
    
    <div class="section-z7a8b">
      <h4>浏览器信息</h4>
      <div class="info-grid-z7a8b">
        <div><strong>用户代理:</strong> <span id="ua-z7a8b"></span></div>
        <div><strong>平台:</strong> <span id="platform-z7a8b"></span></div>
        <div><strong>语言:</strong> <span id="language-z7a8b"></span></div>
        <div><strong>在线状态:</strong> <span id="online-z7a8b"></span></div>
        <div><strong>Cookie启用:</strong> <span id="cookie-z7a8b"></span></div>
        <div><strong>屏幕尺寸:</strong> <span id="screen-z7a8b"></span></div>
        <div><strong>视口尺寸:</strong> <span id="viewport-z7a8b"></span></div>
        <div><strong>设备像素比:</strong> <span id="dpr-z7a8b"></span></div>
      </div>
    </div>
    
    <div class="section-z7a8b">
      <h4>URL操作</h4>
      <div class="url-info-z7a8b">
        <div><strong>完整URL:</strong> <code id="href-z7a8b"></code></div>
        <div><strong>协议:</strong> <code id="protocol-z7a8b"></code></div>
        <div><strong>主机:</strong> <code id="host-z7a8b"></code></div>
        <div><strong>路径:</strong> <code id="pathname-z7a8b"></code></div>
        <div><strong>查询:</strong> <code id="search-z7a8b"></code></div>
        <div><strong>哈希:</strong> <code id="hash-z7a8b"></code></div>
      </div>
      <div class="url-controls-z7a8b">
        <button id="change-hash-z7a8b">修改Hash</button>
        <button id="add-param-z7a8b">添加参数</button>
      </div>
    </div>
    
    <div class="section-z7a8b">
      <h4>历史记录模拟</h4>
      <div class="nav-buttons-z7a8b">
        <button id="page1-z7a8b">页面1</button>
        <button id="page2-z7a8b">页面2</button>
        <button id="page3-z7a8b">页面3</button>
      </div>
      <div id="page-content-z7a8b" class="page-content-z7a8b">
        当前页面: <strong id="current-page-z7a8b">首页</strong>
      </div>
      <div class="history-controls-z7a8b">
        <button id="back-z7a8b">← 后退</button>
        <button id="forward-z7a8b">前进 →</button>
        <span>历史记录长度: <strong id="history-length-z7a8b">0</strong></span>
      </div>
    </div>
    
    <div class="section-z7a8b">
      <h4>剪贴板操作</h4>
      <input type="text" id="clip-input-z7a8b" value="测试文本" class="clip-input-z7a8b">
      <div class="clip-buttons-z7a8b">
        <button id="copy-btn-z7a8b">复制</button>
        <button id="paste-btn-z7a8b">粘贴</button>
      </div>
      <div id="clip-status-z7a8b" class="clip-status-z7a8b"></div>
    </div>
  </div>
</html>

<style>
  #bom-demo-z7a8b {
    font-family: sans-serif;
    max-width: 800px;
  }
  
  .section-z7a8b {
    margin-bottom: 24px;
    padding: 20px;
    background: #f9f9f9;
    border-radius: 8px;
  }
  
  .section-z7a8b h4 {
    margin: 0 0 16px 0;
    color: #333;
    border-bottom: 2px solid #2196F3;
    padding-bottom: 8px;
  }
  
  .info-grid-z7a8b {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
    gap: 12px;
  }
  
  .info-grid-z7a8b div {
    padding: 8px;
    background: white;
    border-radius: 4px;
    font-size: 13px;
  }
  
  .info-grid-z7a8b strong {
    color: #666;
    display: block;
    margin-bottom: 4px;
  }
  
  .info-grid-z7a8b span {
    color: #2196F3;
    word-break: break-all;
  }
  
  .url-info-z7a8b {
    background: white;
    padding: 16px;
    border-radius: 4px;
    margin-bottom: 12px;
  }
  
  .url-info-z7a8b div {
    margin-bottom: 8px;
    font-size: 13px;
  }
  
  .url-info-z7a8b strong {
    color: #666;
    min-width: 80px;
    display: inline-block;
  }
  
  .url-info-z7a8b code {
    background: #f5f5f5;
    padding: 2px 6px;
    border-radius: 3px;
    color: #2196F3;
    word-break: break-all;
  }
  
  .url-controls-z7a8b {
    display: flex;
    gap: 8px;
  }
  
  button {
    padding: 8px 16px;
    background: #2196F3;
    color: white;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    font-size: 13px;
    transition: background 0.2s;
  }
  
  button:hover {
    background: #1976D2;
  }
  
  button:disabled {
    background: #ccc;
    cursor: not-allowed;
  }
  
  .nav-buttons-z7a8b {
    display: flex;
    gap: 8px;
    margin-bottom: 16px;
  }
  
  .page-content-z7a8b {
    padding: 20px;
    background: white;
    border-radius: 4px;
    text-align: center;
    margin-bottom: 16px;
    font-size: 18px;
  }
  
  .page-content-z7a8b strong {
    color: #2196F3;
  }
  
  .history-controls-z7a8b {
    display: flex;
    gap: 12px;
    align-items: center;
  }
  
  .clip-input-z7a8b {
    width: 100%;
    padding: 10px;
    border: 1px solid #ddd;
    border-radius: 4px;
    font-size: 14px;
    margin-bottom: 12px;
  }
  
  .clip-buttons-z7a8b {
    display: flex;
    gap: 8px;
    margin-bottom: 12px;
  }
  
  .clip-status-z7a8b {
    padding: 10px;
    background: white;
    border-radius: 4px;
    min-height: 20px;
    font-size: 13px;
  }
</style>

<script>
  function updateBrowserInfo() {
    document.getElementById('ua-z7a8b').textContent = navigator.userAgent.substring(0, 50) + '...';
    document.getElementById('platform-z7a8b').textContent = navigator.platform;
    document.getElementById('language-z7a8b').textContent = navigator.language;
    document.getElementById('online-z7a8b').textContent = navigator.onLine ? '在线' : '离线';
    document.getElementById('cookie-z7a8b').textContent = navigator.cookieEnabled ? '是' : '否';
    document.getElementById('screen-z7a8b').textContent = `${screen.width} x ${screen.height}`;
    document.getElementById('viewport-z7a8b').textContent = `${window.innerWidth} x ${window.innerHeight}`;
    document.getElementById('dpr-z7a8b').textContent = window.devicePixelRatio;
  }
  
  function updateURLInfo() {
    document.getElementById('href-z7a8b').textContent = location.href;
    document.getElementById('protocol-z7a8b').textContent = location.protocol;
    document.getElementById('host-z7a8b').textContent = location.host;
    document.getElementById('pathname-z7a8b').textContent = location.pathname;
    document.getElementById('search-z7a8b').textContent = location.search || '(无)';
    document.getElementById('hash-z7a8b').textContent = location.hash || '(无)';
  }
  
  updateBrowserInfo();
  updateURLInfo();
  
  window.addEventListener('resize', updateBrowserInfo);
  window.addEventListener('online', updateBrowserInfo);
  window.addEventListener('offline', updateBrowserInfo);
  
  document.getElementById('change-hash-z7a8b').addEventListener('click', () => {
    const randomHash = '#section-' + Math.floor(Math.random() * 100);
    location.hash = randomHash;
    updateURLInfo();
  });
  
  document.getElementById('add-param-z7a8b').addEventListener('click', () => {
    const url = new URL(location.href);
    url.searchParams.set('demo', Date.now());
    history.replaceState(null, '', url);
    updateURLInfo();
  });

  let currentPage = '首页';
  
  function updatePage(page) {
    currentPage = page;
    document.getElementById('current-page-z7a8b').textContent = page;
    document.getElementById('history-length-z7a8b').textContent = history.length;
  }
  
  document.getElementById('page1-z7a8b').addEventListener('click', () => {
    history.pushState({ page: '页面1' }, '', '#page1');
    updatePage('页面1');
  });
  
  document.getElementById('page2-z7a8b').addEventListener('click', () => {
    history.pushState({ page: '页面2' }, '', '#page2');
    updatePage('页面2');
  });
  
  document.getElementById('page3-z7a8b').addEventListener('click', () => {
    history.pushState({ page: '页面3' }, '', '#page3');
    updatePage('页面3');
  });
  
  document.getElementById('back-z7a8b').addEventListener('click', () => {
    history.back();
  });
  
  document.getElementById('forward-z7a8b').addEventListener('click', () => {
    history.forward();
  });
  
  window.addEventListener('popstate', (e) => {
    if (e.state && e.state.page) {
      updatePage(e.state.page);
    }
  });

  document.getElementById('copy-btn-z7a8b').addEventListener('click', async () => {
    const input = document.getElementById('clip-input-z7a8b');
    const status = document.getElementById('clip-status-z7a8b');
    
    try {
      await navigator.clipboard.writeText(input.value);
      status.textContent = '✓ 复制成功: ' + input.value;
      status.style.color = '#4caf50';
    } catch (err) {
      status.textContent = '✗ 复制失败(可能需要用户权限)';
      status.style.color = '#f44336';
    }
  });
  
  document.getElementById('paste-btn-z7a8b').addEventListener('click', async () => {
    const input = document.getElementById('clip-input-z7a8b');
    const status = document.getElementById('clip-status-z7a8b');
    
    try {
      const text = await navigator.clipboard.readText();
      input.value = text;
      status.textContent = '✓ 粘贴成功';
      status.style.color = '#4caf50';
    } catch (err) {
      status.textContent = '✗ 读取剪贴板失败(可能需要用户权限)';
      status.style.color = '#f44336';
    }
  });
</script>

# 九、最佳实践

# 9.1 Window对象

  1. 避免污染全局命名空间,使用模块化或IIFE
  2. 谨慎使用window.open,可能被浏览器阻止
  3. 监听resize事件时使用节流优化性能

# 9.2 Location对象

  1. 使用location.href或location.assign跳转
  2. 使用URLSearchParams操作查询参数
  3. 使用location.replace避免产生历史记录

# 9.3 Navigator对象

  1. 不要完全依赖userAgent检测,优先使用特性检测
  2. 使用现代API时检查兼容性
  3. 注意剪贴板API需要HTTPS和用户权限

# 9.4 History对象

  1. 使用pushState实现单页应用路由
  2. 监听popstate事件响应前进/后退
  3. 状态对象应该是可序列化的

# 9.5 定时器

  1. 动画使用requestAnimationFrame而不是setInterval
  2. 组件销毁时清除定时器
  3. 避免在定时器中执行耗时操作

# 十、总结

BOM提供了丰富的浏览器交互能力:

  1. Window对象:窗口控制、尺寸、位置
  2. Location对象:URL信息、页面导航
  3. Navigator对象:浏览器信息、设备检测
  4. History对象:历史记录、单页应用路由
  5. Screen对象:屏幕信息、方向
  6. 定时器:延迟执行、动画帧

合理使用BOM API,可以构建功能丰富、用户体验良好的Web应用。

祝你变得更强!

编辑 (opens new window)
#Web API#BOM#JavaScript#Browser
上次更新: 2025/11/28
Web API基础-事件处理与委托
Web API存储-客户端存储方案

← Web API基础-事件处理与委托 Web API存储-客户端存储方案→

最近更新
01
AI编程时代的一些心得
09-11
02
Claude Code与Codex的协同工作
09-01
03
Claude Code 最佳实践(个人版)
08-01
更多文章>
Theme by Vdoing | Copyright © 2018-2025 京ICP备2021021832号-2 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式