Apakah ada cara untuk mendeteksi jika jendela browser saat ini tidak aktif?

585

Saya memiliki JavaScript yang melakukan aktivitas secara berkala. Ketika pengguna tidak melihat situs (yaitu, jendela atau tab tidak memiliki fokus), alangkah baiknya untuk tidak berjalan.

Apakah ada cara untuk melakukan ini menggunakan JavaScript?

Titik referensi saya: Obrolan Gmail memutar suara jika jendela yang Anda gunakan tidak aktif.

Luke Francl
sumber
8
Bagi mereka yang tidak puas dengan jawaban di bawah ini, periksa requestAnimationFrameAPI, atau gunakan fitur modern yang frekuensi setTimeout/ setIntervalberkurang ketika jendela tidak terlihat (1 detik di Chrome, misalnya).
Rob W
2
document.body.onblur = function (e) {console.log ('lama');} bekerja untuk elemen yang tidak fokus.
WhyMe
2
Lihat jawaban ini untuk solusi kompatibel lintas-browser yang menggunakan W3C Page Visibility API, kembali ke blur/ focusdi browser yang tidak mendukungnya.
Mathias Bynens
2
80% dari jawaban di bawah ini bukan jawaban untuk pertanyaan ini . Pertanyaan yang diajukan tentang saat ini tidak aktif tetapi banyak jawaban di bawah ini tentang tidak terlihat yang bukan merupakan jawaban untuk pertanyaan ini. Mereka bisa dibilang ditandai sebagai "bukan jawaban"
gman
Kebanyakan orang berbicara tentang tidak aktif ketika yang mereka maksud tidak aktif dan tidak terlihat . Cukup tidak aktif itu mudah - hanya menangani jendela blur/ focusacara ... yang akan digunakan terbatas, karena jendela bisa tidak aktif tetapi terlihat sepenuhnya atau sebagian (ada juga ikon "pratinjau" di beberapa bilah tugas yang orang harapkan terus berlanjut diperbarui).
rustyx

Jawaban:

691

Sejak awalnya menulis jawaban ini, spesifikasi baru telah mencapai status rekomendasi berkat W3C. The Halaman Visibilitas API (di MDN ) sekarang memungkinkan kita untuk lebih akurat mendeteksi ketika halaman tersembunyi kepada pengguna.

document.addEventListener("visibilitychange", onchange);

Dukungan browser saat ini:

  • Chrome 13+
  • Internet Explorer 10+
  • Firefox 10+
  • Opera 12.10+ [ baca catatan ]

Kode berikut jatuh kembali ke metode blur / fokus yang kurang dapat diandalkan di browser yang tidak kompatibel:

(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"});
})();

onfocusindan onfocusoutyang diperlukan untuk IE 9 dan menurunkan , sementara semua yang lain memanfaatkan onfocusdan onblur, kecuali untuk iOS, yang menggunakan onpageshowdan onpagehide.

Andy E
sumber
1
@ bellpeace: IE harus merambat focusindan focusoutdari iframe ke jendela atas. Untuk browser yang lebih baru, Anda hanya perlu menangani focusdan bluracara pada setiap objek iframe window. Anda harus menggunakan kode yang diperbarui yang baru saja saya tambahkan yang setidaknya akan mencakup kasus-kasus di browser yang lebih baru.
Andy E
3
@JulienKronegg: itu sebabnya jawaban saya secara khusus menyebutkan API Visibilitas Halaman yang memasuki status konsep kerja setelah saya menulis jawaban saya. Metode fokus / blur menyediakan fungsionalitas terbatas untuk browser lama. Mengikat ke acara lain, seperti pada jawaban Anda, tidak mencakup lebih dari ini dan lebih berisiko perbedaan perilaku (seperti IE tidak menembakkan mouseout ketika jendela muncul di bawah kursor). Saya menyarankan tindakan yang lebih tepat adalah dengan menampilkan pesan atau ikon yang menunjukkan kepada pengguna bahwa pembaruan mungkin lebih jarang karena kurangnya aktivitas halaman.
Andy E
6
@AndyE Saya mencoba solusi ini pada chromium. Ini berfungsi jika saya mengubah tab, tetapi tidak jika saya mengubah windows (ALT + tab). Haruskah itu Inilah biola - jsfiddle.net/8a9N6/17
Tony Lâmpada
2
@Heliodor: Saya ingin menyimpan kode dalam jawaban minimal untuk saat ini. Itu tidak pernah dimaksudkan untuk menjadi solusi lengkap cut-and-paste, karena implementor mungkin ingin menghindari pengaturan kelas pada tubuh dan mengambil tindakan yang sama sekali berbeda (seperti menghentikan dan memulai timer).
Andy E
8
@AndyE Solusi Anda tampaknya hanya berfungsi jika pengguna mengubah tab, atau meminimalkan / memaksimalkan jendela. Namun, acara onchange tidak dipicu jika pengguna membiarkan tab aktif, tetapi memaksimalkan program lain darinya dari taskbar. Apakah ada solusi untuk skenario itu? Terima kasih!
user1491636
132

