Есть ли способ определить, является ли окно браузера в настоящее время активным?

У меня есть JavaScript, который периодически делать упражнение. Когда пользователь не смотрит на сайт (т. е. окно или вкладка не имеют фокуса), было бы неплохо не запускать.

есть ли способ сделать это с помощью JavaScript?

my reference point: Gmail Chat воспроизводит звук, если окно, которое вы используете, неактивно.

18 ответов


С момента первоначального написания этого ответа новая спецификация достигла рекомендация статус благодаря W3C. The API видимости страницы (on MDN) теперь позволяет нам более точно определить, когда страница скрыта для пользователя.

текущая поддержка браузера:

следующий код использует API, возвращаясь к менее надежному методу размытия / фокусировки в несовместимых браузерах.

(function() {
  var hidden = "hidden";

  // Standards:
  if (hidden in document)
    document.addEventListener("visibilitychange", onchange);
  else if ((hidden = "mozHidden") in document)
    document.addEventListener("mozvisibilitychange", onchange);
  else if ((hidden = "webkitHidden") in document)
    document.addEventListener("webkitvisibilitychange", onchange);
  else if ((hidden = "msHidden") in document)
    document.addEventListener("msvisibilitychange", onchange);
  // IE 9 and lower:
  else if ("onfocusin" in document)
    document.onfocusin = document.onfocusout = onchange;
  // All others:
  else
    window.onpageshow = window.onpagehide
    = window.onfocus = window.onblur = onchange;

  function onchange (evt) {
    var v = "visible", h = "hidden",
        evtMap = {
          focus:v, focusin:v, pageshow:v, blur:h, focusout:h, pagehide:h
        };

    evt = evt || window.event;
    if (evt.type in evtMap)
      document.body.className = evtMap[evt.type];
    else
      document.body.className = this[hidden] ? "hidden" : "visible";
  }

  // set the initial state (but only if browser supports the Page Visibility API)
  if( document[hidden] !== undefined )
    onchange({type: document[hidden] ? "blur" : "focus"});
})();

onfocusin и onfocusout are требуется для IE 9 и ниже, в то время как все остальные используют onfocus и onblur, за исключением iOS, которая использует onpageshow и onpagehide.


Я бы использовал jQuery, потому что тогда все, что вам нужно сделать, это:

$(window).blur(function(){
  //your code here
});
$(window).focus(function(){
  //your code
});

или, по крайней мере, это сработало для меня.


существует 3 типичных метода, используемых для определения, может ли пользователь видеть HTML-страницу, однако ни один из них не работает отлично:

  • на W3C API видимости страницы предполагается сделать это (поддерживается с: Firefox 10, MSIE 10, Chrome 13). Однако этот API вызывает события только тогда, когда вкладка браузера полностью переопределена (например, когда пользователь переходит с одной вкладки на другую). API не вызывает событий, когда видимость не может быть определена с помощью 100% точность (например, Alt + Tab для переключения на другое приложение).

  • используя фокус / размытие на основе методов дает много ложноположительных. Например, если пользователь отображает меньшее окно поверх окна браузера, окно браузера потеряет фокус (onblur raised), но пользователь все еще может видеть его (поэтому его все еще нужно обновить). См. такжеhttp://javascript.info/tutorial/focus

  • опираясь на активность пользователя (перемещение мыши, щелчки, набранный ключ) дает вам много ложного срабатывания тоже. Подумайте о том же случае, что и выше, или пользователь смотрит видео.

чтобы улучшить несовершенное поведение, описанное выше, я использую комбинацию из 3 методов: W3C Visibility API, затем focus/blur и методы активности пользователя, чтобы уменьшить ложноположительную скорость. Это позволяет управлять следующими событиями:

  • изменение вкладки браузера на другую (100% точность, благодаря API видимости страницы W3C)
  • страница потенциально скрыта другим окном, например, из-за Alt + Tab (вероятностный = не 100% точный)
  • внимание пользователя потенциально не сосредоточено на HTML-странице (вероятностно = не 100% точно)

