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


585

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

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

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


8
Для тех, кто не удовлетворен ответами ниже, проверьте requestAnimationFrameAPI или используйте современную функцию, которая уменьшает частоту setTimeout/, setIntervalкогда окно не видно (например, 1 секунда в Chrome).
Роб W

2
document.body.onblur = function (e) {console.log ('lama');} работал для несфокусированных элементов.
WhyMe

2
Посмотрите этот ответ для кросс-браузерно-совместимого решения, которое использует W3C Page Visibility API, обращаясь к blur/ focusв браузерах, которые его не поддерживают.
Матиас Биненс

2
80% ответов ниже не являются ответами на этот вопрос . Вопрос спрашивает о неактивном в настоящее время, но тонны ответа ниже о невидимом, который не является ответом на этот вопрос. Возможно, они должны быть помечены как «не ответ»
Гман

Большинство людей говорят о неактивности, когда имеют в виду неактивность и невидимость . Просто не активен - это просто - просто обрабатывать окно blur/ focusсобытия ... которые будут иметь ограниченное использование, хотя окно может быть неактивным, но полностью или частично видимым (на некоторых панелях задач также есть значки предварительного просмотра, которые люди ожидают, что они будут продолжать обновлено).
rustyx

Ответы:


691

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

document.addEventListener("visibilitychange", onchange);

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

Следующий код возвращается к менее надежному методу размытия / фокусировки в несовместимых браузерах:

