JavaScript 操作 BOM

目录

一、window 对象:BOM 的核心

1. 窗口层级与关系

2. 窗口尺寸与滚动

尺寸细节

滚动操作

3. 窗口操作深度解析

打开新窗口(window.open)

全屏 API 扩展

二、location 对象:URL 与导航控制

1. URL 解析与操作

完整 URL 结构

高级查询参数处理

2. 导航方法对比

三、history 对象:历史记录管理

1. 核心方法与事件

pushState 与 replaceState

popstate 事件

2. SPA 路由应用示例

四、navigator 对象:浏览器与设备信息

1. 实用属性扩展

2. 地理定位高级用法

五、screen 对象:屏幕信息

关键属性解析

六、弹出框与用户交互

原生弹出框的局限性

七、定时器与性能优化

定时器精度与替代方案

精度问题

动画专用定时器

八、BOM 事件与页面生命周期

关键事件扩展

页面可见性(visibilitychange)

页面缓存与加载(pageshow/pagehide)

窗口大小变化防抖

九、BOM 安全限制与最佳实践

核心安全限制

最佳实践


BOM(Browser Object Model,浏览器对象模型)是 JavaScript 与浏览器交互的核心接口,它允许开发者控制浏览器窗口、导航、历史记录、设备信息等关键功能。与 DOM(文档对象模型)专注于网页内容不同,BOM 聚焦于浏览器本身的特性。本文将在基础内容上进行深度扩展,涵盖更多实用细节、高级用法及实际开发场景。

一、window 对象:BOM 的核心

window 是 BOM 的顶层对象,代表浏览器窗口或标签页,同时也是 JavaScript 全局作用域的载体——所有全局变量、函数和对象都是 window 的属性。

1. 窗口层级与关系

在包含框架(iframe)的页面中,window 对象存在层级关系,通过以下属性可访问不同层级的窗口:

// 当前窗口自身
console.log(window.self); // 等同于 window

// 父窗口(若当前窗口是框架)
console.log(window.parent); 

// 顶层窗口(最外层窗口,无父窗口)
console.log(window.top); 

// 检查当前窗口是否是顶层窗口(防止被嵌套在恶意框架中)
if (window.top !== window.self) {
  // 可能被嵌套在第三方网站,可执行防御逻辑
  window.top.location.href = window.self.location.href; // 跳转到顶层
}

// 获取页面中所有框架的窗口对象
const frames = window.frames; // 类数组对象,包含所有 iframe 窗口

应用场景:防止网页被恶意网站通过 iframe 嵌套(点击劫持攻击),可通过检测 window.top 与 window.self 是否一致实现防御。

2. 窗口尺寸与滚动

除基础尺寸属性外,还需掌握滚动相关的操作,这在处理长页面或动态加载内容时至关重要:

尺寸细节
// 窗口内部尺寸(含滚动条,不含工具栏)
const innerSize = {
  width: window.innerWidth,
  height: window.innerHeight
};

// 窗口外部尺寸(含边框、工具栏,不同浏览器定义有差异)
const outerSize = {
  width: window.outerWidth,
  height: window.outerHeight
};

// 文档内容尺寸(实际内容高度,可能超过窗口高度)
const docSize = {
  width: Math.max(
    document.body.scrollWidth,
    document.body.offsetWidth,
    document.documentElement.clientWidth,
    document.documentElement.scrollWidth,
    document.documentElement.offsetWidth
  ),
  height: Math.max(
    document.body.scrollHeight,
    document.body.offsetHeight,
    document.documentElement.clientHeight,
    document.documentElement.scrollHeight,
    document.documentElement.offsetHeight
  )
};
滚动操作
// 获取文档滚动位置(兼容不同浏览器)
const scrollPos = {
  x: window.scrollX || document.documentElement.scrollLeft,
  y: window.scrollY || document.documentElement.scrollTop
};

