Deteksi Jika Tab Browser Berfokus

149

Apakah ada cara lintas-browser yang dapat diandalkan untuk mendeteksi bahwa tab memiliki fokus.

Skenarionya adalah kami memiliki aplikasi yang melakukan polling secara teratur untuk harga saham, dan jika halaman tersebut tidak memiliki fokus, kami dapat menghentikan polling dan menyelamatkan semua orang dari kebisingan lalu lintas, terutama karena orang-orang penggemar membuka beberapa tab dengan portofolio yang berbeda.

Apakah window.onblurdan window.onfocusopsi untuk ini?

Fenton
sumber

Jawaban:

127

Ya, window.onfocusdan window.onblurharus bekerja untuk skenario Anda:

http://www.thefutureoftheweb.com/blog/detect-browser-window-focus

Ryan Wright
sumber
3
Aspek onfocusin / onfocusout ini, dan juga catatan tentang memberi tahu pengguna yang telah Anda jeda adalah catatan yang benar-benar bagus. Terima kasih.
Fenton
7
Harap dicatat bahwa Anda tidak dapat membedakan antara halaman yang aktif atau tidak aktif saat memuat halaman dengan cara ini.
pimvdb
@SteveFenton - onfocusadalah crossbrowser, di mana acara yang Anda sebutkan hanya untuk IE, saya tidak bisa melihat mengapa ini dianggap sebagai catatan yang baik bagi Anda ..
vsync
1
@vsync - baca artikel terkait, Anda akan melihatnya menggunakan kedua 'onfocusin' dan 'onfocus'.
Fenton
Bisakah Anda setidaknya menyebutkan perbedaan antara keduanya?
Lenar Hoyt
53

Edit Penting: Jawaban ini sudah usang. Sejak menulisnya, Visibility API ( mdn , example , spec ) telah diperkenalkan. Ini adalah cara yang lebih baik untuk menyelesaikan masalah ini.


var focused = true;

window.onfocus = function() {
    focused = true;
};
window.onblur = function() {
    focused = false;
};

