fire a function after window resize - javascript

I'm doing a few functions on $(window).resize(). One of the functions inside it, is a complex animation with lots of divs.
$window.resize(function() {
// some functions
doAnim();
}
the problem here is, that the resize() function triggers a lot. Is it possible to fire one function after the resize() is finished, that it doesnt fire a hundred times?
Cheers

So, I believe this is a similar scenario when a user "type" something: you can't know when the user finished to compose a sentence, the same here, you don't know when the user has finished to resize; so what you can do is have an acceptable interval:
var idt;
$(window).resize(function() {
if (idt) clearTimeout(idt);
idt = setTimeout(doAnim, 500);
}
You can also use a closure to avoid to pollute the global scope with idt variable.
However the main point here is that every time the user resize the window, it set a timeout and clear the previous one: the doAnim function will be executed only if the user leave the window without resizing it for half second. Of course you can set your own delay.

You can achieve it as shown below. This will call when you finish with resizing :
var myTimer;
$(window).resize(function()
{
// some functions
var interval = 500;
clearInterval(myTimer);
myTimer = setInterval(function () { doAnim(); }, interval);
});
function doAnim()
{
clearInterval(myTimer);
alert('Resized');
}

Related

request animation frame loop

I came across this code
https://gist.github.com/joelambert/1002116 and i thought of playing around with it
I tried to create a loop and stop it
var tick = 0;
var dor = requestInterval(function(){
tick++;
console.log("hi", tick)
if (tick > 10){
stop();
}
},300)
function stop(){
console.log("stop")
clearRequestInterval(dor);
}
But the clearRequestInterval is not clearing the timer. But when i tried to call it from a button's event handler its working. Am I missing something?
I have attached a codepen
http://codepen.io/srajagop/pen/KgbbpR
#Bergi is right that the example code you tried to use is broken, it doesn't support cancelling the interval timer from within the interval function itself. You can work around that by invoking the clearRequestInterval asynchronously:
function stop() {
console.log("stop");
window.setTimeout(function() {
clearRequestInterval(dor);
}, 0);
}
Or perhaps better, you could fix the example code not to reschedule itself even if it was cancelled from within the interval function.

Javascript while loop changing scroll position of div crashes site

I have a div displaying some horizontally scrollable images with white-space:nowrap; overflow-x:scroll and i'm trying to make the function below work:
var mouseIsInDiv = false;
function autoScroll() {
var i = 1;
while (mouseIsInDiv = false) {
setTimeout(function(){
document.getElementById("theDiv").scrollLeft = i;
i++;
},50);
}
}
It is supposed to loop through (while the mouse is not within this scrollable div) incrementing the scroll position by 1px every 50 miliseconds. In other words it's supposed to scroll through the images automatically when this function is called. I'm not getting any syntactic errors but whenever i press a button that calls this function on a webpage, the browser crashes completely - I'm using the latest versions of Chrome, Safari and Firefox. Any ideas would be really helpful, I've been tearing my hair out over this!
Your loop creates many timeouts that happens in the same time (after 50 milisecs) you need to set the timeout recursivly, inside the set timeout function, and ask if mouseISInDiv inside the set timeout function as well.
The current code state, the loop will run many many times in a small amount of time, and page will crush(it's liek infinite) and after 50 millisecs there will be many set timeouts that ran.
I had a fun time working on this one, so I'll post my response despite the correct answer already having been accepted.
Basically, you need to restructure everything so that the whole scheme is asynchronous. That means event listeners respond to mouse movement, and there are no while loops.
Thus, I present this fiddle. Here is the javascript:
var mouseIsInDiv = false;
var theDiv = document.getElementById("theDiv");
theDiv.onmouseover = function() { mouseIsInDiv = true; };
theDiv.onmouseout = function() {
mouseIsInDiv = false;
scrollLeft1();
};
function scrollLeft1() {
if (mouseIsInDiv == false && theDiv.scrollLeft < theDiv.clientWidth) {
theDiv.scrollLeft += 1;
setTimeout(scrollLeft1, 50);
}
}
scrollLeft1();
As you can see, the function calls itself recursively and asynchronously, and the whole thing can be restarted after manually resetting the scroll. You could also add an event listener for the scroll completion.

run command only at the first occurrence of the window resize

I would like to know if the following situation it's possible. Let's say I have the next $.resize() function:
$(window).resize(function(){
if($(window).width() < 500) console.log('Small');
});
I know that the above will log Small as long as the size of the window is under 500. So this means if you resize your window from 500 to 200, the Small will be logged about 300 times.
Well, that means if you actually have some heavy function running under those circumstances, the browser might crash because it will run over and over again until you stop resizing or the circumstances change.
So that is why I'm asking if there would be a way to run that hypothetical function only at the first occurrence of the condition ?
Use the one event bind which runs the passed in function once only at the first occurrence of the event.
$(window).one("resize", function(){
if($(window).width() < 500) console.log('Small');
});
You can use one event handler in jquery to do it.
http://api.jquery.com/one/
like this
$(window).one('resize', function(){alert('lol');});
This should work
$(window).resize(function(){
// apply whatever you want to do with it, other conditions etc...
// regardless of what you want to do, the following line unbinds the resize event
// from within the handler.. consider calling this once all your conditions are met
$(window).unbind("resize");
});
this handler executes your code, then unbinds the resize event from window
Edit: removed conditions. The purpose of this code is just to show how to call unbind, apply the conditions that are best for your scenario
Edit 2: Addressing one of the comments, unbind the way I presented on the code above, will ultimately unbind all handlers for the resize event. A better approach would be declaring the event handler in a separate method like this:
var Window_Resize = function(e) {
// code goes here
};
$(window).resize(Window_Resize);
and to unbind, use this
$(window).unbind(Window_Resize)
This way you only remove that specific handler. Other handlers would still be bound to this event.
There are two possible solutions. If you want the handler to be run the very first time you resize the window, you can force it to run only once:
$(window).one('resize', function () {
if ($(window).width() < 500) console.log('Small');
});
But then you have a problem: it literally only runs once. What happens if they resize it again to be large?
A solution is to introduce a "tolerance" zone, where you run your code only if the window has been resized within a certain period of time:
var RESIZE_TOLERANCE = 200; // milliseconds
var last_resize = 0;
$(window).resize(function () {
var current_time = (new Date()).getTime();
if (current_time - last_resize < RESIZE_TOLERANCE) {
return; // stop
}
last_resize = current_time;
if ($(window).width() < 500) console.log('Small');
});
This forces the resize handler to run at maximum five times per second. You can change the tolerance if you so desire.
However, think of this situation: we resize down from 999px to 998px, firing the resize handler. Then we resize from 998px to 200px before the 200ms is up. The issue is that we have missed the resize event.
A much better solution is to keep track of the current small state and only execute your heavy code if the state changes:
var RESIZE_TOLERANCE = 100; // milliseconds
var SMALL_TOLERANCE = 500; // pixels
var last_resize = 0;
var small = $(window).width() < SMALL_TOLERANCE;
$(window).resize(function () {
var current_time = (new Date()).getTime();
if (current_time - last_resize < RESIZE_TOLERANCE) {
return; // stop
}
last_resize = current_time;
var is_small = $(window).width() < SMALL_TOLERANCE;
if (is_small !== small) {
// run expensive code here
small = is_small;
}
});
Now the tolerance is 100ms, which means we won't be recalculating the window's width more than that. (You can remove that or change it if you want.) We only run the expensive code if the state of the screen has changed. I don't know if this is what you're looking for, but you will have to be creative if your requirements are subtly different.
The straightfoward answer is:
$(window).resize(function(){
if($(window).width() < 500 && !ranfirst) {
ranfirst = true;
console.log('Small');
}
});
This probably won't work the way you're thinking, though it does satisfy the phrasing of your question. You might find you need to set a timeout to poll the size of the window and unset ranfirst when the size remains the same for 10 seconds or so.

restarting a setInterval

So I have a timer rotates a set of images ever 5 seconds. Therefore, I am running this upon document launch.
$(document).ready(function() {
var intervalID=setInterval(function(){
rotate();
}, 5000);
});
The Rotate function simply just rotates the images. However, I also allow the user to manually select what image they are looking at. Because of this I need to cancel the SetInterval and then start it over back at 5 seconds again
What I am trying to do is cancel the interval then start it over by doing this
$('a').click(function(){
clearInterval(intervalID);
intervalID=setInterval(function(){
rotate();
}, 5000);
});
However, the code doesn't seem to reset the interval like I had hoped.
If the intervalID variable is declared within the .ready() scope, the following ought to work (untested):
$(document).ready(function() {
var rotate = function() { ... },
intervalID = setInterval(rotate, 5000);
$('a').click(function() {
clearInterval(intervalID);
intervalID = setInterval(rotate, 5000);
});
});
Just make intervalID be global variable by declaring it outside and above all functions.
With your current code its scope is limited to $(document).ready() method so it might cause the problem you describe.
Well, it looks like you are declaring interverID locally within the anonymous function from your .ready() handler. I'm actually wondering why you don't face a Reference error in your click-event handler, since intervalID cannot be known there.
You need to make sure that this variable is available and does have a shared context for both functions. Easiest way to go, create an anonymous self invoking method around your script and declare that variable out of scope.
(function _myPrivateContext($, window, document, undefined) {
var intervalID = null;
$(document).ready(function() {
intervalID = setInterval(rotate, 5000);
});
$('a').click(function(){
clearInterval(intervalID);
intervalID = setInterval(rotate, 5000);
});
}(jQuery, window, document));

When using setInterval, if I switch tabs in Chrome and go back, the slider goes crazy catching up

I have a jQuery slider on my site and the code going to the next slide is in a function called nextImage. I used setInterval to run my function on a timer, and it does exactly what I want: it runs my slides on a timer. BUT, if I go to the site in Chrome, switch to another tab and return, the slider runs through the slides continuously until it 'catches up'. Does anyone know of a way to fix this. The following is my code.
setInterval(function() {
nextImage();
}, 8000);
How to detect when a tab is focused or not in Chrome with Javascript?
window.addEventListener('focus', function() {
document.title = 'focused';
},false);
window.addEventListener('blur', function() {
document.title = 'not focused';
},false);
To apply to your situation:
var autopager;
function startAutopager() {
autopager = window.setInterval(nextImage, 8000);
}
function stopAutopager() {
window.clearInterval(autopager);
}
window.addEventListener('focus', startAutopager);
window.addEventListener('blur', stopAutopager);
Note that in the latest version of Chromium, there is either a bug or a 'feature' which is making this less reliable, requiring that the user has clicked at least once anywhere in the window. See linked question above for details.
I post an answer here: How can I make setInterval also work when a tab is inactive in Chrome?
Just do this:
setInterval(function() {
$("#your-image-container").stop(true,true);
nextImage();
}, 1000);
inactive browser tabs buffer some of the setInterval or setTimeout functions.
stop(true,true) - will stop all buffered events and execute immadietly only last animation.
The window.setTimeout() method now clamps to send no more than one timeout per second in inactive tabs. In addition, it now clamps nested timeouts to the smallest value allowed by the HTML5 specification: 4 ms (instead of the 10 ms it used to clamp to).
A few ideas comes to mind:
Idea #1
You can make it so that a short burst is idempotent. For example, you could say:
function now() {
return (new Date()).getTime();
}
var autopagerInterval = 8000;
function startAutopager() {
var startImage = getCurrentImageNumber();
var startTime = now();
var autopager = setInterval(
function() {
var timeSinceStart = now() - startTime();
var targetImage = getCurrentImageNumber + Math.ceil(timeSinceStart/autopagerInterval);
if (getCurrentImageNumber() != targetImage)
setImageNumber(targetImage); // trigger animation, etc.
},
autopagerInterval
);
return autopager;
}
This way even if the function runs 1000 times, it will still run in only a few milliseconds and animate only once.
note: If the user leaves the page and comes back, it will have scrolled. This is probably not what the original poster wants, but I leave this solution up since it is sometimes what you want.
Idea #2
Another way to add idempotence (while still keeping your nextImage() function and not having it scroll to the bottom of the page) would be to have the function set a mutex lock which disappears after a second (cleared by another timeout). Thus even if the setInterval function was called 1000 times, only the first instance would run and the others would do nothing.
var locked = false;
var autopager = window.setInterval(function(){
if (!locked) {
locked = true;
window.setTimeout(function(){
locked=false;
}, 1000);
nextImage();
}
}, 8000);
edit: this may not work, see below
Idea #3
I tried the following test:
function f() {
console.log((new Date()) + window.focus());
window.setTimeout(f, 1000);
}
f();
It seems to indicate that the function is being called every second. This is odd... but I think this means that the callbacks are being called, but that the page renderer refuses to update the page in any graphical way while the tab is unfocused, delaying all operations until the user returns, but operations keep piling up.
Also the window.focus() function doesn't say if the window has focus; it GIVES focus to the window, and is thus irrelevant.
What we want is probably this: How to detect when a tab is focused or not in Chrome with Javascript? -- you can unset your interval when the window loses focus (blur), and reset it when it gains focus.
I don't know exactly what is going on in your function nextImage(), but I had a similar issue. I was using animate() with setInterval() on a jQuery image slider that I created, and I was experiencing the same thing as you when I switched to a different tab and back again. In my case the animate() function was being queued, so once the window regained focus the slider would go crazy. To fix this I just stopped the animate() function from queuing.
There are a couple ways you can do this. the easiest is with .stop(), but this issue and ways to fix it are documented in the jQuery docs. Check this page near the bottom under the heading additional notes: http://api.jquery.com/animate/
I had faced similar issue, somehow this code below works fine for me.
var t1= window.setInterval('autoScroll()', 8000);
window.addEventListener('focus', function() {
focused = true;
window.clearInterval(t1);
t1 = window.setInterval('autoScroll()', 8000);
},false);
window.addEventListener('blur', function() {
focused = false;
window.clearInterval(t1);
},false)
function autoScroll()
{
if ( running == true){
if ( focused = true){
forwardSlide();
}
}
else {
running = true;
}
}
If you are using Soh Tanaka's image slider then just add this...to solve your Google Chrome issue:
$(".image_reel").stop(true, true).fadeOut(300).animate({ left: -image_reelPosition}, 500 ).fadeIn(300);
Take note of the .stop() function. Ignore the fading in and out stuff, that's what I used on my version
Thanks
Seconding the comment by jgerstle to use page visibility events instead, see https://www.w3.org/TR/page-visibility/#example-1-visibility-aware-video-playback for more around subscribing to 'visibilitychange' for hidden/visible states.
This seems to be more useful than focus/blur these days as it covers visible-but-not-selected windows if concerned also about multi-window operating systems.

Categories

Resources