// 滚动到指定位置(绝对位置)
window.scrollTo(0, 500); // 滚动到 Y 轴 500px 处
// 或使用配置对象(支持平滑滚动)
window.scrollTo({
  top: 500,
  behavior: 'smooth' // 平滑滚动
});

// 相对当前位置滚动
window.scrollBy(0, 100); // 向下滚动 100px

// 滚动到指定元素(需元素 ID 或 DOM 对象)
document.getElementById('target').scrollIntoView({
  behavior: 'smooth',
  block: 'start' // 对齐元素顶部
});

注意scrollX/scrollY 是标准属性,scrollLeft/scrollTop 是兼容旧浏览器的写法,现代开发中推荐优先使用标准属性。

3. 窗口操作深度解析

打开新窗口(window.open

window.open 用于创建新窗口,其参数和行为有诸多细节需注意:

// 完整参数示例
const newWindow = window.open(
  'https://example.com', // URL(空字符串则打开空白页)
  'newWindowName', // 窗口名称(可用于定位已存在窗口)
  // 窗口特征字符串(逗号分隔,无空格)
  'width=800,height=600,top=100,left=100,' +
  'toolbar=no,menubar=no,scrollbars=yes,' +
  'resizable=yes,location=yes,status=yes'
);

常用特征参数

  • width/height:窗口宽高(像素)
  • top/left:窗口相对于屏幕的坐标
  • toolbar/menubar/location:是否显示工具栏/菜单栏/地址栏(yes/no
  • scrollbars:是否允许滚动(yes 则内容超出时显示滚动条)
  • resizable:是否允许调整窗口大小
  • noopener:新窗口的 window.opener 为 null(增强安全性,防止钓鱼)
  • noreferrer:不发送 Referrer 头(隐私保护)

安全限制

  • 现代浏览器默认阻止非用户交互(如 load 事件中)触发的 window.open(视为弹窗广告)
  • 仅能对 window.open 创建的窗口执行 moveTo/resizeTo 等操作,主窗口通常受浏览器限制
全屏 API 扩展

除基础的全屏切换,还需掌握全屏状态检测和事件监听:

// 检测当前是否处于全屏状态
function isFullscreen() {
  return !!(
    document.fullscreenElement ||
    document.webkitFullscreenElement ||
    document.msFullscreenElement
  );
}

// 监听全屏状态变化
document.addEventListener('fullscreenchange', () => {
  if (isFullscreen()) {
    console.log('进入全屏模式');
  } else {
    console.log('退出全屏模式');
  }
});

// 全屏错误处理
document.addEventListener('fullscreenerror', (err) => {
  console.error('全屏操作失败:', err);
});

二、location 对象:URL 与导航控制

location 对象封装了当前页面的 URL 信息,并提供导航相关的方法,是单页应用(SPA)路由管理的核心工具。

1. URL 解析与操作

完整 URL 结构

以 https://user:pass@example.com:8080/path/file.html?name=test#section1 为例:

  • protocolhttps:(协议)
  • usernameuser(用户名,现代浏览器很少使用)
  • passwordpass(密码,已废弃,不安全)
  • hostnameexample.com(域名)
  • port8080(端口,默认端口可省略)
  • hostexample.com:8080hostname + port
  • pathname/path/file.html(路径)
  • search?name=test(查询字符串)
  • hash#section1(锚点)
  • href:完整 URL 字符串
高级查询参数处理

使用 URLSearchParams API 高效操作查询字符串(替代手动解析):

// 解析现有查询参数
const params = new URLSearchParams(location.search);

// 获取参数
console.log(params.get('name')); // "test"(不存在则返回 null)
console.log(params.getAll('tags')); // 获取多值参数(如 ?tags=js&tags=bom)

// 添加/修改参数
params.set('page', '2'); // 覆盖现有参数
params.append('filter', 'active'); // 新增参数(允许重复)

// 删除参数
params.delete('name');

// 转换为查询字符串(带 ?)
const newSearch = '?' + params.toString(); // "?page=2&filter=active"

// 应用到当前 URL(不刷新页面,修改地址栏)
history.replaceState(null, '', newSearch);

注意URLSearchParams 不支持嵌套对象,复杂参数需手动序列化(如 JSON.stringify)。

2. 导航方法对比

方法作用历史记录适用场景
location.href = url跳转到新 URL新增记录常规导航
location.assign(url)同 href新增记录与 href 等效,更语义化
location.replace(url)替换当前页面不新增记录避免返回原页面(如登录后替换登录页)
location.reload(force)重新加载无变化刷新页面,force=true 强制从服务器加载
location.hash = 'id'跳转到锚点新增记录(部分浏览器)页面内导航

三、history 对象:历史记录管理

history 对象提供对浏览器历史记录栈的访问,是 SPA 路由实现的核心,通过它可在不刷新页面的情况下修改 URL。

1. 核心方法与事件

pushState 与 replaceState
// 添加新历史记录(不刷新页面)
history.pushState(
  { page: 'product', id: 123 }, // state 对象(可存储页面状态)
  '产品详情页', // 标题(多数浏览器忽略)
  '/product?id=123' // 新 URL(必须与当前 URL 同源)
);

// 替换当前历史记录
history.replaceState(
  { page: 'product', id: 123, filter: 'new' },
  '产品详情页(筛选)',
  '/product?id=123&filter=new'
);

state 对象注意事项

  • 会被序列化存储,建议仅存简单数据(字符串、数字、数组、对象)
  • 大小限制:不同浏览器限制不同(通常 64KB 左右)
  • 刷新页面后需重新初始化(state 会丢失)
popstate 事件

仅在通过浏览器前进/后退按钮或 history.back()/forward()/go() 触发历史记录变化时触发:

window.addEventListener('popstate', (event) => {
  console.log('历史记录变化:', event.state); 
  // 根据 state 数据更新页面内容(SPA 路由核心逻辑)
  if (event.state?.page === 'product') {
    renderProductPage(event.state.id);
  }
});

常见误区pushState 和 replaceState 不会触发 popstate 事件,需手动同步页面状态。

2. SPA 路由应用示例

// 路由配置
const routes = {
  '/': () => renderHome(),
  '/about': () => renderAbout(),
  '/product/:id': (params) => renderProduct(params.id)
};

// 初始化路由
function initRouter() {
  // 处理初始 URL
  handleRoute();
  
  // 监听历史记录变化
  window.addEventListener('popstate', handleRoute);
}

// 处理路由
function handleRoute() {
  const path = location.pathname;
  // 匹配路由并执行对应渲染函数
  for (const [route, handler] of Object.entries(routes)) {
    const match = path.match(parseRoute(route));
    if (match) {
      handler(match.groups); // 传递参数
      return;
    }
  }
  // 404 页面
  renderNotFound();
}

// 跳转路由(不刷新页面)
function navigateTo(url, state = {}) {
  history.pushState(state, '', url);
  handleRoute(); // 手动触发路由更新
}

// 初始化
initRouter();

四、navigator 对象:浏览器与设备信息

navigator 对象提供浏览器和设备的详细信息,常用于特性检测、用户分析和兼容性处理。

1. 实用属性扩展

// 检测 Cookie 是否启用
console.log('Cookie 启用:', navigator.cookieEnabled);

// 设备内存(GB,近似值)
console.log('设备内存:', navigator.deviceMemory); 

// CPU 核心数
console.log('CPU 核心数:', navigator.hardwareConcurrency); 

// 检测是否支持触摸
console.log('支持触摸:', 'ontouchstart' in window);

// 检测是否支持 WebGL
console.log('支持 WebGL:', 'WebGLRenderingContext' in window);

// 浏览器语言(用户首选语言)
console.log('首选语言:', navigator.language); // 如 "zh-CN"
console.log('所有语言:', navigator.languages); // 如 ["zh-CN", "en-US"]

// 浏览器是否处于在线状态
console.log('是否在线:', navigator.onLine);
window.addEventListener('online', () => console.log('恢复在线'));
window.addEventListener('offline', () => console.log('已离线'));

2. 地理定位高级用法

除单次获取位置,还可持续监听位置变化:

// 持续监听位置
let watchId;
if (navigator.geolocation) {
  watchId = navigator.geolocation.watchPosition(
    (position) => {
      console.log('当前位置:', {
        lat: position.coords.latitude,
        lng: position.coords.longitude,
        accuracy: position.coords.accuracy // 精度(米)
      });
    },
    (error) => {
      console.error('位置错误:', error.message);
      // 错误类型:PERMISSION_DENIED(1)、POSITION_UNAVAILABLE(2)、TIMEOUT(3)
    },
    {
      enableHighAccuracy: false, // 高精度模式(更耗电)
      timeout: 5000, // 超时时间(毫秒)
      maximumAge: 300000 // 位置缓存有效期(5分钟)
    }
  );
}

// 停止监听
// navigator.geolocation.clearWatch(watchId);

五、screen 对象:屏幕信息

screen 对象提供用户设备屏幕的硬件信息,常用于响应式布局和适配不同设备。

关键属性解析

属性含义注意事项
width/height屏幕总宽高(像素)含任务栏/Dock 栏区域
availWidth/availHeight可用宽高(像素)不含任务栏/Dock 栏,实际可显示区域
colorDepth颜色深度(位/像素)通常为 24 或 32(真彩色)
pixelDepth像素深度(位/像素)与 colorDepth 通常一致,表示屏幕硬件像素密度
orientation屏幕方向返回 ScreenOrientation 对象,含 angle(旋转角度)和 type(方向类型)

示例:检测屏幕方向变化

screen.orientation.addEventListener('change', () => {
  console.log('屏幕方向:', screen.orientation.type); // 如 "landscape-primary"
  console.log('旋转角度:', screen.orientation.angle);
});

六、弹出框与用户交互

BOM 提供的原生弹出框功能简单但有诸多限制,需合理使用。

原生弹出框的局限性

  1. 样式不可定制:完全由浏览器控制,无法适配页面风格。
  2. 阻塞执行:弹出框显示时,JavaScript 暂停执行,影响用户体验。
  3. 无障碍性问题:部分屏幕阅读器支持有限,且无法添加辅助标签。
  4. 浏览器限制:可能被广告拦截器阻止,或在 iframe 中受限。

现代替代方案:使用自定义模态框(如基于 Bootstrap、React Modal 等库),结合 Promise 实现异步交互:

// 自定义 confirm 替代方案
function customConfirm(message) {
  return new Promise((resolve) => {
    // 创建模态框 DOM 并显示
    const modal = createModal(message);
    modal.confirmBtn.addEventListener('click', () => {
      resolve(true);
      modal.remove();
    });
    modal.cancelBtn.addEventListener('click', () => {
      resolve(false);
      modal.remove();
    });
  });
}

// 使用
customConfirm('确定要删除吗?').then((confirmed) => {
  if (confirmed) {
    // 执行删除操作
  }
});

七、定时器与性能优化

定时器是 BOM 中处理异步任务的核心工具,但使用不当会导致性能问题。

定时器精度与替代方案

精度问题
  • 浏览器的定时器延迟并非精确值,受 JavaScript 线程阻塞、浏览器休眠等影响。
  • setInterval 存在累积误差:若回调执行时间超过间隔,会导致回调叠加执行。

优化方案:使用 setTimeout 递归替代 setInterval

// 更可靠的重复执行
function repeatTask(fn, interval) {
  let lastTime = Date.now();
  
  function execute() {
    const now = Date.now();
    const delay = Math.max(0, interval - (now - lastTime)); // 修正延迟
    lastTime = now;
    fn();
    timeoutId = setTimeout(execute, delay);
  }
  
  let timeoutId = setTimeout(execute, interval);
  return () => clearTimeout(timeoutId); // 返回取消函数
}
动画专用定时器

requestAnimationFrame 是浏览器提供的动画优化 API,与屏幕刷新率同步(通常 60 次/秒):

function animate() {
  // 更新动画状态
  element.style.left = `${x}px`;
  x++;
  
  // 继续下一帧
  if (x < 100) {
    requestId = requestAnimationFrame(animate);
  }
}

// 启动动画
let requestId = requestAnimationFrame(animate);

// 停止动画
// cancelAnimationFrame(requestId);

优势

  • 性能更好:浏览器后台标签页时会暂停,节省资源。
  • 更流畅:与屏幕刷新同步,避免卡顿。

八、BOM 事件与页面生命周期

BOM 事件涵盖页面加载、可见性、尺寸变化等场景,合理监听可提升用户体验。

关键事件扩展

页面可见性(visibilitychange

监听页面是否处于用户可见状态(如切换标签页):

document.addEventListener('visibilitychange', () => {
  if (document.hidden) {
    // 页面不可见(暂停视频、减少请求)
    video.pause();
  } else {
    // 页面可见(恢复播放、刷新数据)
    video.play();
    fetchNewData();
  }
});
页面缓存与加载(pageshow/pagehide
  • pageshow:页面加载时触发(包括从缓存加载)。
  • pagehide:页面卸载时触发(包括进入缓存)。
window.addEventListener('pageshow', (event) => {
  // 检测是否从缓存加载(如浏览器后退按钮)
  if (event.persisted) {
    console.log('页面从缓存加载');
    // 刷新数据,避免显示旧内容
    refreshData();
  }
});
窗口大小变化防抖

resize 事件会频繁触发(如用户拖拽窗口),需用防抖优化:

function debounce(fn, delay = 100) {
  let timeoutId;
  return (...args) => {
    clearTimeout(timeoutId);
    timeoutId = setTimeout(() => fn.apply(this, args), delay);
  };
}

window.addEventListener('resize', debounce(() => {
  console.log('窗口尺寸稳定后执行:', window.innerWidth);
}));

九、BOM 安全限制与最佳实践

浏览器对 BOM 操作施加了严格限制,以保护用户安全和隐私。

核心安全限制

  1. 同源策略:不同源的窗口间无法访问彼此的 window 属性(除非通过 postMessage)。

    // 跨源通信示例(父窗口向子窗口发送消息)
    const iframe = document.querySelector('iframe');
    iframe.contentWindow.postMessage('Hello', 'https://trusted-domain.com');
    
    // 子窗口接收消息
    window.addEventListener('message', (event) => {
      // 验证发送方来源
      if (event.origin !== 'https://trusted-domain.com') return;
      console.log('收到消息:', event.data);
      // 回复消息
      event.source.postMessage('Hi there', event.origin);
    });
    
  2. 弹窗限制:非用户交互触发的 window.open 会被阻止(如 load 事件中)。

    // 正确做法:仅在用户点击时打开弹窗
    document.getElementById('openBtn').addEventListener('click', () => {
      window.open('https://example.com'); // 允许执行
    });
    
  3. 权限控制:地理定位、全屏等 API 需要用户明确授权,且可能被拒绝。

最佳实践

  1. 避免滥用 BOM 操作:如无必要,不修改窗口大小、位置,不依赖原生弹出框。
  2. 特性检测优先:使用 if ('feature' in object) 判断 API 支持,而非浏览器嗅探。
  3. 性能优化:对 resizescroll 等高频事件使用防抖/节流;动画优先用 requestAnimationFrame
  4. 兼容处理:对旧浏览器(如 IE)使用前缀方法(webkitRequestFullscreen),或通过工具库(如 Modernizr)简化检测。
  5. 安全防护:跨源通信时验证 origin;防御点击劫持(检测 window.top);避免在 state 中存储敏感信息。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值