вот как это работает: когда документ теряет фокус, активность пользователя (например, перемещение мыши) на документе контролируется, чтобы определить, является ли окно видимым или нет. Страница вероятность видимости обратно пропорциональна времени последнего действия пользователя на странице: если пользователь долгое время не делает никаких действий над документом, страница, скорее всего, не видна. Код ниже имитирует API видимости страницы W3C: он ведет себя так же, но имеет небольшую ложноположительную скорость. Он имеет преимущество быть multibrowser (протестирован на 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>

поскольку в настоящее время не работает кросс-браузерное решение без ложного срабатывания, вам лучше дважды подумать об отключении периодической активности на вашем веб-сайте.


на GitHub есть аккуратная библиотека:

https://github.com/serkanyersen/ifvisible.js

пример:

// If page is visible right now
if( ifvisible.now() ){
  // Display pop-up
  openPopUp();
}

я протестировал версию 1.0.1 во всех браузерах, которые у меня есть, и могу подтвердить, что она работает с:

  • В IE9, БРАУЗЕР IE10
  • FF 26.0
  • Chrome 34.0

... и, вероятно, все новые версии.

не работает с:

  • IE8-всегда указывайте, что вкладка / окно в настоящее время активна (.now() всегда возвращает true для меня)

Я создаю кометный чат для своего приложения, и когда я получаю сообщение от другого пользователя, я использую:

if(new_message){
    if(!document.hasFocus()){
        audio.play();
        document.title="Have new messages";
    }
    else{
        audio.stop();
        document.title="Application Name";
    } 
}

Это действительно сложно. По-видимому, нет никакого решения с учетом следующих требований.

  • страница включает в себя iframes, которые у вас нет контроля над
  • вы хотите отслеживать изменение состояния видимости независимо от изменения, вызванного изменением вкладки (ctrl+tab) или изменением окна (alt+tab)

Это происходит потому, что:

  • API видимости страницы может надежно сообщить вам об изменении вкладки (даже с iframes), но это не могу сказать, когда пользователь меняет windows.
  • прослушивание событий размытия/фокусировки окна может обнаруживать вкладки alt+и ctrl+, если iframe не имеет фокуса.

учитывая эти ограничения, можно реализовать решение, которое объединяет - API-интерфейс страницы видимость - размытие/фокус окна - документ.activeElement

что способен:

  • 1) ctrl + tab, когда родительская страница имеет фокус: да
  • 2) ctrl + tab при iframe имеет фокус: да
  • 3) alt + tab, когда родительская страница имеет фокус: да
  • 4) alt + tab, когда iframe имеет фокус:нет

когда iframe имеет фокус, ваши события размытия / фокусировки вообще не вызываются, и API видимости страницы не будет запускаться на вкладке alt+.

Я построил на решении @AndyE и реализовал это (почти хорошее) решение здесь: https://dl.dropboxusercontent.com/u/2683925/estante-components/visibility_test1.html (извините, у меня были проблемы с JSFiddle).

Это также доступно на Github:https://github.com/qmagico/estante-components

Это работает на chrome / chromium. Он работает в firefox, за исключением того, что он не загружает содержимое iframe (любая идея, почему?)

в любом случае, чтобы решить последнюю проблему (4), единственный способ сделать это - прослушивание событий размытия / фокусировки на iframe. Если у вас есть некоторый контроль над iframes, вы можете использовать API postMessage для этого.

https://dl.dropboxusercontent.com/u/2683925/estante-components/visibility_test2.html

Я все еще не протестировал это с достаточным количеством браузеров. Если вы можете найти более подробную информацию о том, где это не работает, пожалуйста, дайте мне знать в комментариях ниже.


использование : API видимости страницы

document.addEventListener( 'visibilitychange' , function() {
    if (document.hidden) {
        console.log('bye');
    } else {
        console.log('well back');
    }
}, false );

Я могу использовать ? http://caniuse.com/#feat=pagevisibility


Я начал использовать ответ сообщества wiki, но понял, что он не обнаруживает события alt-tab в Chrome. Это потому, что он использует первый доступный источник событий, и в этом случае это API видимости страницы, который в Chrome, похоже, не отслеживает alt-tabbing.

Я решил немного изменить скрипт, чтобы отслеживать все возможные события для изменения фокуса страницы. Вот функция, которую вы можете ввести:

function onVisibilityChange(callback) {
    var visible = true;

    if (!callback) {
        throw new Error('no callback given');
    }

    function focused() {
        if (!visible) {
            callback(visible = true);
        }
    }

    function unfocused() {
        if (visible) {
            callback(visible = false);
        }
    }

    // Standards:
    if ('hidden' in document) {
        document.addEventListener('visibilitychange',
            function() {(document.hidden ? unfocused : focused)()});
    }
    if ('mozHidden' in document) {
        document.addEventListener('mozvisibilitychange',
            function() {(document.mozHidden ? unfocused : focused)()});
    }
    if ('webkitHidden' in document) {
        document.addEventListener('webkitvisibilitychange',
            function() {(document.webkitHidden ? unfocused : focused)()});
    }
    if ('msHidden' in document) {
        document.addEventListener('msvisibilitychange',
            function() {(document.msHidden ? unfocused : focused)()});
    }
    // IE 9 and lower:
    if ('onfocusin' in document) {
        document.onfocusin = focused;
        document.onfocusout = unfocused;
    }
    // All others:
    window.onpageshow = window.onfocus = focused;
    window.onpagehide = window.onblur = unfocused;
};

