跳到主要内容

beforeunload

2025年01月07日
柏拉文
越努力,越幸运

一、认识


beforeunload 是浏览器提供的一种事件,能够在页面即将被卸载(例如刷新、关闭、导航离开当前页面)时触发。这个事件允许开发者在用户离开页面之前给出一个提示,警告用户如果继续操作可能会丢失正在进行的操作,例如未保存的表单数据。

当触发 beforeunload 事件时,浏览器通常会显示一个警告对话框,询问用户是否确认离开当前页面。如果用户选择继续(通常是“离开”或“确定”),页面会被关闭或刷新。如果用户选择取消(通常是“取消”),则会停止当前页面的卸载操作。注意:

  1. 现代浏览器出于用户体验和防止恶意行为的考虑,通常会显示一个标准的警告信息,而不会显示开发者定义的自定义消息。 beforeunload 事件只能在浏览器离开页面时触发,不能在页面加载时或者 JavaScript 代码执行时主动触发。

  2. beforeunload 事件只能在浏览器离开页面时触发,不能在页面加载时或者 JavaScript 代码执行时主动触发。

beforeunload 触发时机:

  1. 用户刷新页面

  2. 用户关闭浏览器或标签页

  3. 用户点击浏览器的 后退前进 按钮

  4. 用户通过 JavaScript 跳转到其他页面(例如使用 window.location)。

二、语法


window.addEventListener('beforeunload', function(event) {
// 通过设置 event.returnValue 来触发浏览器的提示框
event.returnValue = "您有未保存的更改,确认离开吗?";
// 返回值是旧版浏览器的做法,现代浏览器会忽略它
return "您有未保存的更改,确认离开吗?";
});

在现代浏览器中,beforeunload 事件的 event.returnValue 属性必须被赋值为一个字符串,才能让浏览器显示警告框。但是,浏览器通常会 忽略 这个字符串的内容,显示一个标准的警告信息,防止恶意或过度使用这种提示。

三、场景


3.1 HTML

beforeunload.js

class BeforeUnloadHandler {
constructor() {
if (BeforeUnloadHandler.instance) {
return BeforeUnloadHandler.instance; // 如果已经存在实例,直接返回已有实例
}
this.isListening = false;
BeforeUnloadHandler.instance = this; // 将实例存储在单例中
}

// 事件处理函数
handleBeforeUnload = (event) => {
if (this.isListening) {
const message = "数据正在保存,您确定要离开吗?";
event.returnValue = message; // 现代浏览器
return message; // 旧版浏览器
}
};

// 开始监听 beforeunload 事件
start() {
if (!this.isListening) {
window.addEventListener("beforeunload", this.handleBeforeUnload);
this.isListening = true;
}
}

// 停止监听 beforeunload 事件
end() {
if (this.isListening) {
window.removeEventListener("beforeunload", this.handleBeforeUnload);
this.isListening = false;
}
}
}

// 导出单例实例
const beforeUnloadHandler = new BeforeUnloadHandler();
export default beforeUnloadHandler;

index.js

import beforeUnloadHandler from "./beforeunload.js";

function handleClick() {
beforeUnloadHandler.start();

console.log("正在请求中……");

setTimeout(() => {
console.log("请求成功!");
beforeUnloadHandler.end();
}, 10000);
}

const buttonEl = document.querySelector("button");
buttonEl.addEventListener("click", handleClick);

3.2 React

useBeforeunload.tsx

import { useState, useEffect, useCallback } from 'react';

function useBeforeunload() {
const [isListening, setIsListening] = useState(false);

const handleBeforeUnload = useCallback((event: BeforeUnloadEvent) => {
if (isListening) {
const message = '数据正在保存,您确定要离开吗?';
event.returnValue = message; // 现代浏览器
return message; // 旧版浏览器
}
}, [isListening]);

useEffect(() => {
if (isListening) {
window.addEventListener('beforeunload', handleBeforeUnload);
} else {
window.removeEventListener('beforeunload', handleBeforeUnload);
}

return () => {
window.removeEventListener('beforeunload', handleBeforeUnload);
};
}, [isListening, handleBeforeUnload]);

return {
start: () => setIsListening(true),
end: () => setIsListening(false),
};
}

export default useBeforeunload;

App.tsx

import useBeforeunload from "./hooks/useBeforeunload";

function App() {
const beforeunloadEvent = useBeforeunload();

const handleClick = () => {
beforeunloadEvent.start();
console.log("正在请求中……");
setTimeout(() => {
console.log("请求成功!");
beforeunloadEvent.end();
}, 10000);
};

return (
<div>
<button onClick={handleClick}>提交</button>
</div>
);
}

export default App;