Чи є спосіб виявити, чи вікно браузера наразі не активне?


585

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

Чи можна це зробити за допомогою JavaScript?

Мій орієнтир: Gmail Chat відтворює звук, якщо вікно, яке ви використовуєте, не активне.


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

2
document.body.onblur = function (e) {console.log ('lama');} працював для не зосереджених елементів.
Чому

2
Дивіться цю відповідь на рішення, сумісне для веб-переглядачів, яке використовує API видимості сторінки W3C, повертаючись до blur/ focusв браузерах, які не підтримують його.
Mathias Bynens

2
80% відповідей нижче - це не відповіді на це питання . Питання про неактивний на даний момент момент, але тонни відповідей нижче приблизно не видно, що не є відповіддю на це питання. Вони, мабуть, повинні бути позначені як "не відповідь"
gman

Відповіді:


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від рамки кадрів до верхнього вікна. Для нових браузерів, ви просто повинні обробляти focusі blurподія на кожен Iframe в windowоб'єкті. Вам слід скористатись оновленим кодом, який я щойно додав, який принаймні охоплюватиме ці випадки у новіших браузерах.
Енді Е

3
@JulienKronegg: саме тому у моїй відповіді конкретно згадується API Visibility Page, який увійшов у стан робочого проекту після того, як я спочатку написав свою відповідь. Методи фокусування / розмивання забезпечують обмежений функціонал для старих браузерів. Прив’язка до інших подій, як у вашій відповіді, не охоплює набагато більше, ніж це, і більше загрожує поведінковими відмінностями (наприклад, IE не запускає миші, коли під курсором вискакує вікно). Я б припустив, що більш прийнятною дією було б відображення повідомлення чи піктограми, що вказує користувачеві, що оновлення можуть бути рідше через неактивність сторінки.
Енді Е

6
@AndyE Я спробував це рішення на хромі. Він працює, якщо я змінюю вкладки, але це не так, якщо я змінюю вікна (ALT + вкладка). Чи слід? Ось загадка
Tony Lâmpada

2
@Heliodor: Я хотів би поки що код у відповіді мінімальний. Це ніколи не було задумом для вирізання та вставки, оскільки виконавці можуть захотіти уникати встановлення класу на корпусі та взагалі вживати зовсім інші дії (наприклад, зупинка та запуск таймера).
Енді Е

8
@AndyE Здається, ваше рішення працює лише в тому випадку, якщо користувач змінює вкладки або мінімізує / максимізує вікно. Однак подія onchange не спрацьовує, якщо користувач залишає активну вкладку, але максимізує іншу програму над нею на панелі завдань. Чи є рішення для цього сценарію? Дякую!
користувач1491636

132

Я б використовував jQuery, тому що тоді все, що вам потрібно зробити, це:

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

Або принаймні це працювало на мене.


1
для мене цей дзвінок двічі в iframe
msangel

Якщо ви натиснете всередину консолі firebug (на тій самій сторінці), Firefox windowвтратить фокус, що правильно, але залежно від того, який ваш намір може бути не потрібним.
Маджід Фуладпур

21
Це більше не працює для поточних версій сучасних браузерів, дивіться схвалену відповідь (Page Visibility API)
Jon z

Це рішення не працює на iPad, будь ласка, скористайтеся подією "showhow"
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

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

Для покращення описаної вище недосконалої поведінки я використовую комбінацію трьох методів: API видимості W3C, потім фокусування / розмиття та методи діяльності користувачів, щоб зменшити помилкову позитивну швидкість. Це дозволяє керувати такими подіями:

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

Так це працює: коли документ втрачає фокус, активність користувача (наприклад, переміщення миші) на документі відстежується, щоб визначити, вікно видно чи ні. Ймовірність видимості сторінки обернено пропорційна часу останньої активності користувача на сторінці: якщо користувач довго не здійснює жодної активності в документі, сторінка, ймовірно, не видно. Код нижче імітує API видимості сторінки W3C: він поводиться так само, але має невелику помилкову позитивну швидкість. Вона має перевагу в тому, що вона є мультисекреторною (тестується на Firefox 5, Firefox 10, MSIE 9, MSIE 7, Safari 5, Chrome 9).

    <div id = "x"> </div>

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

    // зареєструватися на потенційній зміні видимості сторінки
    addEvent (документ, "потенційна зміна змін", функція (подія) {
      document.getElementById ("x"). innerHTML + = "potencialVisilityChange: potencialHidden =" + document.potentialHidden + ", document.potentialHiddenSince =" + document.potentialHiddenSince + "s <br>";
    });

    // зареєструватися в API видимості сторінки W3C
    var схований = 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") {
      hidden = "webkitHidden";
      visibilityChange = "webkitvisibilitychange";
    } else if (typeof document.hidden! == "приховано") {
      hidden = "приховано";
      visibilityChange = "видимістьзмінити";
    }
    if (приховано! = null && visibilityChange! = null) {
      addEvent (документ, видимістьЗміни, функція (подія) {
        document.getElementById ("х"). innerHTML + = visibilityChange + ":" + прихований + "=" + документ [приховано] + "<br>";
      });
    }


    var потенціалPageVisibility = {
      pageVisibilityChangeThreshold: 3 * 3600, // за секунди
      init: function () {
        набір функційAsNotHidden () {
          var dispatchEventRequired = document.potentialHidden;
          document.potentialHidden = хибний;
          документ.потенційноHiddenSince = 0;
          if (dispatchEventRequired) dispatchPageVisibilityChangeEvent ();
        }

        функція initPotenicallyHiddenDetection () {
          if (! hasFocusLocal) {
            // у вікні немає фокуса => перевірка активності користувача у вікні
            lastActionDate = нова дата ();
            якщо (timeoutHandler! = null) {
              clearTimeout (timeoutHandler);
            }
            timeoutHandler = setTimeout (checkPageVisibility, potencijalPageVisibility.pageVisibilityChangeThreshold * 1000 + 100); // +100 мс, щоб уникнути проблем із округленням у Firefox
          }
        }

        функція dispatchPageVisibilityChangeEvent () {
          unifiedVisilityChangeEventDispatchAllowed = false;
          var evt = document.createEvent ("Подія");
          evt.initEvent ("зміна потенційної стійкості", істина, істина);
          document.dispatchEvent (evt);
        }

        функція checkPageVisibility () {
          var потенціалHiddenDuration = (hasFocusLocal || lastActionDate == null? 0: Math.floor ((нова дата (). getTime () - lastActionDate.getTime ()) / 1000));
                                        документ.потенційноHiddenSince = потенціалHiddenDuration;
          if (potencijalHiddenDuration> = potencijalPageVisibility.pageVisibilityChangeThreshold &&! document.potentialHidden) {
            // Поріг зміни видимості сторінки raiched => підвищити рівний
            document.potentialHidden = вірно;
            dispatchPageVisibilityChangeEvent ();
          }
        }

        var lastActionDate = null;
        var hasFocusLocal = true;
        var hasMouseOver = вірно;
        document.potentialHidden = хибний;
        документ.потенційноHiddenSince = 0;
        var timeoutHandler = null;

        addEvent (документ, "показ сторінки", функція (подія) {
          document.getElementById ("x"). innerHTML + = "pageshow / doc: <br>";
        });
        addEvent (документ, "pagehide", функція (подія) {
          document.getElementById ("х"). innerHTML + = "pagehide / doc: <br>";
        });
        addEvent (вікно, "показ сторінок", функція (подія) {
          document.getElementById ("х"). innerHTML + = "сторінку show / win: <br>"; // піднімається, коли сторінка відображається вперше
        });
        addEvent (вікно, "pagehide", функція (подія) {
          document.getElementById ("х"). innerHTML + = "pagehide / win: <br>"; // не піднятий
        });
        addEvent (документ, "переміщення миші", функція (подія) {
          lastActionDate = нова дата ();
        });
        addEvent (документ, "перехід миші", функція (подія) {
          hasMouseOver = true;
          setAsNotHidden ();
        });
        addEvent (документ, "миші", функція (подія) {
          hasMouseOver = false;
          initPotenicallyHiddenDetection ();
        });
        addEvent (вікно, "розмиття", функція (подія) {
          hasFocusLocal = хибний;
          initPotenicallyHiddenDetection ();
        });
        addEvent (вікно, "фокус", функція (подія) {
          hasFocusLocal = вірно;
          setAsNotHidden ();
        });
        setAsNotHidden ();
      }
    }

    potencijalPageVisibility.pageVisibilityChangeThreshold = 4; // 4 секунди для тестування
    potencijalPageVisibility.init ();
    </script>

