Page Visibility API 可幫助檢測用戶切換頁面,適用於考試或網課系統。本文介紹了 visibilitychange 事件和 document.visibilityState 的狀態,並提供了基本的防作弊實現方法,如定期檢查頁面可見性和監聽視頻播放狀態。 ...
Page Visibility API
在做考試系統或者網課系統的時候,通常需要監測用戶是否隱藏了當前標簽頁在看其它頁面。
Page Visibility API提供了一個事件和兩個狀態來監測頁面可見性,可以用它來判斷用戶是否切屏。
visibilitychange
這個事件會在頁面可見性變化時觸發。(隱藏時、打開時)
// 使用 addEventLisitener
document.addEventListener('visibilitychange', (e)=>{
console.log('visibilityState: ', document.visibilityState);
});
// 或者 onvisibilitychange
document.onvisibilitychange = function(){
console.log('visibilityState: ', document.visibilityState);
}
document.visibilityState
這個變數有3種值:
-
hidden
:- 瀏覽器被最小化了;
- 瀏覽器打開,但是當前看的是其它標簽頁;
- 這個標簽頁要被卸載了(unload);
-
visible
:-
當前正在看標簽頁;
-
當前瀏覽器處於最小化,但是正在預覽頁面內容;
如下圖,這種把滑鼠移到任務欄圖標上的行為也會觸發 visibilitychange 事件,且 document.visibilityState 變為 visible。
-
-
prerender
:頁面即將或正在渲染,處於不可見狀態。
document.hidden
這個狀態僅用於相容,平時應使用document.visibilityState
。當document.visibilityState
為visible
時,document.hidden
為false
,其餘情況下都為 true
。
iframe
當頁面中通過 iframe 嵌入子文檔時,iframe節點的display:none;
並不會觸發子文檔的 visibilitychange
事件。
子文檔的可見性和父文檔的可見性保持一致。
示例場景
考試違規切屏次數統計
let violationCount = 0;
const maxViolations = 3; // 設定最大違規次數
document.addEventListener('visibilitychange', function() {
if (document.visibilityState === 'visible') {
console.log('考試頁面重新可見');
} else {
violationCount++;
console.log(`考試頁面不可見,違規次數: ${violationCount}`);
// 如果違規次數達到上限,觸髮結束考試的邏輯
if (violationCount >= maxViolations) {
endExam();
} else {
alert(`警告:請勿離開考試頁面!您已違規 ${violationCount} 次,最多允許 ${maxViolations} 次違規。`);
}
}
});
function endExam() {
alert('您已多次離開考試頁面,考試結束!');
// 在此添加結束考試的邏輯,例如提交答案並退出考試界面
submitExam();
}
function submitExam() {
// 模擬提交考試結果
console.log('考試結果已提交');
// 重定向到考試結束頁面或顯示結束信息
window.location.href = '/exam-finished';
}
// 模擬考試開始
console.log('考試開始,請勿離開頁面。');
"被動"網課學習
document.addEventListener('visibilitychange', function() {
if (document.visibilityState === 'visible') {
video.play(); // 視頻播放
} else {
video.pause(); // 視頻暫停
}
});
如何避免逃課?
下麵的策略可以提高逃課的門檻(用戶可能會編寫腳本來讓視頻在頁面不可見的情況下也能播放),但是只能說是防君子不防小人。
-
setInterval
定時檢查visibilityState
,缺點是定時器在頁面不可見的情況下執行頻率和設定的時間可能不一樣。const videoElement = document.querySelector('video'); function checkVisibility() { if (document.visibilityState === 'visible') { if (videoElement.paused) { videoElement.play(); // 播放 } } else { if (!videoElement.paused) { videoElement.pause(); // 暫停 } } } // 初始檢查頁面可見性並啟動定時器 checkVisibility(); setInterval(checkVisibility, 1000); // 每秒檢查一次頁面可見性
-
監聽視頻元素的
play
事件,視頻開始播放的時候visibilityState
應為visible
,否則暫停播放。video.addEventListener('play', function() { if (document.visibilityState !== 'visible') { videoElement.pause(); // 視頻暫停 } });
-
凍結相關的屬性和方法,避免用戶重寫。
Object.defineProperty(document, 'visibilityState', { configurable: false, enumerable: true, writable: false, value: document.visibilityState }); Object.defineProperty(document, 'hidden', { configurable: false, enumerable: true, writable: false, value: document.hidden });