JavaScript: clear setInterval on blur, and resume on focus - javascript

I have a very basic slideshow cycling some div containers. This is my code:
function cycle(){
var $first = $('.panes .pane1');
var $active = $('.panes .active');
var $next = $active.next();
if($next.length != 0){
$active.removeClass('active').addClass('inactive');
$('.panes').animate({left: "-=430px"}, 400);
setTimeout(function(){
$next.removeClass('inactive').addClass('active');
}, 400);
} else {
$active.removeClass('active').addClass('inactive');
$('.panes').animate({left: "0px"}, 400);
setTimeout(function(){
$first.removeClass('inactive').addClass('active');
}, 400);
}
}
$(window).blur(function(){window.clearInterval(cycling)});
$(window).focus(function(){cycling = setInterval('cycle()', 5001);});
Then in my document.ready function, I'm calling the above with cycling = setInterval('cycle()', 5001);
When the page become inactive, the setInterval should clear, then resume when the page regains focus. This works correctly in Chrome, but not IE. I'm not really concerned about IE because it doesn't need to be cleared there.
In Firefox, with the above code, the div container just switches back and forth with each interval, or sometimes it slides to the left twice as far as it is supposed to.
When I remove the $(window).focus(function(){cycling = setInterval('cycle()', 5001);}); line, then it behaves correctly with the exception of that it does not resume when the page regains focus.
What is causing this and how do I resolve the issue?

You are calling setInterval twice, once on focus and once on document ready. The blur handler only clears whichever of those happened second. So the cycle() function gets called twice as often as you expect.
One way to get around that is to initialise cycling to null, assign cycling = null in the blur handler, and then in the ready and focus handlers only create a new interval if cycling === null.
Another way is to only call setInterval from document ready and just leave it running forever (i.e. until the user navigates away from your page), but add an "infocus" flag that you set and clear from the focus and blur handlers:
var infocus = false;
$(window).focus(function(){ infocus = true; })
.blur(function (){ infocus = false; });
Then check the flag:
function cycle() {
if (!infocus)
return;
// rest of your code here...
}

Related

JQuery - mousedown,mouseup and click on same element

I have a carousel and I need to make him work as Instagram carousel does.
On click change slide, but on mousedown just stop animation. My JQuery :
$(".fancy-carousel").on('mousedown',function (e) {
...stop animation
});
$(".fancy-carousel").on('mouseup',function (e) {
..continue animation
});
$(".fancy-carousel").on('click',function (e) {
..change slide
});
But i don´t know how can i let script know about difference between "click" and "mousedown". When i click on element and hold for a time, it stop animation but after "mouseup" it trigger "click" event too. Is there any way how to split this events? Or should i do it with some calculating of mouse hold time?
A “click” is just a full cycle of a “mousedown” and a “mouseup”. You can’t have one without the other.
For your code to know the difference, you’ll need a variable that tracks your intentions.
Create a variable to track your intention - default it to “click”.
var intention = "click";
In your mousedown function, pause the animation and start a timer. We will use this timer to detect how long the mouse is down for (I.e, if it’s for over a second, it’s not a click and you just want to trigger mouseup)
var detectIntention = setTimeout(function(){
intention = "mouseup";
})
In your mouse up function, cancel this timeout. If mouse up is called after just a few MS, then you want to do a click.
clearTimeout(detectIntention);
if (intention === "mouseup") {
// do mouseup stuff
}
// reset intention
intention = click;
Check in your click function that you wanted to do a click;
if (intention === "click") {
// do click stuff
}

Checking Page Visibility and Page Focus for a page

I want to make sure when a user is on the page.
Hence, when a user clicks on another window (looses focus) or changes tab, I should stop playing video on my page.
The problem is trying to do both simultaneously.
For example, through this JS plugin (JQuery Visbility), I am able to check whether the tab/window of my page is open.
Here's how it's doing it:
$(document).on({
'show': function() {
console.log('The page gained visibility; the `show` event was triggered.');
},
'hide': function() {
console.log('The page lost visibility; the `hide` event was triggered.');
}
});
But it can't detect whether the page has focus or not. For example, the page might be open, but I may be opening another window separately and keeping my focus there.
The following code takes care of that (taken from here):
function check()
{
if(document.hasFocus() == lastFocusStatus) return;
lastFocusStatus = !lastFocusStatus;
statusEl.innerText = lastFocusStatus ? 'with' : 'without';
}
window.statusEl = document.getElementById('status');
window.lastFocusStatus = document.hasFocus();
check();
setInterval(check, 200);
Now, I am trying to do both simultaneously. Is it possible?
You can add event listeners for the window's focus and blur events.
var hasFocus = true;
$(window).focus(function(){
hasFocus = true;
});
$(window).blur(function(){
hasFocus = false;
});
//check the hasFocus variable to see if the window has focus