AFAIK, focusdan blursemuanya didukung pada ... semuanya. (lihat http://www.quirksmode.org/dom/events/index.html )

Zirak
sumber
2
Hanya sedikit catatan, dengan semua solusi ini, Anda menjalankan risiko pengguna mengubah tab sebelum javascript dimuat sepenuhnya, sehingga menetapkan nilai yang salah untuk fokus. Tidak yakin ada cara yang baik di sekitarnya.
JayD3e
Tautan pembaruan persis seperti yang saya cari. Terima kasih telah menambahkannya!
webLacky3rdClass
Pertanyaannya secara khusus tentang mendeteksi apakah suatu halaman memiliki fokus, yang berbeda dengan mendeteksi apakah halaman tersebut terlihat. Beberapa halaman dapat dilihat secara bersamaan (di jendela yang berbeda), sementara hanya satu yang dapat memiliki fokus. Gunakan teknik mana saja yang sesuai dengan kebutuhan Anda, tetapi ketahuilah perbedaannya.
jaredjacobs
1
Ini adalah solusi berbahaya karena menjalankan risiko mengesampingkan beberapa pendengar acara lain dalam aplikasi yang lebih besar. Anda sebaiknya mengikuti jawaban ini: stackoverflow.com/a/21935031/549503
mmmeff
51

Saat mencari tentang masalah ini, saya menemukan rekomendasi bahwa API Visibilitas Halaman harus digunakan. Sebagian besar browser modern mendukung API ini sesuai dengan Dapatkah Saya Menggunakan: http://caniuse.com/#feat=pagevisibility .

Berikut ini contoh yang berfungsi (berasal dari cuplikan ini ):

$(document).ready(function() {
  var hidden, visibilityState, visibilityChange;

  if (typeof document.hidden !== "undefined") {
    hidden = "hidden", visibilityChange = "visibilitychange", visibilityState = "visibilityState";
  } else if (typeof document.msHidden !== "undefined") {
    hidden = "msHidden", visibilityChange = "msvisibilitychange", visibilityState = "msVisibilityState";
  }

  var document_hidden = document[hidden];

  document.addEventListener(visibilityChange, function() {
    if(document_hidden != document[hidden]) {
      if(document[hidden]) {
        // Document hidden
      } else {
        // Document shown
      }

      document_hidden = document[hidden];
    }
  });
});

Pembaruan: Contoh di atas dulu memiliki properti awalan untuk peramban Gecko dan WebKit, tetapi saya menghapus implementasi itu karena peramban ini telah menawarkan API Visibilitas Halaman tanpa awalan untuk sementara waktu sekarang. Saya menyimpan awalan khusus Microsoft agar tetap kompatibel dengan IE10.

Ilija
sumber
Ketika awalan vendor berubah dari ini, saya mungkin akan beralih!
Fenton
Hanya masalah sebenarnya dengan ini bukan awalan vendor karena ada rekomendasi W3C resmi (tanggal 29 Oktober 2013). Apa masalah dalam beberapa kasus adalah bahwa API visibilitas halaman didukung di IE10 dan yang lebih baru. Jika Anda perlu mendukung IE9, Anda harus mencari pendekatan yang berbeda ...
Ilija
Ini adalah cara yang benar untuk melakukannya untuk semua browser modern. +1
Ajedi32
Apakah Anda yakin awalan vendor ini diperlukan? Menurut MDN dan CanIUse, mereka tidak diperlukan di Chrome sejak versi 32, atau di Firefox sejak versi 17, dan mereka tidak pernah diperlukan di IE.
Ajedi32
@ Ajedi32 Terima kasih. Saya perlu melakukan beberapa tes dan menggali untuk melihat apa yang masih relevan, dan apa yang bisa ditinggalkan sekarang.
Ilija
37

Mengejutkan melihat tidak ada yang disebutkan document.hasFocus

if (document.hasFocus()) console.log('Tab is active')

MDN memiliki informasi lebih lanjut.

aleclarson
sumber
bekerja untuk saya (diuji di Chrome dan Firefox). Jawaban yang diterima (onfocus / onblur) tidak berfungsi
harmv
Jawaban yang benar lagi di bagian paling bawah. Cara untuk pergi StackOverflow!
Sebelas Oktober
sungguh, bukankah ini jawaban yang sempurna? Adakah yang melihat kelemahan?
gaspar
2
Satu-satunya downside ke ini adalah bahwa jika Anda mencoba untuk menentukan apakah tab dalam fokus dari dalam iframe, maka itu akan gagal jika iframe dimuat ketika halaman induk masih tidak fokus. Untuk menutupi itu juga Anda harus pergi dengan api visibilitas halaman.
Ivan
29

Ya itu harus bekerja untuk Anda. Anda baru saja mengingatkan saya pada tautan yang saya temukan yang mengeksploitasi teknik-teknik ini. baca menarik

Brian Glaz
sumber
2
+1 - itu trik yang sangat cerdik, saya bisa membayangkan itu menipu banyak orang.
Fenton
2
Serangan yang cerdik dan licik. Menarik membacanya, terima kasih.
Voo
4

Saya akan melakukannya dengan cara ini (Referensi http://www.w3.org/TR/page-visibility/ ):

    window.onload = function() {

        // check the visiblility of the page
        var hidden, visibilityState, visibilityChange;

        if (typeof document.hidden !== "undefined") {
            hidden = "hidden", visibilityChange = "visibilitychange", visibilityState = "visibilityState";
        }
        else if (typeof document.mozHidden !== "undefined") {
            hidden = "mozHidden", visibilityChange = "mozvisibilitychange", visibilityState = "mozVisibilityState";
        }
        else if (typeof document.msHidden !== "undefined") {
            hidden = "msHidden", visibilityChange = "msvisibilitychange", visibilityState = "msVisibilityState";
        }
        else if (typeof document.webkitHidden !== "undefined") {
            hidden = "webkitHidden", visibilityChange = "webkitvisibilitychange", visibilityState = "webkitVisibilityState";
        }


        if (typeof document.addEventListener === "undefined" || typeof hidden === "undefined") {
            // not supported
        }
        else {
            document.addEventListener(visibilityChange, function() {
                console.log("hidden: " + document[hidden]);
                console.log(document[visibilityState]);

                switch (document[visibilityState]) {
                case "visible":
                    // visible
                    break;
                case "hidden":
                    // hidden
                    break;
                }
            }, false);
        }

        if (document[visibilityState] === "visible") {
            // visible
        }

    };  
confile
sumber
Bisakah Anda menjelaskan bagaimana jawaban ini berbeda dari jawaban yang diberikan oleh @Ilija - mungkin ada perbedaan, tetapi halus - jadi penjelasan tentang apa itu dan mengapa harus berbeda akan dihargai.
Fenton
2

Cross Browser jQuery Solution! Mentah tersedia di GitHub

Menyenangkan & Mudah Digunakan!

Plugin berikut akan melalui pengujian standar Anda untuk berbagai versi IE, Chrome, Firefox, Safari, dll. Dan menetapkan metode yang Anda nyatakan sesuai. Ini juga menangani masalah-masalah seperti:

  • onblur | .blur / onfocus | .fokus " duplikat " panggilan
  • Jendela kehilangan fokus melalui pemilihan aplikasi alternatif, seperti kata
    • Ini cenderung tidak diinginkan hanya karena, jika Anda memiliki halaman bank yang terbuka, dan itu adalah peristiwa blur yang memerintahkannya untuk menutupi halaman tersebut, maka jika Anda membuka kalkulator, Anda tidak dapat melihat halaman itu lagi!
  • Tidak memicu pemuatan halaman

Gunakan sesederhana: Gulir ke Bawah untuk ' Jalankan Cuplikan '

$.winFocus(function(event, isVisible) {
    console.log("Combo\t\t", event, isVisible);
});

//  OR Pass False boolean, and it will not trigger on load,
//  Instead, it will first trigger on first blur of current tab_window
$.winFocus(function(event, isVisible) {
    console.log("Combo\t\t", event, isVisible);
}, false);

//  OR Establish an object having methods "blur" & "focus", and/or "blurFocus"
//  (yes, you can set all 3, tho blurFocus is the only one with an 'isVisible' param)
$.winFocus({
    blur: function(event) {
        console.log("Blur\t\t", event);
    },
    focus: function(event) {
        console.log("Focus\t\t", event);
    }
});

//  OR First method becoms a "blur", second method becoms "focus"!
$.winFocus(function(event) {
    console.log("Blur\t\t", event);
},
function(event) {
    console.log("Focus\t\t", event);
});

/*    Begin Plugin    */
;;(function($){$.winFocus||($.extend({winFocus:function(){var a=!0,b=[];$(document).data("winFocus")||$(document).data("winFocus",$.winFocus.init());for(x in arguments)"object"==typeof arguments[x]?(arguments[x].blur&&$.winFocus.methods.blur.push(arguments[x].blur),arguments[x].focus&&$.winFocus.methods.focus.push(arguments[x].focus),arguments[x].blurFocus&&$.winFocus.methods.blurFocus.push(arguments[x].blurFocus),arguments[x].initRun&&(a=arguments[x].initRun)):"function"==typeof arguments[x]?b.push(arguments[x]):
"boolean"==typeof arguments[x]&&(a=arguments[x]);b&&(1==b.length?$.winFocus.methods.blurFocus.push(b[0]):($.winFocus.methods.blur.push(b[0]),$.winFocus.methods.focus.push(b[1])));if(a)$.winFocus.methods.onChange()}}),$.winFocus.init=function(){$.winFocus.props.hidden in document?document.addEventListener("visibilitychange",$.winFocus.methods.onChange):($.winFocus.props.hidden="mozHidden")in document?document.addEventListener("mozvisibilitychange",$.winFocus.methods.onChange):($.winFocus.props.hidden=
"webkitHidden")in document?document.addEventListener("webkitvisibilitychange",$.winFocus.methods.onChange):($.winFocus.props.hidden="msHidden")in document?document.addEventListener("msvisibilitychange",$.winFocus.methods.onChange):($.winFocus.props.hidden="onfocusin")in document?document.onfocusin=document.onfocusout=$.winFocus.methods.onChange:window.onpageshow=window.onpagehide=window.onfocus=window.onblur=$.winFocus.methods.onChange;return $.winFocus},$.winFocus.methods={blurFocus:[],blur:[],focus:[],
exeCB:function(a){$.winFocus.methods.blurFocus&&$.each($.winFocus.methods.blurFocus,function(b,c){this.apply($.winFocus,[a,!a.hidden])});a.hidden&&$.winFocus.methods.blur&&$.each($.winFocus.methods.blur,function(b,c){this.apply($.winFocus,[a])});!a.hidden&&$.winFocus.methods.focus&&$.each($.winFocus.methods.focus,function(b,c){this.apply($.winFocus,[a])})},onChange:function(a){var b={focus:!1,focusin:!1,pageshow:!1,blur:!0,focusout:!0,pagehide:!0};if(a=a||window.event)a.hidden=a.type in b?b[a.type]:
document[$.winFocus.props.hidden],$(window).data("visible",!a.hidden),$.winFocus.methods.exeCB(a);else try{$.winFocus.methods.onChange.call(document,new Event("visibilitychange"))}catch(c){}}},$.winFocus.props={hidden:"hidden"})})(jQuery);
/*    End Plugin      */

// Simple example
$(function() {
	$.winFocus(function(event, isVisible) {
		$('td tbody').empty();
		$.each(event, function(i) {
			$('td tbody').append(
				$('<tr />').append(
					$('<th />', { text: i }),
					$('<td />', { text: this.toString() })
				)
			)
		});
		if (isVisible) 
			$("#isVisible").stop().delay(100).fadeOut('fast', function(e) {
				$('body').addClass('visible');
				$(this).stop().text('TRUE').fadeIn('slow');
			});
		else {
			$('body').removeClass('visible');
			$("#isVisible").text('FALSE');
		}
	});
})
body { background: #AAF; }
table { width: 100%; }
table table { border-collapse: collapse; margin: 0 auto; width: auto; }
tbody > tr > th { text-align: right; }
td { width: 50%; }
th, td { padding: .1em .5em; }
td th, td td { border: 1px solid; }
.visible { background: #FFA; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<h3>See Console for Event Object Returned</h3>
<table>
    <tr>
        <th><p>Is Visible?</p></th>
        <td><p id="isVisible">TRUE</p></td>
    </tr>
    <tr>
        <td colspan="2">
            <table>
                <thead>
                    <tr>
                        <th colspan="2">Event Data <span style="font-size: .8em;">{ See Console for More Details }</span></th>
                    </tr>
                </thead>
                <tbody></tbody>
            </table>
        </td>
    </tr>
</table>

SpYk3HH
sumber
Anda harus menghapus kode untuk plugin.
Patrick Desjardins
@ PatrickDesjardins ya. Berencana melakukan ini akhir pekan ini bersama dengan hal-hal lain. SAYA? Buat intisari untuk banyak barang yang saya miliki. Jdmckinstry di github. Akan menambahkan tautan ke jawaban lama seperti ini saat saya menambahkannya ke inti
SpYk3HH
Bagaimana jika saya ingin halaman kehilangan fokus, ketika saya beralih ke aplikasi lain, seperti "Word" atau "Calculator"?
Benas
@ Arena mungkin salah, tapi saya percaya itu adalah fungsi dasar dari yang sangat mendasar jQuery(window).blur/focus, yang tidak diinginkan oleh banyak orang, jadi salah satu alasan saya membuat plugin ini. Plugin ini dimaksudkan untuk membantu menyediakan apa yang belum jQuery
SpYk3HH