Javascript: Обнаруживает общее количество раз, когда окно браузера теряет фокус [дубликат]

Вот версия ответа ConroyP выше, которая работает, даже если у конструктора требуются параметры:

//If Object.create isn't already defined, we just do the simple shim,
//without the second argument, since that's all we need here
var object_create = Object.create;
if (typeof object_create !== 'function') {
    object_create = function(o) {
        function F() {}
        F.prototype = o;
        return new F();
    };
}

function deepCopy(obj) {
    if(obj == null || typeof(obj) !== 'object'){
        return obj;
    }
    //make sure the returned object has the same prototype as the original
    var ret = object_create(obj.constructor.prototype);
    for(var key in obj){
        ret[key] = deepCopy(obj[key]);
    }
    return ret;
}

Эта функция также доступна в моей библиотеке simpleoo .

Редактировать:

Вот более надежная версия (благодаря Джастину МакКэндлесу, теперь это также поддерживает циклические ссылки):

/**
 * Deep copy an object (make copies of all its object properties, sub-properties, etc.)
 * An improved version of http://keithdevens.com/weblog/archive/2007/Jun/07/javascript.clone
 * that doesn't break if the constructor has required parameters
 * 
 * It also borrows some code from http://stackoverflow.com/a/11621004/560114
 */ 
function deepCopy(src, /* INTERNAL */ _visited, _copiesVisited) {
    if(src === null || typeof(src) !== 'object'){
        return src;
    }

    //Honor native/custom clone methods
    if(typeof src.clone == 'function'){
        return src.clone(true);
    }

    //Special cases:
    //Date
    if(src instanceof Date){
        return new Date(src.getTime());
    }
    //RegExp
    if(src instanceof RegExp){
        return new RegExp(src);
    }
    //DOM Element
    if(src.nodeType && typeof src.cloneNode == 'function'){
        return src.cloneNode(true);
    }

    // Initialize the visited objects arrays if needed.
    // This is used to detect cyclic references.
    if (_visited === undefined){
        _visited = [];
        _copiesVisited = [];
    }

    // Check if this object has already been visited
    var i, len = _visited.length;
    for (i = 0; i < len; i++) {
        // If so, get the copy we already made
        if (src === _visited[i]) {
            return _copiesVisited[i];
        }
    }

    //Array
    if (Object.prototype.toString.call(src) == '[object Array]') {
        //[].slice() by itself would soft clone
        var ret = src.slice();

        //add it to the visited array
        _visited.push(src);
        _copiesVisited.push(ret);

        var i = ret.length;
        while (i--) {
            ret[i] = deepCopy(ret[i], _visited, _copiesVisited);
        }
        return ret;
    }

    //If we've reached here, we have a regular object

    //make sure the returned object has the same prototype as the original
    var proto = (Object.getPrototypeOf ? Object.getPrototypeOf(src): src.__proto__);
    if (!proto) {
        proto = src.constructor.prototype; //this line would probably only be reached by very old browsers 
    }
    var dest = object_create(proto);

    //add this object to the visited array
    _visited.push(src);
    _copiesVisited.push(dest);

    for (var key in src) {
        //Note: this does NOT preserve ES5 property attributes like 'writable', 'enumerable', etc.
        //For an example of how this could be modified to do so, see the singleMixin() function
        dest[key] = deepCopy(src[key], _visited, _copiesVisited);
    }
    return dest;
}

//If Object.create isn't already defined, we just do the simple shim,
//without the second argument, since that's all we need here
var object_create = Object.create;
if (typeof object_create !== 'function') {
    object_create = function(o) {
        function F() {}
        F.prototype = o;
        return new F();
    };
}

502
задан Mark Amery 4 March 2015 в 12:49
поделиться

18 ответов

С момента написания этого ответа новая спецификация достигла рекомендации благодаря W3C. API видимости страницы 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 являются , необходимыми для IE 9 и ниже , а все остальные используют onfocus и onblur, за исключением iOS, который использует onpageshow и onpagehide.

