如何使用JS判斷用戶是否處于閑置狀態(tài)
最近在項(xiàng)目中遇到一個(gè)需求:需要監(jiān)控用戶是否開著頁面但是沒有操作,或者開了頁面但已經(jīng)離開電腦,發(fā)現(xiàn)了瀏覽器原生提供了專有的 API: IdleDetector(空閑檢測(cè)器)。當(dāng)然我們也可以自行實(shí)現(xiàn),我們分別來聊聊這兩種方式:
什么是 IdleDetector?
IdleDetector 是瀏覽器提供的一個(gè)實(shí)驗(yàn)性 API,用于檢測(cè)用戶的閑置狀態(tài)。它可以監(jiān)控以下兩種狀態(tài):
- 用戶狀態(tài)(
user):用戶是否在電腦前(通過鍵盤、鼠標(biāo)等交互判斷)。 - 屏幕狀態(tài)(
screen):屏幕是否被鎖定(例如屏幕保護(hù)程序啟動(dòng)或電腦休眠)。
目前,IdleDetector 在 Chrome 94+ 、Edge 94+ 中都支持,Firefox 和 Safari 尚未支持。但是 IdleDetector 仍為實(shí)驗(yàn)性 API,未來可能有變化,且在非 HTTPS 環(huán)境下無法使用。
底層原理
IdleDetector 通過瀏覽器底層機(jī)制檢測(cè)用戶和屏幕狀態(tài),開發(fā)者只需設(shè)置閾值并監(jiān)聽狀態(tài)變化即可。API 會(huì)根據(jù)用戶交互和系統(tǒng)狀態(tài)自動(dòng)判斷是否進(jìn)入閑置。
使用要注意
IdleDetector 需要用戶授權(quán),權(quán)限請(qǐng)求可通過以下兩種方式觸發(fā):
- 顯式請(qǐng)求:調(diào)用
IdleDetector.requestPermission(),需在用戶手勢(shì)(如點(diǎn)擊)中觸發(fā)。 - 隱式請(qǐng)求:直接調(diào)用
detector.start(),瀏覽器會(huì)自動(dòng)彈出權(quán)限提示。
如果正確被調(diào)用會(huì)有這樣的提示:

