Существует 3 типичных метода определения того, видит ли пользователь страницу HTML, однако ни один из них не работает идеально:
Предполагается, что API видимости страницы W3C делает это (поддерживается начиная с: Firefox 10, MSIE 10, Chrome 13). Однако этот API вызывает события только тогда, когда вкладка браузера полностью переопределена (например, когда пользователь переключается с одной вкладки на другую). API не вызывает события, когда видимость не может быть определена со 100% точностью (например, Alt + Tab для переключения на другое приложение).
Использование методов фокусировки / размытия дает вам много ложных срабатываний. Например, если пользователь отображает меньшее окно в верхней части окна браузера, окно браузера потеряет фокус (onblur
поднят), но пользователь все еще сможет его увидеть (поэтому его все равно необходимо обновить). Смотри также http://javascript.info/tutorial/focus
- Опираясь на активность пользователя (движение мыши, щелчки, нажатие клавиши) также дает вам много ложных срабатываний. Подумайте о том же случае, что и выше, или о пользователе, просматривающем видео.
Чтобы улучшить несовершенное поведение, описанное выше, я использую комбинацию из 3 методов: API видимости W3C, затем методы фокусировки / размытия и пользовательской активности, чтобы уменьшить вероятность ложных срабатываний. Это позволяет управлять следующими событиями:
- Изменение вкладки браузера на другую (точность 100%, благодаря API видимости страницы W3C)
- Страница потенциально скрыта другим окном, например из-за Alt + Tab (вероятностный = не на 100% точный)
- Внимание пользователя потенциально не сосредоточено на HTML-странице (вероятностное = не на 100% точный)
Вот как это работает: когда документ теряет фокус, пользовательская активность (например, перемещение мыши) над документом отслеживается, чтобы определить, является ли окно видимым или нет. Вероятность видимости страницы обратно пропорциональна времени последнего пользовательского действия на странице: если пользователь долгое время не выполняет никаких действий с документом, скорее всего, страница не видна. Приведенный ниже код имитирует API видимости страницы W3C: он ведет себя так же, но имеет небольшой процент ложных срабатываний. Он имеет преимущество в том, что является мультибраузером (протестирован на Firefox 5, Firefox 10, MSIE 9, MSIE 7, Safari 5, Chrome 9).
<div id="x"></div>
<script>
/**
Registers the handler to the event for the given object.
@param obj the object which will raise the event
@param evType the event type: click, keypress, mouseover, ...
@param fn the event handler function
@param isCapturing set the event mode (true = capturing event, false = bubbling event)
@return true if the event handler has been attached correctly
*/
function addEvent(obj, evType, fn, isCapturing){
if (isCapturing==null) isCapturing=false;
if (obj.addEventListener){
// Firefox
obj.addEventListener(evType, fn, isCapturing);
return true;
} else if (obj.attachEvent){
// MSIE
var r = obj.attachEvent('on'+evType, fn);
return r;
} else {
return false;
}
}
// register to the potential page visibility change
addEvent(document, "potentialvisilitychange", function(event) {
document.getElementById("x").innerHTML+="potentialVisilityChange: potentialHidden="+document.potentialHidden+", document.potentiallyHiddenSince="+document.potentiallyHiddenSince+" s<br>";
});
// register to the W3C Page Visibility API
var hidden=null;
var visibilityChange=null;
if (typeof document.mozHidden !== "undefined") {
hidden="mozHidden";
visibilityChange="mozvisibilitychange";
} else if (typeof document.msHidden !== "undefined") {
hidden="msHidden";
visibilityChange="msvisibilitychange";
} else if (typeof document.webkitHidden!=="undefined") {
hidden="webkitHidden";
visibilityChange="webkitvisibilitychange";
} else if (typeof document.hidden !=="hidden") {
hidden="hidden";
visibilityChange="visibilitychange";
}
if (hidden!=null && visibilityChange!=null) {
addEvent(document, visibilityChange, function(event) {
document.getElementById("x").innerHTML+=visibilityChange+": "+hidden+"="+document[hidden]+"<br>";
});
}
var potentialPageVisibility = {
pageVisibilityChangeThreshold:3*3600, // in seconds
init:function() {
function setAsNotHidden() {
var dispatchEventRequired=document.potentialHidden;
document.potentialHidden=false;
document.potentiallyHiddenSince=0;
if (dispatchEventRequired) dispatchPageVisibilityChangeEvent();
}
function initPotentiallyHiddenDetection() {
if (!hasFocusLocal) {
// the window does not has the focus => check for user activity in the window
lastActionDate=new Date();
if (timeoutHandler!=null) {
clearTimeout(timeoutHandler);
}
timeoutHandler = setTimeout(checkPageVisibility, potentialPageVisibility.pageVisibilityChangeThreshold*1000+100); // +100 ms to avoid rounding issues under Firefox
}
}
function dispatchPageVisibilityChangeEvent() {
unifiedVisilityChangeEventDispatchAllowed=false;
var evt = document.createEvent("Event");
evt.initEvent("potentialvisilitychange", true, true);
document.dispatchEvent(evt);
}
function checkPageVisibility() {
var potentialHiddenDuration=(hasFocusLocal || lastActionDate==null?0:Math.floor((new Date().getTime()-lastActionDate.getTime())/1000));
document.potentiallyHiddenSince=potentialHiddenDuration;
if (potentialHiddenDuration>=potentialPageVisibility.pageVisibilityChangeThreshold && !document.potentialHidden) {
// page visibility change threshold raiched => raise the even
document.potentialHidden=true;
dispatchPageVisibilityChangeEvent();
}
}
var lastActionDate=null;
var hasFocusLocal=true;
var hasMouseOver=true;
document.potentialHidden=false;
document.potentiallyHiddenSince=0;
var timeoutHandler = null;
addEvent(document, "pageshow", function(event) {
document.getElementById("x").innerHTML+="pageshow/doc:<br>";
});
addEvent(document, "pagehide", function(event) {
document.getElementById("x").innerHTML+="pagehide/doc:<br>";
});
addEvent(window, "pageshow", function(event) {
document.getElementById("x").innerHTML+="pageshow/win:<br>"; // raised when the page first shows
});
addEvent(window, "pagehide", function(event) {
document.getElementById("x").innerHTML+="pagehide/win:<br>"; // not raised
});
addEvent(document, "mousemove", function(event) {
lastActionDate=new Date();
});
addEvent(document, "mouseover", function(event) {
hasMouseOver=true;
setAsNotHidden();
});
addEvent(document, "mouseout", function(event) {
hasMouseOver=false;
initPotentiallyHiddenDetection();
});
addEvent(window, "blur", function(event) {
hasFocusLocal=false;
initPotentiallyHiddenDetection();
});
addEvent(window, "focus", function(event) {
hasFocusLocal=true;
setAsNotHidden();
});
setAsNotHidden();
}
}
potentialPageVisibility.pageVisibilityChangeThreshold=4; // 4 seconds for testing
potentialPageVisibility.init();
</script>
Поскольку в настоящее время не существует работающего кросс-браузерного решения без ложных срабатываний, вам следует подумать дважды об отключении периодической активности на вашем веб-сайте.