596
ответ дан 18 revs, 9 users 69% 21 August 2018 в 12:26
поделиться
  • 1
    Разве вы не объявляете hidden дважды? – Majid Fouladpour 8 August 2012 в 00:23
  • 2
    @JulienKronegg: вот почему мой ответ специально упоминает API видимости страницы, который ввел рабочий статус проекта после того, как я изначально написал свой ответ. Методы focus / blur предоставляют ограниченную функциональность для старых браузеров. Связывание с другими событиями, как и в вашем ответе, не охватывает гораздо больше, чем это, и более подвержено риску поведенческих различий (например, IE не запускает мышь, когда под курсором появляется окно). Я бы предположил, что более подходящим действием будет отображение сообщения или значка, указывающего пользователю, что обновления могут быть менее частыми из-за неактивности страницы. – Andy E 7 September 2012 в 17:12
  • 3
    @AndyE Я пробовал это решение на хром. Он работает, если я меняю вкладки, но это не так, если я меняю окна (вкладка ALT +). Должно ли это? Вот скрипка - jsfiddle.net/8a9N6/17 – Tony Lâmpada 16 September 2013 в 22:25
  • 4
    @Heliodor: Я бы хотел сохранить код в минимальном ответе. Это никогда не предназначалось для полного решения для вырезания и вставки, так как разработчики могли бы захотеть не устанавливать класс на теле и вообще совсем не выполнять действия (например, останавливать и запускать таймер). – Andy E 14 August 2014 в 09:58
  • 5
    @AndyE Ваше решение работает только в том случае, если пользователь меняет вкладки или минимизирует / увеличивает окно. Однако событие onchange не запускается, если пользователь покидает вкладку активным, но максимизирует другую программу поверх нее с панели задач. Есть ли решение для этого сценария? Благодаря! – user1491636 4 November 2014 в 19:43

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

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

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

2
ответ дан Austin Hyde 21 August 2018 в 12:26
поделиться
  • 1
    Если у пользователя нет мыши. – grawity 29 June 2009 в 20:36
  • 2
  • 3
  • 4
    Это также не играет в кости, если пользователь просматривает видео – jamiew 29 November 2011 в 19:32
  • 5
    вы можете использовать onkeypress или другие подобные события, чтобы сбросить таймер и решить проблему без мыши. Конечно, это все равно не будет работать для пользователей, активно просматривающих страницу, чтобы посмотреть видео, изучить изображение и т. Д. – joshuahedlund 10 January 2012 в 23:34

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

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.

0
ответ дан B.F. 21 August 2018 в 12:26
поделиться

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

Это выполнит задачу, например. обновляйте страницу каждые 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);
}
2
ответ дан brasofilo 21 August 2018 в 12:26
поделиться
  • 1
    Опираясь на методы фокуса / размытия не работают (это дает вам много ложных срабатываний), см. Stackoverflow.com/a/9502074/698168 – Julien Kronegg 7 September 2012 в 15:43

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

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

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

121
ответ дан Carson Wright 21 August 2018 в 12:26
поделиться
  • 1
    Очень приятно, я использовал это, чтобы контролировать фоновый процесс, и он работал красиво. – Travis J 2 October 2012 в 22:31
  • 2
    Действительно ли это работает? – Armin Cifuentes 18 April 2013 в 19:04
  • 3
    для меня этот вызов дважды в iframe – msangel 29 June 2013 в 14:59
  • 4
    Это больше не работает для текущих версий современных браузеров, см. Одобренный ответ (API видимости страницы) – Jon z 25 February 2014 в 19:10
  • 5
    Он работает в хроме 51.0.2704.106 м – Denny 1 July 2016 в 20:09

Я начал использовать ответ 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');
});
5
ответ дан Daniel Buckmaster 21 August 2018 в 12:26
поделиться

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

if(!document.hasFocus()) {
    // do stuff
}
0
ответ дан El Programmer 21 August 2018 в 12:26
поделиться

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

if(new_message){
    if(!document.hasFocus()){
        audio.play();
        document.title="Have new messages";
    }
    else{
        audio.stop();
        document.title="Application Name";
    } 
}
9
ответ дан Grey Li 21 August 2018 в 12:26
поделиться
  • 1
    Самое чистое решение с поддержкой back to IE6 – Paul Cooper 3 March 2015 в 10:34
  • 2
    document.hasFocus() - самый чистый способ сделать это. Все другие способы использования видимости api или события, основанного или ищущего различные уровни активности пользователя / отсутствия активности, становятся чрезмерно сложными и полными краевыми случаями и отверстиями. поместите его на простой интервал и поднимите пользовательское событие, когда результаты будут изменены. Пример: jsfiddle.net/59utucz6/1 – danatcofo 29 March 2017 в 18:17
  • 3
    Эффективный и в отличие от других решений дает правильную обратную связь при переключении на другую вкладку или окно браузера и даже на другое приложение. – ow3n 17 July 2017 в 09:20

Просто хотел добавить: вопрос нечетко написан. «Когда пользователь не смотрит на сайт (т. Е. Окно или вкладка не имеют фокуса) ...»

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

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