(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.


1
@bellpeace: IE должен распространяться focusinи focusoutот iframe до верхнего окна. Для новых браузеров, вы просто должны обрабатывать focusи blurсобытие на каждый Iframe в windowобъекте. Вы должны использовать обновленный код, который я только что добавил, который будет по крайней мере покрывать эти случаи в новых браузерах.
Энди Э

3
@JulienKronegg: именно поэтому в моем ответе конкретно упоминается API видимости страницы, который вошел в рабочее состояние черновика после того, как я первоначально написал свой ответ. Методы фокусировки / размытия предоставляют ограниченную функциональность для старых браузеров. Привязка к другим событиям, как в вашем ответе, не покрывает намного больше, чем это, и больше подвержена риску поведенческих различий (например, IE не запускает мышку, когда окно появляется под курсором). Я хотел бы предложить более подходящее действие - отобразить сообщение или значок, указывающий пользователю, что обновления могут происходить реже из-за неактивности страницы.
Энди Э,

6
@AndyE Я попробовал это решение на хроме. Это работает, если я меняю вкладки, но не если я меняю окна (ALT + вкладка). Должно ли это? Вот скрипка - jsfiddle.net/8a9N6/17
Тони Лампада

2
@Heliodor: я бы хотел пока оставить минимальный код в ответе. Оно никогда не предназначалось для комплексного решения «вырезать и вставить», так как разработчики могут не захотеть устанавливать класс в теле и предпринимать совершенно иные действия (такие как остановка и запуск таймера).
Энди Э.

8
@ AndyE Ваше решение работает, только если пользователь меняет вкладки или сворачивает / разворачивает окно. Однако событие onchange не запускается, если пользователь оставляет вкладку активной, а максимизирует над ней другую программу с панели задач. Есть ли решение для этого сценария? Спасибо!
user1491636

132

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

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

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


1
для меня это вызов дважды в iframe
msangel

В Firefox, если вы щелкнете внутри консоли Firebug (на той же странице), windowон потеряет фокус, что правильно, но в зависимости от того, что вы хотите, может быть не то, что вам нужно.
Маджид Фуладпур

21
Это больше не работает для текущих версий современных браузеров, см. Утвержденный ответ (Page Visibility API)
Jon z

Это решение не работает на iPad, пожалуйста, используйте событие "pageshow"
ElizaS

И BLUR, и FOCUS отключаются при загрузке страницы. Когда я открываю новое окно со своей страницы, ничего не происходит, но как только новое окно закрывается, оба события срабатывают: / (с использованием IE8)
SearchForKnowledge

49

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

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

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

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

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

  • Замена вкладки браузера на другую (точность 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>

    <Скрипт>
    / **
    Регистрирует обработчик для события для данного объекта.
    @param obj объект, который будет вызывать событие
    @param evType тип события: щелчок, нажатие клавиши, наведение мыши, ...
    @param fn функция обработчика событий
    @param isCapturing установить режим события (true = событие захвата, false = событие всплытия)
    @return true, если обработчик событий был правильно подключен
    * /
    function addEvent (obj, evType, fn, isCapturing) {
      if (isCapturing == null) isCapturing = false; 
      if (obj.addEventListener) {
        // Fire Fox
        obj.addEventListener (evType, fn, isCapturing);
        вернуть истину;
      } else if (obj.attachEvent) {
        // MSIE
        var r = obj.attachEvent ('on' + evType, fn);
        возврат г;
      } еще {
        вернуть ложь;
      }
    }

    // зарегистрироваться на потенциальное изменение видимости страницы
    addEvent (документ, "потенциальная возможность изменения", функция (событие) {
      document.getElementById ("x"). innerHTML + = "потенциалVisilityChange: потенциалHidden =" + document.potentialHidden + ", document.potentiallyHiddenSince =" + document.potentiallyHiddenSince + "s <br>";
    });

    // зарегистрироваться в API видимости страницы W3C
    var hidden = null;
    var visibilityChange = null;
    if (typeof document.mozHidden! == "undefined") {
      скрытый = "mozHidden";
      visibilityChange = "mozvisibilitychange";
    } else if (typeof document.msHidden! == "undefined") {
      скрытый = "msHidden";
      visibilityChange = "msvisibilitychange";
    } else if (typeof document.webkitHidden! == "undefined") {
      скрытый = "webkitHidden";
      visibilityChange = "webkitvisibilitychange";
    } else if (typeof document.hidden! == "hidden") {
      скрытый = «скрытый»;
      visibilityChange = "visibilitychange";
    }
    if (hidden! = null && visibilityChange! = null) {
      addEvent (document, visibilityChange, function (event) {
        document.getElementById ("x"). innerHTML + = visibilityChange + ":" + hidden + "=" + document [hidden] + "<br>";
      });
    }


    var потенциальноPageVisibility = {
      pageVisibilityChangeThreshold: 3 * 3600, // в секундах
      init: function () {
        function setAsNotHidden () {
          var dispatchEventRequired = document.potentialHidden;
          document.potentialHidden = FALSE;
          document.potentiallyHiddenSince = 0;
          if (dispatchEventRequired) dispatchPageVisibilityChangeEvent ();
        }

        function initPotentiallyHiddenDetection () {
          if (! hasFocusLocal) {
            // окно не имеет фокуса => проверка активности пользователя в окне
            lastActionDate = new Date ();
            if (timeoutHandler! = null) {
              clearTimeout (timeoutHandler);
            }
            timeoutHandler = setTimeout (checkPageVisibility, потенциальноPageVisibility.pageVisibilityChangeThreshold * 1000 + 100); // +100 мс, чтобы избежать проблем с округлением в Firefox
          }
        }

        function dispatchPageVisibilityChangeEvent () {
          unifiedVisilityChangeEventDispatchAllowed = FALSE;
          var evt = document.createEvent ("Event");
          evt.initEvent («потенциальная возможность изменения», правда, правда);
          document.dispatchEvent (EVT);
        }

        function checkPageVisibility () {
          var потенциальноHiddenDuration = (hasFocusLocal || lastActionDate == null? 0: Math.floor ((new Date (). getTime () - lastActionDate.getTime ()) / 1000));
                                        document.potentiallyHiddenSince = potentialHiddenDuration;
          if (потенциалHiddenDuration> = потенциалPageVisibility.pageVisibilityChangeThreshold &&! document.potentialHidden) {
            // порог изменения видимости страницы raiched => повысить четность
            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 ( "х") innerHTML + = "pageshow / док: <br>".
        });
        addEvent (document, "pagehide", function (event) {
          document.getElementById ( "х") innerHTML + = "pagehide / док: <br>".
        });
        addEvent (окно, "pagehow", функция (событие) {
          document.getElementById ( "х") innerHTML + = "pageshow / выиграть: <br>". // поднял, когда страница впервые показывает
        });
        addEvent (окно, "pagehide", функция (событие) {
          document.getElementById ( "х") innerHTML + = "pagehide / выиграть: <br>". // не поднят
        });
        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 (окно, «размытие», функция (событие) {
          hasFocusLocal = FALSE;
          initPotentiallyHiddenDetection ();
        });
        addEvent (окно, «фокус», функция (событие) {
          hasFocusLocal = TRUE;
          setAsNotHidden ();
        });
        setAsNotHidden ();
      }
    }

    potentialPageVisibility.pageVisibilityChangeThreshold = 4; // 4 секунды для тестирования
    potentialPageVisibility.init ();
    </ Скрипт>

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


Не приведет ли использование оператора строгого сравнения к строке «undefined» вместо неопределенного ключевого слова к ложным срабатываниям в приведенном выше коде?
Джонатон

@kiran: На самом деле это работает с Alt + Tab. Вы не можете определить, является ли страница скрытой, когда вы нажимаете Alt + Tab, потому что вы можете переключиться на меньшее окно, чтобы вы не могли гарантировать, что ваша страница полностью скрыта. Вот почему я использую понятие «потенциально скрытый» (в этом примере порог установлен на 4 секунды, поэтому вам нужно переключиться в другое окно, используя Alt + Tab, по крайней мере, на 4 секунды). Однако ваш комментарий показывает, что ответ был не очень ясен, поэтому я перефразировал его.
Жюльен Кронегг

@JulienKronegg Я думаю, что это пока лучшее решение. Однако приведенный выше код крайне нуждается в рефакторинге и абстракциях. Почему бы вам не загрузить его на GitHub и позволить сообществу провести его рефакторинг?
Джейкоб

1
@ Джейкоб Я рад, что тебе понравилось мое решение. Не стесняйтесь продвигать его в проект GitHub самостоятельно. Я даю код с лицензией Creative Commons BY creativecommons.org/licenses/by/4.0
Жюльен Кронегг

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для меня)

Принятый ответ вызвал проблемы в IE9. Эта библиотека прекрасно работает.
Том Теман

20

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

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

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


Вопрос не в видимости страницы. Речь идет о не активных / активных
gman

Я думаю, что OP не говорит о функции ide
l2aelba

1
Я тоже не говорю об ide. Я говорю о alt-tabbing / cmd-tabbing для другого приложения. Внезапно страница не активна. API видимости страницы не помогает мне узнать, не активна ли страница, а только помогает узнать, не видна ли она.
мужчина

18

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

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

2
Самое чистое решение с поддержкой IE6
Пол Купер

4
document.hasFocus()это самый чистый способ сделать это. Все остальные способы, использующие API видимости или события, основанные на событиях или ищущие различные уровни пользовательской активности / отсутствия активности, становятся слишком сложными и полными крайних случаев и дыр. поместите его в простой интервал и создайте пользовательское событие при изменении результатов. Пример: jsfiddle.net/59utucz6/1
danatcofo

1
Эффективно и в отличие от других решений дает правильную обратную связь при переключении на другую вкладку или окно браузера и даже другое приложение.
ow3n

Без сомнения, это самый чистый способ, но он не работает в Firefox
Hardik Chugh

1
Если я открою инструменты Chrome Dev, то document.hasFocus () будет равен false. Или даже если вы нажмете на верхней панели браузера, то же самое происходит. Я не уверен, что это решение подходит для приостановки видео, анимации и т. Д.
tylik

16

Я начал с использования вики-ответа сообщества, но понял, что он не обнаруживает события 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');
});

Эта версия прослушивает все различные события видимости и запускает обратный вызов, если любое из них вызывает изменение. focusedИ unfocusedобработчики убедитесь , что обратного вызова не вызывается несколько раз , если несколько API , поймать такое же изменение видимости.


У Chrome например есть и то document.hiddenи другое document.webkitHidden. Без elseв ifконструкции мы получим 2 обратных вызовов правильные?
Кристиан Вестербик

@ChristiaanWesterbeek Это хорошая мысль, я не думал об этом! Если вы можете редактировать этот пост, продолжайте, и я приму :)
Даниэль Бакмастер,

Эй, подождите минутку: редактирование, чтобы добавить «еще», предложенное ChristiaanWesterbeek и фактически добавленное @ 1.21Gigawatts, не кажется хорошей идеей: оно отвергает первоначальную покупку идеи Даниэля, которая заключается в том, чтобы попробовать все поддерживаемые методы параллельно. И нет никакого риска, что обратный вызов будет вызван дважды, потому что focus () и unfocused () подавляют дополнительные вызовы, когда ничего не меняется. На самом деле кажется, что мы должны вернуться к первой версии.
Луи Семприни

@LouisSemprini это отличный улов. Я забыл первоначальное намерение кода! Я восстановил оригинал и добавил объяснение!
Даниэль Бакмастер

проверяя это на сегодняшний день, он не обнаруживает alt + tab, по крайней мере, на Chrome 78 + macos
Hugo Gresse

7

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

  • Страница содержит фреймы, которые вы не можете контролировать
  • Вы хотите отслеживать изменение состояния видимости независимо от того, было ли изменение вызвано изменением TAB (Ctrl + Tab) или изменением окна (Alt + Tab)

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

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

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

Это способно:

  • 1) Ctrl + Tab, когда родительская страница имеет фокус: ДА
  • 2) Ctrl + Tab, когда iframe имеет фокус: ДА
  • 3) alt + tab, когда родительская страница имеет фокус: ДА
  • 4) alt + tab, когда iframe имеет фокус: NO <- облом

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

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

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

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

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

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

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


В моих тестах он также работал на IE9, IE10 и Chrome на Android.
Тони Лампада

1
Похоже, что IPAD нужно совершенно другое решение - stackoverflow.com/questions/4940657/…
Тони Лампада

3
Все эти ссылки 404s :(
Даниэль Бакмастер

6
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/


5

это работает для меня на Chrome 67, Firefox 67,

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

3

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

(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

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

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

Видеть:


2
Я думаю, что это связано с BFCache: когда пользователь нажимает кнопку «Назад» или «Вперед» - это не связано со страницей вверху рабочего стола компьютера.
неполярность

2

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

Это выполнит задачу, например, обновит страницу каждые 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);
}

Полагаться на методы фокусировки / размытия не получается (это дает много ложных срабатываний), см.
Stackoverflow.com/a/9502074/698168

2

Для решения без 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 <10; iOS <7)


как насчет поддержки браузера? на данный момент разветвляется хорошо в Chrome, Safari и Firefox.
Сельва Ганапати

1

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

Это дает дополнительное преимущество: он говорит, находится ли пользователь в режиме ожидания, а не просто проверяет, не активировано ли окно.

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


30
Если только у пользователя нет мыши.
user1686

@Annan: это codinghorror.com/blog/2007/03/… сейчас.
Чиборг

Это также не играет в кости, если пользователь смотрит видео
jamiew

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

1

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


0

Вот солидное, современное решение. (Короткая сладкая 👌🏽)

document.addEventListener("visibilitychange", () => {
  console.log( document.hasFocus() )
})

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


0

Если вы хотите действовать на размытие всего браузера : как я уже говорил, если браузер теряет фокус, ни одно из предложенных событий не срабатывает. Моя идея состоит в том, чтобы считать в цикле и сбрасывать счетчик в случае возникновения события. Если счетчик достигает предела, я делаю location.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.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.