參考以下實(shí)現(xiàn)來調(diào)用:
async function startIdleDetection(threshold = 60000) {
if (!window.IdleDetector) {
console.error('瀏覽器不支持 IdleDetector API');
return { success: false, error: 'API_NOT_SUPPORTED' };
}
try {
// 至少要60秒, 任何小于 60 秒的設(shè)置會(huì)導(dǎo)致 start() 拋出 DOMException
const validatedThreshold = Math.max(threshold, 60000);
const state = await IdleDetector.requestPermission();
if (state !== 'granted') {
console.error('用戶拒絕了空閑檢測(cè)權(quán)限');
return { success: false, error: 'PERMISSION_DENIED' };
}
const detector = new IdleDetector();
// 添加更健壯的事件處理
const handleStateChange = () => {
const now = new Date().toISOString();
console.log(`[${now}] 用戶狀態(tài): ${detector.userState}, 屏幕狀態(tài): ${detector.screenState}`);
// 實(shí)際業(yè)務(wù)邏輯可以在這里添加
if (detector.userState === 'idle') {
// 用戶閑置處理
}
};
detector.addEventListener('change', handleStateChange);
// 添加錯(cuò)誤處理
detector.addEventListener('error', (err) => {
console.error('檢測(cè)器錯(cuò)誤:', err);
});
await detector.start({ threshold: validatedThreshold });
return { success: true, detector }; // 返回檢測(cè)器實(shí)例以便后續(xù)控制
} catch (err) {
console.error('空閑檢測(cè)初始化失敗:', err);
return { success: false, error: err.message };
}
}
// 正確用法示例(必須在用戶交互中調(diào)用):
document.getElementById('startBtn').addEventListener('click', async () => {
const result = await startIdleDetection(90000); // 90秒閾值
if (!result.success) {
// 處理失敗情況
}
});
如果用戶拒絕了怎么辦了?我們要有備選方案:
自行實(shí)現(xiàn)用戶閑置檢測(cè)
通過監(jiān)聽一系列瀏覽器事件,判斷用戶是否處于活躍狀態(tài)。核心事件包括:
visibilitychange:檢測(cè)頁面是否可見(例如用戶切換標(biāo)簽頁或最小化窗口)。mousemove/mousedown/keydown/touchstart等事件:檢測(cè)用戶是否與頁面交互。focus/blur:檢測(cè)窗口是否獲得焦點(diǎn)。
通過設(shè)置一個(gè)定時(shí)器,當(dāng)用戶超過一定時(shí)間未觸發(fā)上述事件時(shí),判定為閑置狀態(tài)。
示例代碼:
class IdleMonitor {
constructor(idleTimeout = 60000) {
this.idleTimeout = idleTimeout; // 閑置超時(shí)時(shí)間(毫秒)
this.timer = null;
this.isIdle = false;
this.handleActivity = this.handleActivity.bind(this);
this.checkIdle = this.checkIdle.bind(this);
this.init();
}
init() {
// 監(jiān)聽用戶交互事件
['mousemove', 'mousedown', 'keydown', 'touchstart'].forEach(event => {
window.addEventListener(event, this.handleActivity);
});
// 監(jiān)聽頁面可見性
document.addEventListener('visibilitychange', this.handleActivity);
// 啟動(dòng)定時(shí)器
this.startTimer();
}
handleActivity() {
if (this.isIdle) {
this.isIdle = false;
console.log('用戶恢復(fù)活躍');
}
this.startTimer();
}
startTimer() {
clearTimeout(this.timer);
this.timer = setTimeout(this.checkIdle, this.idleTimeout);
}
checkIdle() {
this.isIdle = true;
console.log('用戶進(jìn)入閑置狀態(tài)');
}
destroy() {
clearTimeout(this.timer);
['mousemove', 'mousedown', 'keydown', 'touchstart'].forEach(event => {
window.removeEventListener(event, this.handleActivity);
});
document.removeEventListener('visibilitychange', this.handleActivity);
}
}
// 使用示例
const monitor = new IdleMonitor(90000); // 90秒無操作進(jìn)入閑置
這里只是舉例,所以只用了點(diǎn)擊、觸摸、鼠標(biāo)移動(dòng)這些事件,實(shí)際上要監(jiān)聽的事件可能要更多一些,如 scroll、wheel、touchmove,也可以根據(jù)業(yè)務(wù)適當(dāng)調(diào)整。但是對(duì)比原生的IdleDetector有一個(gè)弊端就是跨域的 iframe 中的無法監(jiān)聽到??缬虻?iframe 內(nèi)的交互事件(如 mousemove、keydown)不會(huì)冒泡到父頁面,但如果 iframe 和父頁面同源,可以在 iframe 內(nèi)部綁定事件監(jiān)聽并通過 postMessage 通知父頁面。實(shí)際使用的時(shí)候也可以通過防抖或節(jié)流來優(yōu)化頻繁觸發(fā)的事件比如 mousemove 降低性能開銷。
知識(shí)延展
如何使用 JS 判斷用戶是否處于活躍狀態(tài)
在 javascript 中我們可以通過監(jiān)聽某些鼠標(biāo)或鍵盤相關(guān)的事件來判定用戶是否在活躍中。
實(shí)現(xiàn)代碼
案例演示了如何獲取用戶活躍狀態(tài),時(shí)間閾值定為5秒,超出該閾值沒有操作表示非活躍,否則屬于正在活躍中
html
<p id="userState"></p>
js
//活躍狀態(tài),true活躍中,false非活躍
let state = false;
//定時(shí)器
let timer = null;
//非活躍判定閾值,5秒沒有任何活動(dòng)表示非活躍
let timeout = 5000;
//用于展示狀態(tài)信息的html元素
let userStateEl = document.getElementById('userState');
//批量添加事件監(jiān)聽
[
'mousemove', //鼠標(biāo)移動(dòng)
'mousedown', //鼠標(biāo)按下
'touchstart', //觸摸屏幕(移動(dòng)端)
'wheel', //鼠標(biāo)滾輪
'resize', //頁面尺寸變化
'keydown', //鍵盤輸入
]
.map(event =>{
window.addEventListener(event, onActive)
})
//觸發(fā)活躍中
function onActive(){
//更新狀態(tài)
state = true;
renderState();
//重置定時(shí)器
clearTimeout(timer);
timer = setTimeout(() =>{
state = false;
renderState();
}, timeout);
}
//更新狀態(tài)信息
function renderState(){
if(state){
userStateEl.textContent = "活躍中 "
} else {
userStateEl.textContent = "?非活躍狀態(tài)"
}
}
//立刻觸發(fā)一次活躍中
onActive();
總結(jié)
瀏覽器為開發(fā)者檢測(cè)用戶是否空閑提供了實(shí)驗(yàn)性的API: IdleDetector,但是需要比較新的瀏覽器并且需要用戶顯式授權(quán),如果瀏覽器不支持或者用戶拒絕了,我們也可以通過自行實(shí)現(xiàn)的方式來檢測(cè),只是有些許缺陷。
到此這篇關(guān)于如何使用JS判斷用戶是否處于閑置狀態(tài)的文章就介紹到這了,更多相關(guān)JS判斷用戶狀態(tài)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
11種JavaScript前端數(shù)據(jù)去重方式總結(jié)
這篇文章主要為大家總結(jié)了JavaScript去重的11種方式,各有優(yōu)缺點(diǎn),文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價(jià)值,需要的可以根據(jù)需求合理使用2023-06-06
全面解析JavaScript中“&&”和“||”操作符(總結(jié)篇)
這篇文章主要介紹了全面解析JavaScript中“&&”和“||”操作符(總結(jié)篇)的相關(guān)資料,需要的朋友可以參考下2016-07-07
js清空表單數(shù)據(jù)的兩種方式(遍歷+reset)
這篇文章主要介紹了js清空表單數(shù)據(jù)的兩種方式(遍歷+reset),需要的朋友可以參考下2014-07-07
ES6中的Promise.all()和Promise.race()函數(shù)的實(shí)現(xiàn)方法
這篇文章主要介紹了ES6的Promise.all()和Promise.race()函數(shù),結(jié)合實(shí)例代碼介紹了ES6 Promise.race和Promise.all方法使用,通過實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2023-02-02