0
ответ дан HolgerJeromin 21 August 2018 в 12:26
поделиться

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

  • W3C API видимости страницы Предполагается сделать это (поддерживается с: Firefox 10, MSIE 10, Chrome 13). Однако этот API вызывает только события, когда вкладка браузера полностью переопределяется (например, когда пользователь переходит с одной вкладки на другую). API не увеличивает события, когда видимость не может быть определена с 100% -ной точностью (например, Alt + Tab для переключения на другое приложение).
  • Использование методов фокуса / размытия дает вам много ложных срабатываний. Например, если пользователь отображает меньшее окно поверх окна браузера, окно браузера потеряет фокус (onblur поднят), но пользователь все еще может его увидеть (так что его все еще нужно обновлять). См. Также http://javascript.info/tutorial/focus
  • . Опираясь на активность пользователя (перемещение мыши, клики, ввод строки), вы также получаете много ложных срабатываний. Подумайте о том же случае, что и выше, или о пользователе, смотрящем видео.

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

  • Изменение вкладки браузера на другую (точность 100%, благодаря W3C API видимости страницы)
  • Страница потенциально скрыта другим окном , например из-за 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>

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

38
ответ дан Julien Kronegg 21 August 2018 в 12:26
поделиться
  • 1
    Не использовал бы строгий оператор сравнения в строке «undefined» вместо ключевого слова undefined вызывать ложные срабатывания в приведенном выше коде? – Jonathon 16 July 2016 в 00:23
  • 2
    Он не работает с вкладкой alt + – kiran Gopal 1 December 2016 в 08:30
  • 3
    @kiran: На самом деле он работает с Alt + Tab. Вы не можете определить, скрыта ли страница, когда вы делаете вкладку Alt +, потому что вы можете переключиться на меньшее окно, чтобы вы не могли гарантировать, что ваша страница полностью скрыта. Вот почему я использую понятие «потенциально скрытый», (в примере порог установлен на 4 секунды, поэтому вам нужно переключиться в другое окно, используя Alt + Tab не менее 4 секунд). Однако ваш комментарий показывает, что ответ был не таким ясным, поэтому я переформулировал его. – Julien Kronegg 1 December 2016 в 22:14
  • 4
    @JulienKronegg Я думаю, что это лучшее решение. Тем не менее, код выше чрезвычайно нуждается в рефакторинге и абстракциях. Почему вы не загружаете его в GitHub, и пусть сообщество реорганизует его? – Jacob 20 April 2017 в 11:24
  • 5
    @Jacob Я рад, что вам понравилось мое решение. Не стесняйтесь продвигать его в проект GitHub самостоятельно. Я даю код с лицензией Creative Commons BY creativecommons.org/licenses/by/4.0 – Julien Kronegg 20 April 2017 в 14:17

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

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

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

8
ответ дан l2aelba 21 August 2018 в 12:26
поделиться

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);

})();
3
ответ дан maryam 21 August 2018 в 12:26
поделиться

Для решения без jQuery проверьте Visibility.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 & lt; 10; iOS & lt; 7)

1
ответ дан Niko 21 August 2018 в 12:26
поделиться

В 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 для меня)
24
ответ дан Piotr De 21 August 2018 в 12:26
поделиться
  • 1
    Принятый ответ вызвал проблемы в IE9. Эта библиотека отлично работает. – Tom Teman 14 January 2015 в 16:59
  • 2
    Удивительная библиотека! благодаря! играл главную роль. – vsync 27 January 2015 в 11:28

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

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

См .:

2
ответ дан sim642 21 August 2018 в 12:26
поделиться
  • 1
    Я думаю, что это связано с BFCache: когда пользователь нажимает «Назад» или «Вперед» - это не связано с тем, что страница находится на вершине рабочего стола компьютера. – 太極者無極而生 11 July 2013 в 08:07

Для angular.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() - это функция области видимости в пределах области действия любого Контроллера.

1
ответ дан Steve Campbell 21 August 2018 в 12:26
поделиться

Это действительно сложно. Кажется, что нет решения, учитывая следующие требования.

  • На странице представлены iframe, над которыми вы не контролируете
  • . Вы хотите отслеживать изменение состояния видимости независимо от того, вызванное изменением TAB (ctrl + tab) или сменой окна (alt + tab)

Это происходит из-за того, что:

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

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

Это может:

  • 1) ctrl + tab, когда главная страница имеет фокус: ДА
  • 2) ctrl + tab, когда iframe имеет фокус: ДА
  • 3) alt + tab, когда главная страница имеет фокус: ДА
  • 4) alt + tab, когда iframe имеет фокус: NO & lt; - bummer

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

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

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

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

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

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

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

7
ответ дан Tony Lâmpada 21 August 2018 в 12:26
поделиться
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/

4
ответ дан yckart 21 August 2018 в 12:26
поделиться
Другие вопросы по тегам:

Похожие вопросы: