beforeunload
2025年01月07日
一、认识
beforeunload
是浏览器提供的一种事件,能够在页面即将被卸载(例如刷新、关闭、导航离开当前页面)时触发。这个事件允许开发者在用户离开页面之前给出一个提示,警告用户如果继续操作可能会丢失正在进行的操作,例如未保存的表单数据。
当触发 beforeunload
事件时,浏览器通常会显示一个警告对话框,询问用户是否确认离开当前页面。如果用户选择继续(通常是“离开”或“确定”),页面会被关闭或刷新。如果用户选择取消(通常是“取消”),则会停止当前页面的卸载操作。注意:
-
现代浏览器出于用户体验和防止恶意行为的考虑,通常会显示一个标准的警告信息,而不会显示开发者定义的自定义消息。
beforeunload
事件只能在浏览器离开页面时触发,不能在页面加载时或者JavaScript
代码执行时主动触发。 -
beforeunload
事件只能在浏览器离开页面时触发,不能在页面加载时或者JavaScript
代码执行时主动触发。
beforeunload
触发时机:
-
用户刷新页面
-
用户关闭浏览器或标签页
-
用户点击浏览器的 后退 或 前进 按钮
-
用户通过
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;