Saya akan menggunakan jQuery karena semua yang harus Anda lakukan adalah ini:

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

Atau setidaknya itu berhasil untuk saya.

Carson Wright
sumber
1
bagi saya panggilan ini dua kali dalam iframe
msangel
Di Firefox, jika Anda mengklik di dalam konsol pembakar (pada halaman yang sama), windowfokus akan longgar, yang benar, tetapi tergantung pada apa niat Anda mungkin bukan yang Anda butuhkan.
Majid Fouladpour
21
Ini tidak lagi berfungsi untuk versi browser modern saat ini, lihat jawaban yang disetujui (Page Visibility API)
Jon z
Solusi ini tidak berfungsi di iPad silakan gunakan acara "pageshow"
ElizaS
BLUR dan FOCUS akan menyala saat halaman dimuat. Ketika saya membuka jendela baru dari halaman saya, tidak ada yang terjadi tetapi setelah jendela baru menutup kedua acara tersebut akan dimatikan: / (menggunakan IE8)
SearchForKnowledge
49

Ada 3 metode khas yang digunakan untuk menentukan apakah pengguna dapat melihat halaman HTML, namun tidak ada yang bekerja dengan sempurna:

  • The W3C Halaman Visibilitas API seharusnya melakukan hal ini (didukung sejak: Firefox 10, MSIE 10, Chrome 13). Namun, API ini hanya memunculkan peristiwa-peristiwa ketika tab browser sepenuhnya ditimpa (misalnya ketika pengguna berubah dari satu tab ke yang lain). API tidak memunculkan peristiwa ketika visibilitas tidak dapat ditentukan dengan akurasi 100% (misalnya Alt + Tab untuk beralih ke aplikasi lain).

  • Menggunakan metode fokus / blur memberi Anda banyak kesalahan positif. Misalnya, jika pengguna menampilkan jendela yang lebih kecil di atas jendela browser, jendela browser akan kehilangan fokus ( onblurdinaikkan) tetapi pengguna masih dapat melihatnya (jadi masih perlu di-refresh). Lihat juga http://javascript.info/tutorial/focus

  • Mengandalkan aktivitas pengguna (gerakan mouse, klik, ketikan kunci) memberi Anda banyak false positive juga. Pikirkan kasus yang sama seperti di atas, atau pengguna yang menonton video.

Untuk meningkatkan perilaku tidak sempurna yang dijelaskan di atas, saya menggunakan kombinasi dari 3 metode: W3C Visibility API, lalu fokus / blur dan metode aktivitas pengguna untuk mengurangi tingkat positif palsu. Ini memungkinkan untuk mengelola acara berikut:

  • Mengubah tab browser ke yang lain (akurasi 100%, berkat W3C Page Visibility API)
  • Halaman berpotensi disembunyikan oleh jendela lain, misalnya karena Alt + Tab (probabilitas = tidak 100% akurat)
  • Perhatian pengguna berpotensi tidak terfokus pada halaman HTML (probabilitas = tidak 100% akurat)