использовать его как это:

onVisibilityChange(function(visible) {
    console.log('the page is now', visible ? 'focused' : 'unfocused');
});

var visibilityChange = (function (window) {
    var inView = false;
    return function (fn) {
        window.onfocus = window.onblur = window.onpageshow = window.onpagehide = function (e) {
            if ({focus:1, pageshow:1}[e.type]) {
                if (inView) return;
                fn("visible");
                inView = true;
            } else if (inView) {
                fn("hidden");
                inView = false;
            }
        };
    };
}(this));

visibilityChange(function (state) {
    console.log(state);
});

http://jsfiddle.net/ARTsinn/JTxQY/


u может использовать:

(function () {

    var requiredResolution = 10; // ms
    var checkInterval = 1000; // ms
    var tolerance = 20; // percent


    var counter = 0;
    var expected = checkInterval / requiredResolution;
    //console.log('expected:', expected);

    window.setInterval(function () {
        counter++;
    }, requiredResolution);

    window.setInterval(function () {
        var deviation = 100 * Math.abs(1 - counter / expected);
        // console.log('is:', counter, '(off by', deviation , '%)');
        if (deviation > tolerance) {
            console.warn('Timer resolution not sufficient!');
        }
        counter = 0;
    }, checkInterval);

})();

в HTML 5 вы также можете использовать:

  • onpageshow: скрипт для запуска, когда окно становится видимым
  • onpagehide: скрипт для запуска, когда окно скрыто

посмотреть:


немного более сложным способом было бы использовать setInterval() чтобы проверить положение мыши и сравнить с последней проверкой. Если мышь не перемещалась в течение заданного времени, пользователь, вероятно, простаивает.

это имеет дополнительное преимущество, говоря, если пользователь простаивает, а не просто проверка если окно не является активным.

как указывали многие люди, это не всегда хороший способ проверить, простаивает ли окно пользователя или браузера, как пользователь может даже не использовать мышь или смотреть видео или подобное. Я просто предлагаю один из возможных способов проверить праздность.


это адаптация ответа от Энди Э.

Это будет выполнять задачу, например, обновлять страницу каждые 30 секунд, но только если страница видна и сосредоточенным.

если видимость не может быть обнаружена, то будет использоваться только фокус.

если пользователь фокусирует страницу, то она будет обновляться немедленно

страница не будет обновляться до 30 секунд после любого вызова ajax

var windowFocused = true;
var timeOut2 = null;

$(function(){
  $.ajaxSetup ({
    cache: false
  });
  $("#content").ajaxComplete(function(event,request, settings){
       set_refresh_page(); // ajax call has just been made, so page doesn't need updating again for 30 seconds
   });
  // check visibility and focus of window, so as not to keep updating unnecessarily
  (function() {
      var hidden, change, vis = {
              hidden: "visibilitychange",
              mozHidden: "mozvisibilitychange",
              webkitHidden: "webkitvisibilitychange",
              msHidden: "msvisibilitychange",
              oHidden: "ovisibilitychange" /* not currently supported */
          };
      for (hidden in vis) {
          if (vis.hasOwnProperty(hidden) && hidden in document) {
              change = vis[hidden];
              break;
          }
      }
      document.body.className="visible";
      if (change){     // this will check the tab visibility instead of window focus
          document.addEventListener(change, onchange,false);
      }

      if(navigator.appName == "Microsoft Internet Explorer")
         window.onfocus = document.onfocusin = document.onfocusout = onchangeFocus
      else
         window.onfocus = window.onblur = onchangeFocus;

      function onchangeFocus(evt){
        evt = evt || window.event;
        if (evt.type == "focus" || evt.type == "focusin"){
          windowFocused=true; 
        }
        else if (evt.type == "blur" || evt.type == "focusout"){
          windowFocused=false;
        }
        if (evt.type == "focus"){
          update_page();  // only update using window.onfocus, because document.onfocusin can trigger on every click
        }

      }

      function onchange () {
        document.body.className = this[hidden] ? "hidden" : "visible";
        update_page();
      }

      function update_page(){
        if(windowFocused&&(document.body.className=="visible")){
          set_refresh_page(1000);
        }
      }


  })();
  set_refresh_page();
})

function get_date_time_string(){
  var d = new Date();
  var dT = [];
  dT.push(d.getDate());
  dT.push(d.getMonth())
  dT.push(d.getFullYear());
  dT.push(d.getHours());
  dT.push(d.getMinutes());
  dT.push(d.getSeconds());
  dT.push(d.getMilliseconds());
  return dT.join('_');
}