Toggled boolean variable evaluated in conditional statement on click event

I am toggling the boolean variable "userTurn". There are two functions that set the value of "userTurn":
runGame(), which sets "userTurn" to true after the last sound in the current round is played.
buttonClick(), which should only be executed if "userTurn" is true. buttonClick sets "userTurn" to false after the user successfully copies the current pattern, or if the user makes a mistake.
I am evaluating the value of "userTurn" in a conditional statement that is inside of a click event.
$(".quarter").on('click', function(){
if( userTurn===true && isOn===true){
var color = $(this).attr('id');
clearTimeout(buzz);
buttonClick(color);
}
})
After the user successfully copies the current pattern, or if the user makes a mistake, "userTurn" is set to false. The problem I am running into is, after "userTurn" is set to false, the code inside the conditional statement is still executing. The goal is for ".quarter" to only be clickable when "userTurn" is true. Why is the buttonClick() function still executing when ".quarter" is clicked even when "userTurn" is false??
Here are the two functions that set the value of "userTurn":
runGame():
function runGame(){
if(isOn===true){
userTurn = false;
count();
playPattern(order, index);
if(index===counter-1&&userTurn===false){
userTurn=true;
clearInterval(play);
buzz = setTimeout(buzzer, 10000);
}else{
index++;
}
} else {
clearInterval(play);
clearTimeout(buzz);
}
}
2.buttonClick():
function buttonClick(color){
var yellowSound = document.getElementById("horseSound");
var redSound = document.getElementById("endFx");
var greenSound = document.getElementById("westernRicochet");
var blueSound = document.getElementById("robotBlip");
$("#red").mousedown(function(){
$("#red").css("background-color", "#ff4d4d");
redSound.play();
});
$("#red").mouseup(function(){
$("#red").css("background-color", "#ff0000");
redSound.pause();
});
$("#blue").mousedown(function(){
$("#blue").css("background-color", "#0000e6");
blueSound.play();
});
$("#blue").mouseup(function(){
$("#blue").css("background-color", "#000099");
blueSound.pause();
});
$("#green").mousedown(function(){
$("#green").css("background-color", "#00e600");
greenSound.play();
});
$("#green").mouseup(function(){
$("#green").css("background-color", "#009900");
greenSound.pause();
});
$("#yellow").mousedown(function(){
$("#yellow").css("background-color", "#ffff4d");
yellowSound.play();
});
$("#yellow").mouseup(function(){
$("#yellow").css("background-color", "#ffff00");
yellowSound.pause();
});
if(color===order[compareIndex]){
userPattern.push(color);
console.log(userPattern, "match");
buzz = setTimeout(buzzer, 10000);
compareIndex++;
if(userPattern.length===counter){
userTurn=false;
compareIndex=0;
index=0;
counter++;
count();
userPattern.length=0;
play = setInterval(runGame, 2000);
clearTimeout(buzz);
}
} else {
userTurn=false;
console.log('don\'t match');
clearTimeout(buzz);
index=0;
compareIndex = 0;
userPattern.length=0;
buzz = setTimeout(buzzer, 1);
}
}
TURN DOWN YOUR VOLUME BEFORE GOING TO THE CODEPEN!!
Here is a link to the codepen.
To start the game click "On Off" and then click "start". The timing is slow so you have to wait for the pattern to play, then click the buttons in the same order as they were played. The problem is seen after the first sound is played. From that point on any click the user makes will play the sound and light up the button, even when the game is playing the pattern and "userTurn" is false. Also be warned, this is still a work in progress, there are a couple of other bugs I'm working on, for example the the first turn the user makes will not light up or play the sound, but the selection is pushed into the correct array and the game will proceed properly based on your selection. I know this is a lot of info but I'm stuck on this so any feed back will be very appreciated. Thanks!
You're using buttonClick to handle clicking the buttons, but inside buttonClick you're setting mouseup and mousedown event listeners for each button as well. Since the individual buttons have their own mouseup and mousedown listeners, the events will happen whether or not buttonClick is called again.
You would have to check if userTurn is true inside of each of those event handlers as well to prevent them from triggering. Also, it would better to set these outside of buttonClick so you won't be adding new listeners every time buttonClick gets called.

Identifying window focus/blur events with iframes