Beginilah cara kerjanya: ketika dokumen kehilangan fokus, aktivitas pengguna (seperti gerakan mouse) pada dokumen dimonitor untuk menentukan apakah jendela terlihat atau tidak. Probabilitas visibilitas halaman berbanding terbalik dengan waktu aktivitas pengguna terakhir pada halaman: jika pengguna tidak membuat aktivitas pada dokumen untuk waktu yang lama, halaman kemungkinan besar tidak terlihat. Kode di bawah ini meniru API Visibilitas Halaman W3C: berperilaku dengan cara yang sama tetapi memiliki tingkat positif palsu yang kecil. Ini memiliki keuntungan menjadi multibrowser (diuji pada Firefox 5, Firefox 10, MSIE 9, MSIE 7, Safari 5, Chrome 9).

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

    <script>
    / **
    Mendaftarkan pawang ke acara untuk objek yang diberikan.
    @param keberatan objek yang akan memunculkan acara
    @param, ketikkan jenis acara: klik, tekan tombol, tetikus, ...
    @param fn fungsi event handler
    @param isCapturing mengatur mode acara (true = menangkap acara, false = acara menggelegak)
    @ kembali benar jika pengendali acara telah terpasang dengan benar
    * /
    function addEvent (obj, evType, fn, isCapturing) {
      if (isCapturing == null) isCapturing = false; 
      if (obj.addEventListener) {
        // Firefox
        obj.addEventListener (evType, fn, isCapturing);
        kembali benar;
      } lain jika (obj.attachEvent) {
        // MSIE
        var r = obj.attachEvent ('on' + evType, fn);
        return r;
      } lain {
        return false;
      }
    }

    // daftar ke perubahan visibilitas halaman potensial
    addEvent (dokumen, "perubahan potensivisilitas", fungsi (acara) {
      document.getElementById ("x"). innerHTML + = "potentialVisilityChange: potentialHidden =" + document.potentialHidden + ", document.potentialHiddenSince =" + document.potentialHiddenSince + "s <br>";
    });

    // daftar ke API Visibilitas Halaman W3C
    var hidden = null;
    var visibilityChange = null;
    if (typeof document.mozHidden! == "undefined") {
      hidden = "mozHidden";
      visibilityChange = "mozvisibilitychange";
    } lain jika (typeof document.msHidden! == "undefined") {
      hidden = "msHidden";
      visibilityChange = "msvisibilitychange";
    } lain jika (typeof document.webkitHidden! == "undefined") {
      hidden = "webkitHidden";
      visibilityChange = "webkitvisibilitychange";
    } lain jika (typeof document.hidden! == "disembunyikan") {
      hidden = "hidden";
      visibilityChange = "visibilitychange";
    }
    if (hidden! = null && visibilityChange! = null) {
      addEvent (dokumen, perubahan visibilitas, fungsi (acara) {
        document.getElementById ("x"). innerHTML + = visibilityChange + ":" + hidden + "=" + dokumen [tersembunyi] + "<br>";
      });
    }


    var potentialPageVisibility = {
      pageVisibilityChangeThreshold: 3 * 3600, // dalam detik
      init: function () {
        fungsi setAsNotHidden () {
          var dispatchEventRequired = document.potentialHidden;
          document.potentialHidden = false;
          document.potentialHiddenSince = 0;
          if (dispatchEventRequired) dispatchPageVisibilityChangeEvent ();
        }

        function initPotentialHiddenDetection () {
          if (! hasFocusLocal) {
            // jendela tidak memiliki fokus => periksa aktivitas pengguna di jendela
            lastActionDate = Tanggal baru ();
            if (timeoutHandler! = null) {
              clearTimeout (timeoutHandler);
            }
            timeoutHandler = setTimeout (checkPageVisibility, potentialPageVisibility.pageVisibilityChangeThreshold * 1000 + 100); // +100 ms untuk menghindari masalah pembulatan di bawah 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 ((Tanggal baru (). getTime () - lastActionDate.getTime ()) / 1000));
                                        document.potentialHiddenSince = potentialHiddenDuration;
          if (potentialHiddenDuration> = potentialPageVisibility.pageVisibilityChangeThreshold &&! document.potentialHidden) {
            // ambang perubahan visibilitas halaman raiched => naikkan genap
            document.potentialHidden = true;
            dispatchPageVisibilityChangeEvent ();
          }
        }

        var lastActionDate = null;
        var hasFocusLocal = true;
        var hasMouseOver = true;
        document.potentialHidden = false;
        document.potentialHiddenSince = 0;
        var timeoutHandler = null;

        addEvent (dokumen, "pageshow", fungsi (acara) {
          document.getElementById ("x"). innerHTML + = "pageshow / doc: <br>";
        });
        addEvent (dokumen, "pagehide", function (event) {
          document.getElementById ("x"). innerHTML + = "pagehide / doc: <br>";
        });
        addEvent (jendela, "pageshow", fungsi (acara) {
          document.getElementById ("x"). innerHTML + = "pageshow / win: <br>"; // terangkat saat halaman pertama kali ditampilkan
        });
        addEvent (window, "pagehide", function (event) {
          document.getElementById ("x"). innerHTML + = "pagehide / win: <br>"; // tidak dibangkitkan
        });
        addEvent (dokumen, "mousemove", fungsi (acara) {
          lastActionDate = Tanggal baru ();
        });
        addEvent (dokumen, "mouseover", fungsi (acara) {
          hasMouseOver = true;
          setAsNotHidden ();
        });
        addEvent (dokumen, "mouseout", fungsi (acara) {
          hasMouseOver = false;
          initPotentialHiddenDetection ();
        });
        addEvent (window, "blur", function (event) {
          hasFocusLocal = false;
          initPotentialHiddenDetection ();
        });
        addEvent (jendela, "fokus", fungsi (acara) {
          hasFocusLocal = true;
          setAsNotHidden ();
        });
        setAsNotHidden ();
      }
    }

    potentialPageVisibility.pageVisibilityChangeThreshold = 4; // 4 detik untuk pengujian
    potentialPageVisibility.init ();
    </script>

Karena saat ini tidak ada solusi lintas-browser yang berfungsi tanpa false positive, Anda sebaiknya berpikir dua kali untuk menonaktifkan aktivitas berkala di situs web Anda.

Julien Kronegg
sumber
Tidak akan menggunakan operator perbandingan ketat pada string 'tidak terdefinisi' alih-alih kata kunci tidak terdefinisi menyebabkan positif palsu dalam kode di atas?
Jonathon
@kiran: Sebenarnya ini bekerja dengan Alt + Tab. Anda tidak dapat menentukan apakah halaman disembunyikan ketika Anda melakukan Alt + Tab karena Anda dapat beralih ke jendela yang lebih kecil sehingga Anda tidak dapat menjamin bahwa halaman Anda sepenuhnya tersembunyi. Inilah sebabnya saya menggunakan gagasan "berpotensi disembunyikan" (dalam contoh, ambang batas diatur ke 4 detik, jadi Anda perlu beralih ke jendela lain menggunakan Alt + Tab selama minimal 4 detik). Namun komentar Anda menunjukkan bahwa jawabannya tidak begitu jelas, jadi saya ulangi.
Julien Kronegg
@JulienKronegg Saya pikir ini adalah solusi terbaik. Namun, kode di atas sangat membutuhkan beberapa refactoring dan abstraksi. Mengapa Anda tidak mengunggahnya ke GitHub dan membiarkan komunitas refactoring?
Yakub
1
@ Jacob Saya senang Anda menyukai solusi saya. Jangan ragu untuk mempromosikannya menjadi proyek GitHub sendiri. Saya memberikan kode dengan lisensi Creative Commons BY creativecommons.org/licenses/by/4.0
Julien Kronegg
26

Ada perpustakaan yang rapi tersedia di GitHub:

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

Contoh:

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

Saya telah menguji versi 1.0.1 di semua browser yang saya miliki dan dapat mengonfirmasi bahwa itu berfungsi dengan:

  • IE9, IE10
  • FF 26.0
  • Chrome 34.0

... dan mungkin semua versi yang lebih baru.

Tidak sepenuhnya bekerja dengan:

  • IE8 - selalu menunjukkan bahwa tab / jendela sedang aktif ( .now()selalu kembali trueuntuk saya)
omnomnom
sumber
Jawaban yang diterima menyebabkan masalah di IE9. Perpustakaan ini berfungsi dengan baik.
Tom Teman
20

Menggunakan: Halaman Visibilitas API

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

Bisakah saya menggunakan? http://caniuse.com/#feat=pagevisibility

l2aelba
sumber
Pertanyaannya bukan tentang visibilitas halaman. Ini tentang tidak aktif / aktif
GM
Saya pikir OP tidak berbicara tentang fungsi ide
l2aelba
1
Saya juga tidak berbicara tentang ide. Saya berbicara tentang alt-tabbing / cmd-tabbing ke aplikasi lain. Tiba-tiba halaman itu tidak aktif. Api visibilitas halaman tidak membantu saya mengetahui jika halaman tidak aktif, itu hanya membantu saya mengetahui apakah itu mungkin tidak terlihat.
GM
18

Saya membuat Obrolan Komet untuk aplikasi saya, dan ketika saya menerima pesan dari pengguna lain saya menggunakan:

if(new_message){
    if(!document.hasFocus()){
        audio.play();
        document.title="Have new messages";
    }
    else{
        audio.stop();
        document.title="Application Name";
    } 
}
infinito84
sumber
2
Solusi terbersih dengan dukungan kembali ke IE6
Paul Cooper
4
document.hasFocus()adalah cara paling bersih untuk melakukannya. Semua cara lain menggunakan api visibilitas atau peristiwa berbasis atau mencari berbagai tingkat aktivitas pengguna / kurangnya aktivitas menjadi terlalu rumit dan penuh dengan kasus tepi dan lubang. letakkan di interval sederhana dan angkat acara khusus saat hasilnya berubah. Contoh: jsfiddle.net/59utucz6/1
danatcofo
1
Efisien, dan tidak seperti solusi lain, memberikan umpan balik yang benar ketika Anda beralih ke tab atau jendela browser lain, dan bahkan aplikasi yang berbeda.
ow3n
Tidak diragukan lagi, ini adalah cara terbersih, tetapi tidak bekerja di firefox
hardik chugh
1
Jika saya membuka alat Chrome Dev maka document.hasFocus () sama dengan false. Atau bahkan jika Anda mengklik pada panel atas browser, hal yang sama terjadi. Saya tidak yakin solusi ini cocok untuk menjeda video, animasi, dll
tylik
16

Saya mulai menggunakan jawaban wiki komunitas, tetapi menyadari bahwa itu tidak mendeteksi acara alt-tab di Chrome. Ini karena ia menggunakan sumber acara pertama yang tersedia, dan dalam hal ini adalah API visibilitas halaman, yang di Chrome tampaknya tidak melacak tab-alt.

Saya memutuskan untuk memodifikasi skrip sedikit untuk melacak semua peristiwa yang mungkin terjadi untuk perubahan fokus halaman. Inilah fungsi yang bisa Anda singgahi:

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

Gunakan seperti ini:

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

Versi ini mendengarkan semua peristiwa visibilitas yang berbeda dan mengaktifkan panggilan balik jika ada dari mereka yang menyebabkan perubahan. The focuseddan unfocusedpenangan memastikan bahwa callback tidak disebut beberapa kali jika beberapa API menangkap perubahan visibilitas yang sama.

Daniel Buckmaster
sumber
Chrome misalnya memiliki keduanya document.hiddendan document.webkitHidden. Tanpa elsedalam ifkonstruksi kita akan mendapatkan 2 panggilan balik kan?
Christiaan Westerbeek
@ChristiaanWesterbeek Itu poin yang bagus, saya tidak memikirkan itu! Jika Anda dapat mengedit posting ini, silakan dan saya akan menerima :)
Daniel Buckmaster
Eh, tunggu dulu: edit untuk menambahkan "lain" yang disarankan oleh ChristiaanWesterbeek dan sebenarnya ditambahkan oleh @ 1.21Gigawatt sepertinya bukan ide yang bagus: ini mengalahkan pembelian asli ide Daniel, yaitu mencoba semua yang didukung metode secara paralel. Dan tidak ada risiko panggilan balik dipanggil dua kali karena fokus () dan tidak fokus () menekan panggilan tambahan ketika tidak ada yang berubah. Benar-benar sepertinya kita harus kembali ke putaran pertama.
Louis Semprini
@LouisSemprini itu tangkapan yang bagus. Saya lupa maksud asli kode! Saya telah memulihkan yang asli dan menambahkan penjelasan!
Daniel Buckmaster
memeriksa ini pada hari ini, ia tidak mendeteksi alt + tab setidaknya di Chrome 78 + macos
Hugo Gresse
7