function do_refresh_page(){

// do tasks here

// e.g. some ajax call to update part of the page.

// (date time parameter will probably force the server not to cache)

//      $.ajax({
//        type: "POST",
//        url: "someUrl.php",
//        data: "t=" + get_date_time_string()+"&task=update",
//        success: function(html){
//          $('#content').html(html);
//        }
//      });

}

function set_refresh_page(interval){
  interval = typeof interval !== 'undefined' ? interval : 30000; // default time = 30 seconds
  if(timeOut2 != null) clearTimeout(timeOut2);
  timeOut2 = setTimeout(function(){
    if((document.body.className=="visible")&&windowFocused){
      do_refresh_page();
    }
    set_refresh_page();
  }, interval);
}

для решения без jQuery проверьте видимости.js который предоставляет информацию о трех состояниях страницы

visible    ... page is visible
hidden     ... page is not visible
prerender  ... page is being prerendered by the browser

, а также удобство-обертки для метода setInterval

/* Perform action every second if visible */
Visibility.every(1000, function () {
    action();
});

/* Perform action every second if visible, every 60 sec if not visible */
Visibility.every(1000, 60*1000, function () {
    action();
});

резервный вариант для старых браузеров (IE


угловое.js, вот директива (основанная на принятом ответе), которая позволит вашему контроллеру реагировать на изменение видимости:

myApp.directive('reactOnWindowFocus', function($parse) {
    return {
        restrict: "A",
        link: function(scope, element, attrs) {
            var hidden = "hidden";
            var currentlyVisible = true;
            var functionOrExpression = $parse(attrs.reactOnWindowFocus);

          // Standards:
          if (hidden in document)
            document.addEventListener("visibilitychange", onchange);
          else if ((hidden = "mozHidden") in document)
            document.addEventListener("mozvisibilitychange", onchange);
          else if ((hidden = "webkitHidden") in document)
            document.addEventListener("webkitvisibilitychange", onchange);
          else if ((hidden = "msHidden") in document)
            document.addEventListener("msvisibilitychange", onchange);
          else if ("onfocusin" in document) {
                // IE 9 and lower:
            document.onfocusin = onshow;
                document.onfocusout = onhide;
          } else {
                // All others:
            window.onpageshow = window.onfocus = onshow;
                window.onpagehide = window.onblur = onhide;
            }

          function onchange (evt) {
                //occurs both on leaving and on returning
                currentlyVisible = !currentlyVisible;
                doSomethingIfAppropriate();
          }

            function onshow(evt) {
                //for older browsers
                currentlyVisible = true;
                doSomethingIfAppropriate();
            }

            function onhide(evt) {
                //for older browsers
                currentlyVisible = false;
                doSomethingIfAppropriate();
            }

            function doSomethingIfAppropriate() {
                if (currentlyVisible) {
                    //trigger angular digest cycle in this scope
                    scope.$apply(function() {
                        functionOrExpression(scope);
                    });
                }
            }
        }
    };

});

вы можете использовать такой пример: <div react-on-window-focus="refresh()">, где refresh() является функцией области в области любого контроллера в области.


это работает для меня на chrome 67, firefox 67,

if(!document.hasFocus()) {
    // do stuff
}

Если вы хотите действовать on весь размытие браузер: Как я прокомментировал, если браузер потеряет фокус, ни одно из предложенных событий не сработает. Моя идея-подсчитать в цикле и сбросить счетчик, если произойдет пожар. Если счетчик достигает предела, я делаю местоположение.href на другую страницу. Это также срабатывает, если вы работаете над dev-tools.

var iput=document.getElementById("hiddenInput");
   ,count=1
   ;
function check(){
         count++;
         if(count%2===0){
           iput.focus();
         }
         else{
           iput.blur();
         }
         iput.value=count;  
         if(count>3){
           location.href="http://Nirwana.com";
         }              
         setTimeout(function(){check()},1000);
}   
iput.onblur=function(){count=1}
iput.onfocus=function(){count=1}
check();

это проект успешно протестирован на FF.


просто хотел добавить: Вопрос не ясно написан. "Когда пользователь не смотрит на сайте (т. е. окно или вкладка не в фокусе)..."

Я могу смотреть на сайт, когда он не имеет фокуса. Большинство настольных систем могут показывать windows параллельно:)

вот почему API видимости страницы, вероятно, правильный ответ, потому что он предотвращает обновление сайта, когда "пользователь не может видеть обновления" , которые могут сильно отличаться от " вкладка не имеет внимание."