【WEB】BOM 浏览器对象模型解析(二)

一、history 对象:浏览历史管理

history 对象用于操作浏览器的会话历史记录,可实现前进、后退等导航功能,是单页应用(SPA)的核心技术之一。

1.1 基础历史操作

history 对象提供了控制历史记录的基础方法:

方法说明示例
back()后退到上一页(等价于浏览器的后退按钮)history.back()
forward()前进到下一页(等价于浏览器的前进按钮)history.forward()
go(n)跳转到历史记录中第 n 个页面(n 为整数)history.go(-2)(后退2页)
length历史记录的长度(只读)console.log(history.length)

代码示例:基础历史导航

<button id="btnBack">后退</button>
<button id="btnForward">前进</button>
<button id="btnGoBack2">后退2页</button>
<button id="btnShowLength">显示历史长度</button>

<script>
  document.getElementById("btnBack").addEventListener("click", () => {
    history.back();
  });

  document.getElementById("btnForward").addEventListener("click", () => {
    history.forward();
  });

  document.getElementById("btnGoBack2").addEventListener("click", () => {
    // 若历史记录不足2条,无效果
    history.go(-2);
  });

  document.getElementById("btnShowLength").addEventListener("click", () => {
    alert(`历史记录长度:${history.length}`);
  });
</script>

1.2 HTML5 扩展:pushState 与 replaceState

HTML5 新增了 pushStatereplaceState 方法,允许在不刷新页面的情况下修改 URL 并添加历史记录,是单页应用实现无刷新导航的核心。

1.2.1 方法语法

history.pushState(state, title, url);
history.replaceState(state, title, url);
  • state:状态对象(会被存储在历史记录中,可通过 popstate 事件获取)
  • title:页面标题(多数浏览器忽略此参数,建议传空字符串)
  • url:新 URL(必须与当前页面同源,否则报错)

区别

  • pushState:添加新的历史记录条目
  • replaceState:替换当前历史记录条目(不增加历史长度)

代码示例:无刷新修改 URL

<div id="content">首页内容</div>
<button id="btnPage1">跳转到页面1</button>
<button id="btnPage2">跳转到页面2</button>

<script>
  const content = document.getElementById("content");

  // 跳转到页面1(pushState)
  document.getElementById("btnPage1").addEventListener("click", () => {
    // 添加新历史记录,URL 改为 /page1
    history.pushState(
      { page: 1, title: "页面1" }, // state对象
      "", // title(忽略)
      "/page1" // URL
    );
    // 更新页面内容(模拟页面切换)
    content.textContent = "页面1内容";
  });

  // 跳转到页面2(replaceState)
  document.getElementById("btnPage2").addEventListener("click", () => {
    // 替换当前历史记录,URL 改为 /page2
    history.replaceState(
      { page: 2, title: "页面2" },
      "",
      "/page2"
    );
    content.textContent = "页面2内容";
  });

  // 监听历史记录变化(用户点击前进/后退按钮时触发)
  window.addEventListener("popstate", (event) => {
    console.log("历史记录变化,当前state:", event.state);
    // 根据 state 更新页面内容
    if (event.state) {
      content.textContent = `${event.state.title}内容`;
    } else {
      // 若 state 为 null,显示首页
      content.textContent = "首页内容";
    }
  });
</script>

1.2.2 popstate 事件

popstate 事件在用户点击前进/后退按钮或调用 history.back()/forward()/go() 时触发,事件对象的 state 属性包含对应历史记录的 state 对象。

注意:调用 pushStatereplaceState 不会触发 popstate 事件,仅当用户主动改变历史记录时触发。

二、BOM 弹窗与窗口控制

BOM 提供了多种弹窗方式和窗口控制方法,用于与用户交互或管理浏览器窗口(注意:现代浏览器对弹窗有严格限制,避免滥用)。

2.1 原生弹窗:alert、confirm、prompt

浏览器内置了三种简单弹窗,用于基础交互:

2.1.1 alert():提示框

语法:window.alert(message)


alert("这是一个提示框");
// 示例:操作成功提示
function saveData() {
  // 模拟保存数据
  const isSuccess = true;
  if (isSuccess) {
    alert("数据保存成功!");
  }
}

在这里插入图片描述

特点

  • 只有一个“确定”按钮
  • 会阻塞代码执行(直到用户点击确定)
  • 样式由浏览器控制,无法自定义

2.1.2 confirm():确认框