Оскільки в даний час не існує робочого крос-браузерного рішення без помилкового позитиву, вам слід подумати двічі про відключення періодичної активності на вашому веб-сайті.


Чи не використання суворого оператора порівняння для рядка 'undefined' замість undefined ключового слова не спричинить помилкових позитивів у наведеному вище коді?
Джонатан

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

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

1
@Jacob Я щасливий, що тобі сподобалось моє рішення. Не соромтеся самостійно просувати його у проект 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

Я думаю, що ОП не говорить про функцію ide
l2aelba

1
Я також не говорю про іде. Я говорю про alt-tabbing / cmd-tabbing для іншої програми. Раптом сторінка не активна. Api видимості сторінки не допомагає мені знати, якщо сторінка не активна, вона лише допомагає мені знати, чи можливо, це не видно.
gman

18

Я створюю чат на комету для своєї програми, і коли я отримую повідомлення від іншого користувача, я використовую:

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, здається, не відслідковує вкладку альт.

Я вирішив трохи змінити сценарій, щоб відстежувати всі можливі події для зміни фокусу сторінки. Ось функція, яку можна скинути:

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, не здається гарною ідеєю: воно перемагає оригінальну покупку ідеї Даніеля, а саме спробувати всі підтримувані методи паралельно. І немає ніякого ризику виклику зворотного дзвінка двічі, оскільки сфокусований () та нефокусований () пригнічують додаткові дзвінки, коли нічого не змінюється. Дійсно здається, що нам слід повернутися до першої версії.
Луї Семпріні

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

перевіривши це станом на сьогодні, він не виявляє
Hugo Gresse

7

Це справді хитро. Здається, не існує рішення з урахуванням наступних вимог.

  • Сторінка містить рамки кадрів, над якими ви не маєте контролю
  • Ви хочете відстежувати зміну стану видимості незалежно від зміни, викликаної зміною TAB (ctrl + вкладка) або зміною вікна (alt + tab)

Це відбувається тому, що:

  • API видимості сторінки може надійно повідомити вам про зміну вкладки (навіть із iframes), але він не може визначити, коли користувач змінює вікна.
  • Прослуховування подій розмивання / фокусування вікна може виявити вкладки alt + та ctrl +, якщо у кадрі iframe немає фокусу.

Враховуючи ці обмеження, можна реалізувати рішення, яке поєднує в собі - API видимості сторінки - розмиття / фокус вікна - document.activeElement

Це вміє:

  • 1) вкладка ctrl +, коли головна сторінка має фокус: ТАК
  • 2) клавіша ctrl +, якщо фокус рамки iframe: ТАК
  • 3) вкладка alt +, коли головна сторінка має фокус: ТАК
  • 4) клавіша alt +, якщо фокус iframe має фокус: NO <- промінь

Якщо фокус iframe має фокус, події розмиття / фокусування взагалі не викликаються, а API видимості сторінки не запускатиметься на вкладці 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

Я все ще не перевіряв це достатньою кількістю браузерів. Якщо ви можете знайти більше інформації про те, де це не працює, повідомте мене про це у коментарях нижче.


У моїх тестах він також працював на IE9, IE10 та Chrome на Android.
Tony Lâmpada

1
Здається, IPAD потребує зовсім іншого рішення - stackoverflow.com/questions/4940657/…
Тоні лампаду

3
Усі ці посилання 404-х років :(
Даніель Бакмастер

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)


як щодо підтримки браузера? поки що розщеплення добре в хромі, сафарі та фаєрфоксі.
Сельва Ганапаті

1

Трохи складнішим способом було б скористатися setInterval()для перевірки положення миші та порівняння з останньою перевіркою. Якщо миша не перемістилася протягом певного часу, користувач, ймовірно, не працює.

Це має додаткову перевагу в повідомленні, якщо користувач простоює, а не просто перевіряє, чи вікно неактивне.

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


30
Якщо у користувача немає миші.
користувач1686


Це також не грає у кістки, якщо користувач переглядає відео
jamiew

ви можете використовувати onkeypress або інші подібні події, щоб скинути таймер та вирішити проблему, що не стосується миші. Звичайно, користувачі, які активно переглядають сторінку, все одно не працюватимуть, щоб подивитися відео, вивчити зображення тощо
joshuahedlund

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 на іншу сторінку. Це також спрацьовує, якщо ви працюєте на інструментах розробки.

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.