Ini sangat sulit. Tampaknya tidak ada solusi mengingat persyaratan berikut.

  • Halaman ini mencakup iframe yang tidak dapat Anda kendalikan
  • Anda ingin melacak perubahan status visibilitas terlepas dari perubahan yang dipicu oleh perubahan TAB (tab ctrl +) atau perubahan jendela (alt + tab)

Ini terjadi karena:

  • Halaman Visibility API andal dapat memberi tahu Anda tentang perubahan tab (bahkan dengan iframe), tetapi tidak dapat memberi tahu Anda saat pengguna mengubah windows.
  • Mendengarkan peristiwa blur / fokus jendela dapat mendeteksi alt + tab dan ctrl + tab, selama iframe tidak memiliki fokus.

Dengan adanya batasan ini, dimungkinkan untuk mengimplementasikan solusi yang menggabungkan - Halaman Visibilitas API - window blur / focus - document.activeElement

Itu mampu:

  • 1) tab ctrl + ketika halaman induk memiliki fokus: YA
  • 2) tab ctrl + ketika iframe memiliki fokus: YES
  • 3) alt + tab ketika halaman induk memiliki fokus: YA
  • 4) alt + tab ketika iframe memiliki fokus: NO <- gelandangan

Ketika iframe memiliki fokus, acara blur / fokus Anda tidak dipanggil sama sekali, dan laman Visibilitas API tidak akan memicu pada alt + tab.