语法:const result = window.confirm(message)

const agree = confirm("是否删除这条数据?");
if (agree) {
  console.log("用户点击了确定,执行删除");
} else {
  console.log("用户点击了取消,取消删除");
}

在这里插入图片描述

特点

  • 有“确定”和“取消”两个按钮
  • 返回布尔值(确定为 true,取消为 false)
  • 同样阻塞代码执行

2.1.3 prompt():输入框

语法:const input = window.prompt(message, defaultValue)

const username = prompt("请输入用户名:", "admin");
if (username !== null) { // 用户点击确定(取消返回 null)
  console.log("用户输入:", username);
} else {
  console.log("用户取消输入");
}

在这里插入图片描述

特点

  • 包含输入框、“确定”和“取消”按钮
  • 第二个参数为输入框默认值(可选)
  • 返回用户输入的字符串(取消返回 null)

2.2 窗口控制:open() 与 close()

window.open() 用于打开新窗口,window.close() 用于关闭窗口(通常只能关闭由脚本打开的窗口)。

2.2.1 open() 方法详解

const newWindow = window.open(url, name, features);
  • url:新窗口的 URL(为空则打开空白页)
  • name:窗口名称(或目标framename,如 “_blank” 表示新窗口)
  • features:窗口特性字符串(逗号分隔,如 width=500,height=300

常用 features 参数

参数说明示例值
width窗口宽度(像素)width=800
height窗口高度(像素)height=600
left窗口左上角x坐标(像素)left=100
top窗口左上角y坐标(像素)top=100
toolbar是否显示工具栏(yes/no)toolbar=no
menubar是否显示菜单栏menubar=no
scrollbars是否显示滚动条scrollbars=yes
resizable窗口是否可调整大小resizable=yes
status是否显示状态栏status=no

代码示例:打开自定义窗口

function openCustomWindow() {
  const url = "https://example.com";
  const name = "exampleWindow";
  // 窗口特性:宽800,高600,距离屏幕左、上各100px,无工具栏和菜单栏
  const features = "width=800,height=600,left=100,top=100,toolbar=no,menubar=no,scrollbars=yes,resizable=yes";
  
  const newWin = window.open(url, name, features);
  
  // 若浏览器拦截弹窗
  if (!newWin) {
    alert("弹窗被浏览器拦截,请允许弹窗后重试");
  } else {
    // 2秒后聚焦新窗口
    setTimeout(() => {
      newWin.focus();
    }, 2000);
  }
}

2.2.2 close() 方法

// 关闭当前窗口(通常被浏览器禁止,除非窗口由脚本打开)
window.close();

// 关闭由脚本打开的窗口
const newWin = window.open("https://example.com");
setTimeout(() => {
  newWin.close(); // 有效
}, 5000);

注意:现代浏览器通常禁止脚本关闭非脚本打开的窗口(如用户手动打开的标签页)。

三、定时器:setTimeout 与 setInterval

定时器是 BOM 提供的核心功能,用于延迟执行或重复执行代码,广泛应用于动画、轮询等场景。

3.1 setTimeout:延迟执行一次

setTimeout 用于在指定毫秒数后执行一次函数。

3.1.2 基本语法

语法:const timerId = setTimeout(callback, delay, arg1, arg2, ...)

const timerId = setTimeout(() => {
  console.log("2秒后执行");
}, 2000);

// 带参数的回调
setTimeout((name, age) => {
  console.log(`Hello, ${name}!你${age}岁了`);
}, 1000, "BOM", 5); 
// 输出:"Hello, BOM!你5岁了"
  • callback:延迟后执行的函数
  • delay:延迟时间(毫秒,默认0)
  • 后续参数:传递给回调函数的参数
  • 返回值:定时器 ID(用于清除定时器)

3.1.2 清除定时器:clearTimeout

// 启动定时器
const timerId = setTimeout(() => {
  console.log("这个不会执行");
}, 1000);

// 清除定时器(在执行前)
clearTimeout(timerId);
console.log("定时器已清除");

应用场景:按钮防重复点击

<button id="submitBtn">提交</button>

<script>
  const btn = document.getElementById("submitBtn");
  let isClicked = false;

  btn.addEventListener("click", () => {
    if (isClicked) return;
    isClicked = true;
    btn.disabled = true;
    btn.textContent = "提交中...";

    // 模拟接口请求
    setTimeout(() => {
      console.log("提交成功");
      btn.textContent = "提交";
      btn.disabled = false;
      isClicked = false;
    }, 2000);
  });
</script>

3.2 setInterval:重复执行

setInterval 用于每隔指定毫秒数重复执行函数。

3.2.1 基本语法

语法:const timerId = setInterval(callback, interval, arg1, ...)

let count = 0;
const timerId = setInterval(() => {
  count++;
  console.log(`已执行${count}`);
  // 执行5次后停止
  if (count >= 5) {
    clearInterval(timerId);
    console.log("定时器已停止");
  }
}, 1000);
  • setTimeout 语法类似,但会重复执行
  • 同样返回定时器 ID,用于清除

3.2.2 清除定时器:clearInterval

let seconds = 0;
const timerId = setInterval(() => {
  seconds++;
  console.log(`已过去${seconds}`);
}, 1000);

// 5秒后停止计时器
setTimeout(() => {
  clearInterval(timerId);
  console.log("计时器已停止");
}, 5000);

应用场景:倒计时

<div id="countdown">10</div>

<script>
  const countdownEl = document.getElementById("countdown");
  let remaining = 10;

  const timerId = setInterval(() => {
    remaining--;
    countdownEl.textContent = remaining;
    if (remaining <= 0) {
      clearInterval(timerId);
      countdownEl.textContent = "时间到!";
    }
  }, 1000);
</script>

3.3 定时器的this指向问题

定时器回调函数中的 this 默认指向 window(非严格模式)或 undefined(严格模式),需注意绑定正确的上下文。

解决方案

  1. 使用箭头函数(继承外部 this
  2. 使用 bind() 绑定 this
const obj = {
  name: "BOM",
  // 方法1:箭头函数
  startTimer1() {
    setTimeout(() => {
      console.log(this.name); // 输出:"BOM"(this指向obj)
    }, 1000);
  },
  // 方法2:bind绑定
  startTimer2() {
    setTimeout(function() {
      console.log(this.name); // 输出:"BOM"
    }.bind(this), 1000);
  }
};

obj.startTimer1();
obj.startTimer2();

四、本地存储:localStorage 与 sessionStorage

Web Storage API(localStoragesessionStorage)是 BOM 提供的本地数据存储方案,用于在浏览器中存储键值对数据(替代传统的 cookie)。

4.1 两者的区别

特性localStoragesessionStorage
生命周期永久存储(除非手动删除或清除浏览器数据)仅在当前会话有效(关闭标签页后删除)
作用域同源下所有标签页/窗口共享仅当前标签页/窗口(即使同源)
存储容量约 5-10MB约 5-10MB
数据共享同源页面可共享不可跨标签页共享

4.2 核心方法与属性

两者的 API 完全一致:

方法/属性说明
setItem(key, value)存储数据(value 必须为字符串)
getItem(key)获取数据(返回字符串,不存在则为 null)
removeItem(key)删除指定键的数据
clear()清空所有数据
length存储的键值对数量
key(index)获取指定索引的键名

代码示例:localStorage 基本操作

// 存储数据
localStorage.setItem("username", "BOM");
localStorage.setItem("age", "10"); // 数字也会被转为字符串

// 获取数据
const username = localStorage.getItem("username");
console.log(username); // 输出:"BOM"

// 存储对象(需先转为字符串)
const user = { name: "BOM", skills: ["JS", "HTML"] };
localStorage.setItem("user", JSON.stringify(user));

// 获取对象(需解析)
const storedUser = JSON.parse(localStorage.getItem("user"));
console.log(storedUser.skills); // 输出:["JS", "HTML"]

// 删除数据
localStorage.removeItem("age");

// 清空所有数据(谨慎使用)
// localStorage.clear();

// 遍历所有数据
for (let i = 0; i < localStorage.length; i++) {
  const key = localStorage.key(i);
  const value = localStorage.getItem(key);
  console.log(`${key}: ${value}`);
}

注意

  • 仅支持字符串存储,复杂类型需用 JSON.stringify 转换
  • 不要存储敏感信息(明文存储,易被获取)
  • 存储大量数据可能影响页面性能

4.3 storage 事件

localStorage 的数据发生变化时(新增、修改、删除),同源下的其他页面会触发 storage 事件(当前页面不会触发)。

代码示例:监听存储变化

// 在页面 A中监听
window.addEventListener("storage", (event) => {
  console.log("存储发生变化:");
  console.log("旧值:", event.oldValue);
  console.log("新值:", event.newValue);
  console.log("键名:", event.key);
  console.log("源地址:", event.url); // 触发变化的页面URL
});

// 在页面 B中修改数据(会触发页面A的storage事件)
localStorage.setItem("username", "NewBOM");

五、BOM 事件:监听浏览器行为

BOM 提供了一系列事件用于监听浏览器窗口的状态变化,如窗口大小改变、滚动、关闭等。

5.1 resize:窗口大小变化

resize 事件在窗口大小改变时触发(包括浏览器窗口缩放、最大化/最小化)。

代码示例:响应窗口大小变化

<div id="sizeInfo">窗口大小:宽 × 高</div>

<script>
  const sizeInfo = document.getElementById("sizeInfo");

  // 更新尺寸信息的函数
  function updateSize() {
    const width = window.innerWidth;
    const height = window.innerHeight;
    sizeInfo.textContent = `窗口大小:${width} × ${height}`;
  }

  // 初始调用
  updateSize();

  // 监听窗口大小变化
  window.addEventListener("resize", updateSize);
</script>

优化:频繁触发的事件(如 resize)可使用节流(throttle)优化性能:

// 节流函数(限制事件触发频率)
function throttle(func, delay = 100) {
  let lastTime = 0;
  return (...args) => {
    const now = Date.now();
    if (now - lastTime >= delay) {
      func.apply(this, args);
      lastTime = now;
    }
  };
}

// 使用节流后的函数监听resize
window.addEventListener("resize", throttle(updateSize, 200)); // 每200ms最多执行一次

5.2 scroll:页面滚动

scroll 事件在页面或元素滚动时触发,常用于实现滚动加载、回到顶部按钮等功能。

代码示例:滚动时显示回到顶部按钮

<button id="backToTop" style="position: fixed; bottom: 20px; right: 20px; display: none;">回到顶部</button>

<script>
  const backToTopBtn = document.getElementById("backToTop");

  // 监听页面滚动
  window.addEventListener("scroll", () => {
    // 获取滚动距离(文档顶部到可视区顶部)
    const scrollTop = window.pageYOffset || document.documentElement.scrollTop;

    // 滚动超过300px时显示按钮
    if (scrollTop > 300) {
      backToTopBtn.style.display = "block";
    } else {
      backToTopBtn.style.display = "none";
    }
  });

  // 回到顶部功能
  backToTopBtn.addEventListener("click", () => {
    // 平滑滚动到顶部
    window.scrollTo({
      top: 0,
      behavior: "smooth"
    });
  });
</script>

六、BOM 兼容性与最佳实践

6.1 兼容性处理

虽然主流浏览器对 BOM 核心 API 支持良好,但部分旧浏览器(如 IE8 及以下)存在差异:

  • addEventListener 在 IE8 中不支持,需使用 attachEvent
  • localStorage 在 IE7 及以下不支持
  • geolocation 在 IE9 及以下不支持
  • history.pushState 在 IE9 及以下不支持

兼容写法示例(事件监听)

function addEvent(element, event, handler) {
  if (element.addEventListener) {
    element.addEventListener(event, handler);
  } else if (element.attachEvent) { // IE8及以下
    element.attachEvent("on" + event, handler);
  } else {
    element["on" + event] = handler;
  }
}

// 使用
addEvent(window, "resize", () => {
  console.log("窗口大小变化");
});

6.2 最佳实践

  1. 避免滥用弹窗:alert/confirm/prompt 体验差,推荐使用自定义弹窗组件
  2. 及时清除定时器:组件卸载或页面关闭前清除定时器,避免内存泄漏
  3. 慎用 evalwindow.eval 执行字符串代码,存在安全风险和性能问题
  4. 本地存储安全:不存储敏感信息(如密码),敏感数据需加密
  5. 特性检测优先:用 typeofin 检测 API 是否存在,而非浏览器检测
  6. 防抖节流:对 resize、scroll 等高频事件使用防抖或节流优化性能
  7. 历史记录管理:SPA 中使用 pushState 时,确保服务器能正确处理新 URL 的请求

掌握 BOM 是前端开发的基础,合理使用 BOM 可以显著提升用户体验,但需注意兼容性和性能问题,遵循最佳实践。

BOM 与 DOM 共同构成了前端开发的核心基础,深入理解两者的协同工作机制,将为构建复杂交互的 web 应用奠定坚实基础。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值