I'm trying to reliably identify when a browser window/tab is activated and deactivated. Normally, window's focus and blur events would do, but the document contains several iframes.
When an iframe is focused, the main window gets unfocused and vice versa, so we have the following possibilities of focus events [(none) means the window/tab is deactivated]:
current focus new focus events
----------------------------------------------------------------------
window (none) window:blur
window iframe window:blur + iframe:focus
iframe (none) iframe:blur
iframe window iframe:blur + window:focus
iframe another iframe iframe:blur + iframe:focus
(none) window window:focus
(none) iframe iframe:focus
It is no problem to register all of these events, as shown by this fiddle. But whenever we switch from the main window to an iframe or vice versa, or between two iframes, the respective blur and focus events both fire; and they fire with a small delay at that.
I am worried about the concurrency here, since the blur handler could go and start doing stuff, but it should have never started because the user actually just switched focus somewhere in between the frames.
Example: A page should do some AJAX requests periodically whenever it is currently not active. That is, it should start requesting whenever the user deactivates the tab and stop requesting as soon as it's activated again. So we bind a function to the blur event that initiates the requests. If the user just clicks on another iframe, blur, and shortly after that, focus is triggered. But the blur handler already fires away, making at least one request before it can be stopped again.
And that's my problem: How can I reliably detect when a user actually (de-)activates a browser window containing iframes, without risking to get a false alarm caused by two immediate blur and focus events?
I wrote a half-baked solution that uses a timeout after a blur event in order to determine if there was an immediate focus event after it (fiddle):
var active = false,
timeout = 50, // ms
lastBlur = 0,
lastFocus = 0;
function handleBlur() {
if (lastBlur - lastFocus > timeout) {
active = false;
}
}
function handleFocus() {
if (lastFocus - lastBlur > timeout) {
active = true;
}
}
$(window).on('focus', function () {
lastFocus = Date.now();
handleFocus();
}).on('blur', function () {
lastBlur = Date.now();
window.setTimeout(handleBlur, timeout);
});
$('iframe').each(function () {
$(this.contentWindow).on('focus', function () {
lastFocus = Date.now();
handleFocus();
}).on('blur', function () {
lastBlur = Date.now();
window.setTimeout(handleBlur, timeout);
});
});
But I believe this could be very problematic, especially on slower machines. Increasing the timeout is also not acceptable to me, 50 ms is really my pain threshold.
Is there a way that doesn't depend on the client to be fast enough?
you could poll for the document.hasFocus() value, which should be true if either an iframe or the main window are focused
setInterval(function checkFocus(){
if( checkFocus.prev == document.hasFocus() ) return;
if(document.hasFocus()) onFocus();
else onBlur();
checkFocus.prev = document.hasFocus();
},100);
function onFocus(){ console.log('browser window activated') }
function onBlur(){ console.log('browser window deactivated') }
I was trying to do it without polling, but the iframe doesn't fire an onblur event (if the browser window is deactivated when the iframe was on focus, I get no events fired), so I ended up needing polling for half of it anyway, but maybe someone can figure something out with this code
function onFocus(){ console.log('browser window activated'); }
function onBlur(){ console.log('browser window deactivated'); }
var inter;
var iframeFocused;
window.focus(); // I needed this for events to fire afterwards initially
addEventListener('focus', function(e){
console.log('global window focused');
if(iframeFocused){
console.log('iframe lost focus');
iframeFocused = false;
clearInterval(inter);
}
else onFocus();
});
addEventListener('blur', function(e){
console.log('global window lost focus');
if(document.hasFocus()){
console.log('iframe focused');
iframeFocused = true;
inter = setInterval(()=>{
if(!document.hasFocus()){
console.log('iframe lost focus');
iframeFocused = false;
onBlur();
clearInterval(inter);
}
},100);
}
else onBlur();
});

prevent javascript execute click event if not ready

previous.click(function(){
if ((parseInt($('.slider-box').css('margin-left'))) < 0) {
$('.slider-box').animate({'margin-left':'+='+rubrikWidth});
}
});
This code executes a slider to move right, if a button is clicked and the the margin-left is < 0.
But when you click fast and often, the if-clause calculates to slow and then it is possible to move the slider to far.
i want to prevent the button to be clicked too fast.
is there a way to do:
click -> execute event -> and after this execution, you can click again.
You can simply use the animate callback. This should do it, assuming .slider-box only matches a single element:
previous.click(function(){
var slider = $('.slider-box');
if (!slider.data('busy') && (parseInt(slider.css('margin-left'))) < 0) {
slider.data('busy', true).animate({'margin-left':'+='+rubrikWidth}, function() {
slider.data('busy', false);
});
}
});
you can check if an animation is running by checking:
$('.slider-box').queue('fx').length
and use preventDefault if the queue's length is more than zero

Categories

Resources