Saya membangun di atas solusi @ AndyE dan menerapkan solusi (hampir baik) ini di sini: https://dl.dropboxusercontent.com/u/2683925/estante-components/visibility_test1.html (maaf, saya punya masalah dengan JSFiddle).

Ini juga tersedia di Github: https://github.com/qmagico/estante-components

Ini bekerja pada chrome / chromium. Jenisnya berfungsi pada firefox, kecuali ia tidak memuat konten iframe (ada ide mengapa?)

Bagaimanapun, untuk menyelesaikan masalah terakhir (4), satu-satunya cara yang dapat Anda lakukan adalah mendengarkan acara blur / fokus pada iframe. Jika Anda memiliki kontrol atas iframe, Anda dapat menggunakan API postMessage untuk melakukannya.

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

Saya masih belum menguji ini dengan cukup banyak browser. Jika Anda dapat menemukan lebih banyak info tentang tempat ini tidak berfungsi, beri tahu saya di komentar di bawah.

Tony Lâmpada
sumber
Dalam pengujian saya ini juga bekerja pada IE9, IE10 dan Chrome di Android.
Tony Lâmpada
1
Sepertinya iPad membutuhkan solusi yang sama sekali berbeda - stackoverflow.com/questions/4940657/…
Tony Lâmpada
3
Semua tautan ini berusia 404an :(
Daniel Buckmaster
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/

yaart
sumber
5

ini bekerja untuk saya di chrome 67, firefox 67,

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

kamu bisa menggunakan:

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

})();
maryam
sumber
3

