定期的に活動しているJavaScriptがあります。ユーザーがサイトを見ていないとき(つまり、ウィンドウやタブにフォーカスがないとき)は、実行しないのがいいでしょう。
JavaScriptを使用してこれを行う方法はありますか?
私の参照ポイント:あなたが使用しているウィンドウがアクティブでない場合、Gmailチャットは音を出します。
もともとこの答えを書いて以来、新しい仕様はW3Cのおかげで推奨ステータスに達しました。 Page Visibility API (on MDN )を使用すると、ページがユーザーに非表示になるタイミングをより正確に検出できるようになりました。
現在のブラウザのサポート:
次のコードはAPIを利用していますが、互換性のないブラウザでは信頼性の低いぼかし/フォーカス方法に頼っています。
(function() {
var hidden = "hidden";
// Standards:
if (hidden in document)
document.addEventListener("visibilitychange", onchange);
else if ((hidden = "mozHidden") in document)
document.addEventListener("mozvisibilitychange", onchange);
else if ((hidden = "webkitHidden") in document)
document.addEventListener("webkitvisibilitychange", onchange);
else if ((hidden = "msHidden") in document)
document.addEventListener("msvisibilitychange", onchange);
// IE 9 and lower:
else if ("onfocusin" in document)
document.onfocusin = document.onfocusout = onchange;
// All others:
else
window.onpageshow = window.onpagehide
= window.onfocus = window.onblur = onchange;
function onchange (evt) {
var v = "visible", h = "hidden",
evtMap = {
focus:v, focusin:v, pageshow:v, blur:h, focusout:h, pagehide:h
};
evt = evt || window.event;
if (evt.type in evtMap)
document.body.className = evtMap[evt.type];
else
document.body.className = this[hidden] ? "hidden" : "visible";
}
// set the initial state (but only if browser supports the Page Visibility API)
if( document[hidden] !== undefined )
onchange({type: document[hidden] ? "blur" : "focus"});
})();
onfocusin
name__およびonfocusout
name__は IE 9以下に必要 ですが、他のすべてのものはonfocus
name__およびonblur
name__を使用しますが、iOSはonpageshow
name__およびonpagehide
name__を使用します。
私がjQueryを使うのは、あなたがしなければならないのはこれだけだからです。
$(window).blur(function(){
//your code here
});
$(window).focus(function(){
//your code
});
または少なくともそれは私のために働いた。
GitHubには素敵なライブラリがあります。
https://github.com/serkanyersen/ifvisible.js
例:
// If page is visible right now
if( ifvisible.now() ){
// Display pop-up
openPopUp();
}
私は私が持っているすべてのブラウザでバージョン1.0.1をテストし、それが動作することを確認することができます:
...そしておそらくすべての新しいバージョンです。
完全には動作しません。
.now()
は常にtrue
を返す)私は自分のアプリ用にComet Chatを作成し、他のユーザーからメッセージを受け取ったときには次のものを使用します。
if(new_message){
if(!document.hasFocus()){
audio.play();
document.title="Have new messages";
}
else{
audio.stop();
document.title="Application Name";
}
}
:ページ表示API を使用する
document.addEventListener( 'visibilitychange' , function() {
if (document.hidden) {
console.log('bye');
} else {
console.log('well back');
}
}, false );
私はコミュニティWikiの答えを使って始めましたが、それがChromeの代替タブイベントを検出していないことに気付きました。これは、最初に利用可能なイベントソースを使用しているためです。この場合は、ページ可視化APIです。Chromeでは、Alt + Tabキーを追跡できないようです。
私はスクリプトを少し修正して、すべてのページフォーカスの変更に関する可能性のあるイベントを追跡することにしました。これはあなたが入れることができる関数です:
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)()});
} else if ('mozHidden' in document) {
document.addEventListener('mozvisibilitychange',
function() {(document.mozHidden ? unfocused : focused)()});
} else if ('webkitHidden' in document) {
document.addEventListener('webkitvisibilitychange',
function() {(document.webkitHidden ? unfocused : focused)()});
} else if ('msHidden' in document) {
document.addEventListener('msvisibilitychange',
function() {(document.msHidden ? unfocused : focused)()});
} else if ('onfocusin' in document) {
// IE 9 and lower:
document.onfocusin = focused;
document.onfocusout = unfocused;
} else {
// All others:
window.onpageshow = window.onfocus = focused;
window.onpagehide = window.onblur = unfocused;
}
};
このように使用してください。
onVisibilityChange(function(visible) {
console.log('the page is now', visible ? 'focused' : 'unfocused');
});
これは本当にトリッキーです。以下の要件を考慮した解決策はないようです。
これは次のような理由で起こります。
これらの制限を考えると、 - ページの可視性API - ウィンドウのぼかし/フォーカス - document.activeElementを組み合わせたソリューションを実装することが可能です。
それは可能です:
Iframeにフォーカスがあると、blur/focusイベントはまったく呼び出されず、Visibility APIページはalt +タブではトリガーされません。
私は@ AndyEのソリューションを基にして、この(ほぼ良い)ソリューションをここに実装しました。 https://dl.dropboxusercontent.com/u/2683925/estante-components/visibility_test1.html (申し訳ありません) 、JSFiddleに問題がありました。
これはGithubでも利用できます。 https://github.com/qmagico/estante-components
これはクロム/クロムに作用します。それは、iframeの内容をロードしないことを除いて、Firefox上でも同様に機能します(何故か?)
とにかく、最後の問題(4)を解決する唯一の方法は、iframeのぼかし/フォーカスイベントを監視することです。 iframeをある程度制御できる場合は、postMessage APIを使用してそれを実行できます。
https://dl.dropboxusercontent.com/u/2683925/estante-components/visibility_test2.html
私はまだ十分なブラウザでこれをテストしていません。あなたがこれがうまくいかない場所についてのより多くの情報を見つけることができるならば、下のコメントで私に知らせてください。
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);
});
HTML 5では、次のものも使えます。
onpageshow
:ウィンドウが見えるようになったときに実行されるスクリプトonpagehide
:ウィンドウが隠れているときに実行されるスクリプト見る:
これは私にとってChrome 67、Firefox 67、で機能します。
if(!document.hasFocus()) {
// do stuff
}
あなたが使用することができます:
(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);
})();
これはAndy Eからの回答の修正です。
これはタスクを行います。ページが表示されてフォーカスされている場合に限り、30秒ごとにページを更新します。
視認性が検出できない場合は、焦点のみが使用されます。
ユーザーがページにフォーカスを合わせた場合は、すぐに更新されます
Ajaxの呼び出しから30秒が経過するまでページは更新されません
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);
}
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()
はControllerがスコープ内にあるものすべてのスコープ内のスコープ関数です。
JQueryを使わない解決策については、3つのページ状態に関する情報を提供する 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)への代替も可能です。
もう少し複雑な方法はsetInterval()
を使ってマウスの位置をチェックし、最後のチェックと比較することです。マウスが設定された時間内に動かなかった場合、ユーザーはおそらくアイドル状態です。
これは、ウィンドウがアクティブでないかどうかをjustでチェックするのではなく、ユーザーがアイドル状態かどうかを判断するという利点があります。
多くの人が指摘しているように、ユーザーがマウスを使用していない、ビデオを見ていないなどの理由で、ユーザーウィンドウまたはブラウザウィンドウがアイドル状態かどうかを確認するにはこれが必ずしも良い方法とは言えません。私はただアイドル状態をチェックする一つの可能な方法を提案しています。
ただ追加したいと思いました:質問は明確に書かれていません。 「ユーザーがサイトを見ていないとき(つまり、ウィンドウまたはタブにフォーカスがないとき)...」
焦点が合っていないサイトを見ることができます。ほとんどのデスクトップシステムはウィンドウを並行して表示することができます:)
ページ可視化APIはおそらく「ユーザーが更新を見ることができない」場合には「タブにフォーカスがない」とは大きく異なる場合があるため、サイトの更新を妨げるため、正しい答えであると考えられます。
onwholeブラウザぼかしのように動作したい場合:ブラウザのルーズフォーカスがない場合は推奨されないイベントが発生します。私の考えは、イベントが発生した場合、ループでカウントアップし、カウンターをリセットすることです。カウンターが限界に達したら私は他のページに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でテストが成功したドラフトです。