Di HTML 5 Anda juga bisa menggunakan:

  • onpageshow: Script untuk dijalankan ketika jendela menjadi terlihat
  • onpagehide: Script untuk dijalankan ketika jendela disembunyikan

Lihat:

roberkules
sumber
2
Saya pikir ini terkait dengan BFCache: ketika pengguna mengklik Kembali atau Maju - ini tidak terkait dengan halaman yang berada di bagian atas desktop komputer.
nonopolaritas
2

Ini adalah adaptasi dari jawaban dari Andy E.

Ini akan melakukan tugas, mis. Menyegarkan halaman setiap 30 detik, tetapi hanya jika halaman tersebut terlihat dan fokus.

Jika visibilitas tidak dapat dideteksi, maka hanya fokus yang akan digunakan.

Jika pengguna memfokuskan halaman, maka itu akan segera diperbarui

Halaman tidak akan diperbarui lagi hingga 30 detik setelah panggilan 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);
}
roger
sumber
Mengandalkan metode fokus / blur tidak berfungsi (ini memberi Anda banyak false positive), lihat stackoverflow.com/a/9502074/698168
Julien Kronegg
2

Untuk solusi tanpa jQuery, periksa Visibility.js yang menyediakan informasi tentang tiga status halaman

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

dan juga pembungkus kenyamanan untuk 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();
});

Fallback untuk browser lama (IE <10; iOS <7) juga tersedia

Niko
sumber
bagaimana dengan dukungan browser? untuk sekarang forking bagus di chrome, safari, dan firefox.
Selva Ganapathi
1

Cara yang sedikit lebih rumit akan digunakan setInterval()untuk memeriksa posisi mouse dan membandingkannya dengan pemeriksaan terakhir. Jika mouse belum bergerak dalam waktu yang ditentukan, pengguna mungkin menganggur.

Ini memiliki keuntungan tambahan dengan memberi tahu jika pengguna menganggur, alih-alih hanya memeriksa jika jendela tidak aktif.

Seperti yang telah ditunjukkan oleh banyak orang, ini tidak selalu merupakan cara yang baik untuk memeriksa apakah pengguna atau jendela peramban menganggur, karena pengguna bahkan mungkin tidak menggunakan mouse atau menonton video, atau serupa. Saya hanya menyarankan satu cara yang mungkin untuk memeriksa idle-ness.

Austin Hyde
sumber
30
Kecuali jika pengguna tidak memiliki mouse.
user1686
@Annan: Ini codinghorror.com/blog/2007/03/... sekarang.
chiborg
Ini juga tidak bermain dadu jika pengguna menonton video
jamiew
Anda dapat menggunakan onkeypress atau acara serupa lainnya untuk mereset timer dan menyelesaikan masalah non-mouse. Tentu saja itu tidak akan berfungsi bagi pengguna yang secara aktif melihat halaman untuk menonton video, mempelajari gambar, dll.
joshuahedlund
1

Untuk angular.js, berikut adalah arahan (berdasarkan jawaban yang diterima) yang akan memungkinkan controller Anda bereaksi terhadap perubahan visibilitas:

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

});

Anda dapat menggunakannya seperti contoh ini:, di <div react-on-window-focus="refresh()">mana refresh()fungsi lingkup dalam lingkup apa pun Pengontrol berada dalam ruang lingkup.

Steve Campbell
sumber
0

Inilah solusi yang solid dan modern. (Pendek manis 👌🏽)

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

Ini akan mengatur pendengar untuk memicu ketika peristiwa visibilitas dipecat yang bisa menjadi fokus atau kabur.

Cory Robinson
sumber
0

Jika Anda ingin bertindak pada seluruh peramban blur : Seperti yang saya komentari, jika peramban kehilangan fokus, tidak ada acara yang disarankan yang diaktifkan. Ide saya adalah untuk menghitung dalam satu lingkaran dan mengatur ulang penghitung jika suatu peristiwa terjadi. Jika penghitung mencapai batas saya melakukan location.href ke halaman lain. Ini juga menyala jika Anda bekerja pada 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();

Ini adalah konsep yang berhasil diuji pada FF.

BF
sumber