Repeat a javascript function with a delay in it - javascript

I have a javascript function that cycles through background images inside of a div. It works well, but it stops when it runs through all the images. How can I call this again when it's done?
$(document).ready(function(){
FlipBackgroundOver();
});
function FlipBackgroundOver()
{
$(".bkgrndimg").each(function(index) {
$(this).hide();
$(this).delay(5000 * index).fadeIn(5000).fadeOut();
});
}
If I add FlipBackgroundOver(); inside the function, it goes into an infinite loop because the delay doesn't stop the function execution.
Here is a fiddle with what I'm working with: http://jsfiddle.net/RyGKV/

On jq 1.6+, you should use promise() relevant callback:
function test() {
$("img").each(function(index) {
$(this).delay(3000 * index).fadeIn(3000).fadeOut();
}).promise().done(test);
}
-jsFiddle-

You can use setInterval to repeat execution with a dealy
$(document).ready(function(){
setInterval(FlipBackgroundOver, 10000); // delay should be based on the number of images => numer_of_images * delay_for_each
});
You can change the delay to value you want based on the number of images you have.

Create a loop in an infinite interval by your own. I used the helper function next for this.
var elements = $("img");
var index = 0;
function next() {
index = ++index == elements.length ? 0 : index;
elements.hide().eq(index).fadeIn(3000).fadeOut();
}
function start() {
// show first one directly
next();
// start infinite loop
setInterval(function() {
next();
}, 3000);
}
start();
Working example.

Related

Clearing setInterval() Issue

I have a setInterval on a function X that runs every 500ms. In this function X, I call another function Y that essentially binds an event on some divs. However, I would like to unbind these events the next time the function X is called (to start "fresh"). My code doesn't seem to work:
setInterval(this.board.updateBoard, 500); //called from another constructor
This then initiates the functions below:
Board.prototype.updateBoard = function() {
//I attempt to unbind ALL my divs
var divs = this.$el.find("div");
for(var i = 0; i < divs.length; i++) {
$(divs[i]).unbind(); //Apparently this doesn't work?
}
//...some code here...
//find appropriate $div's (multiple of them), and then calls this.beginWalking() below on each of those
//loop here
this.beginWalking($div, direction + "0", direction + "1");
//end of loop
}
//alternate between classes to give appearance of walking
Board.prototype.beginWalking = function ($div, dir0, dir1) {
return setInterval(function () {
if ($div.hasClass(dir0)) {
$div.removeClass(dir0);
$div.addClass(dir1);
} else {
$div.removeClass(dir1);
$div.addClass(dir0);
}
}.bind(this), 80);
};
Basically, updateBoard is called every 500ms. Each time it's called, beginWalking is called to set another interval on a div. The purpose of this other interval, which functions correctly, is to add and remove a class every 80ms. I just can't seem to unbind everything before the next updateBoard is called.
Any suggestions appreciated!
use clearInterval()
edit: $(selector).toggleClass(dir0) might also be helpful
// In other file, use a global (no var) if you need to read it from another file:
updaterGlobal = setInterval(this.board.updateBoard, 500);
// store interval references for clearing:
var updaterLocals = [];
Board.prototype.updateBoard = function() {
//I attempt to unbind ALL my divs
var divs = this.$el.find("div");
// Stop existing div timers:
while(updaterLocals.length > 0){
clearInterval(updaterLocals[0]);
updaterLocals.shift(); // remove the first timer
}
//...some code here...
//loop here to call the below on several $div's
this.beginWalking($div, direction + "0", direction + "1");
//end of loop
}
//alternate between classes to give appearance of walking
Board.prototype.beginWalking = function ($div, dir0, dir1) {
var interval = setInterval(function () {
if ($div.hasClass(dir0)) {
$div.removeClass(dir0);
$div.addClass(dir1);
} else {
$div.removeClass(dir1);
$div.addClass(dir0);
}
}.bind(this), 80);
// Save the timer:
updaterLocals.push(interval);
return;
};

Pausing Recursive Function Call

I have a function of which I'm supposed to pause on mouseenter and pause on mouseleave but the problem is that the function is recursive. You pass in a parent and index and it will recursively loop through each inner div displaying and hiding. The function looks like this:
var delay = 1000;
function cycle(variable, j){
var jmax = jQuery(variable + " div").length;
jQuery(variable + " div:eq(" + j + ")")
.css('display', 'block')
.animate({opacity: 1}, 600)
.animate({opacity: 1}, delay)
.animate({opacity: 0}, 800, function(){
if(j+1 === jmax){
j=0;
}else{
j++;
}
jQuery(this).css('display', 'none').animate({opacity: 0}, 10);
cycle(variable, j);
});
}
I've tried setting a timeout and clearing it but it doesn't seem to do anything (it seems to ignore the timeout entirely), I've tried using stop() and calling the function again on mouseout but that seemed to repeat the function call (I was seeing duplicates) and it stopped mid animation which didn't work. I tried adding in a default variable at one point (var pause = false || true;) but I also couldn't get it to work as expected (though I feel the solution relies on that variable). I'm open to all suggestions but there are some rules of engagement:
Rules: There can't be any major changes in how this function works as many things rely on it, it's something I do not have control over. Assume the function call looks like this jQuery('#divList', 0) and holds a bunch of div elements as children.
The timeout function is the last solution I tried which looks like:
jQuery('#divList').on('mouseenter', function(){
setTimeout(cycle, 100000);
})
.on('mouseleave', function(){
window.clearTimeout();
});
Perhaps something like this? I simplified the animation just to make the example simpler, but you should be able to adapt it to your needs.
First, we have a function that's responsible for animating a set of elements. Every function call returns a new function that allows to toggle the animation (transition between pause and resume).
function startCycleAnimation(els) {
var index = 0,
$els = $(els),
$animatedEl;
animate($nextEl());
return pauseCycleAnimation;
function animate($el, startOpacity) {
$el.css('opacity', startOpacity || 1)
.animate({ opacity: 0 }, 800, function () {
animate($nextEl());
});
}
function $nextEl() {
index = index % $els.length;
return $animatedEl = $els.slice(index++, index);
}
function pauseCycleAnimation() {
$animatedEl.stop(true);
return resumeCycleAnimation;
}
function resumeCycleAnimation() {
animate($animatedEl, $animatedEl.css('opacity'));
return pauseCycleAnimation;
}
}
Then we can kick-start everything with something like:
$(function () {
var $animationContainer = $('#animation-container'),
toggleAnimation = startCycleAnimation($animationContainer.children('div'));
$animationContainer.mouseenter(pauseOrResume).mouseleave(pauseOrResume);
function pauseOrResume() {
toggleAnimation = toggleAnimation();
}
});
Example HTML
<body>
<div id="animation-container">
<div>Div 1</div>
<div>Div 2</div>
<div>Div 3</div>
</div>
</body>
If you want something more generic, it seems there's a plugin that overrides animate and allows to pause/resume animations in a generic way.
You will need to put a flag that each cycle checks before it determines if it is going to run. Then you can just change that flag when the mouse events are triggered. If you need to pick up where you left off when you unpause, consider saving the last value of j
function cycle(variable, j){
if (window.paused) {
window.last_j = j;
return;
}
...
Then when you want to pause, just set window.paused = true . To resume, change it back to false and call cycle again:
cycle(variable, last_j);

Javascript function shows only last element of array

Please take a look at this function:
var msgdiv, i=0;
msgdiv=$("#message");
messages=["Welcome!","Добро пожаловать!"];
function fadeMessages(messages, div){
while(i<messages.length){
div.fadeOut(1000).html('').append(messages[i]).fadeIn(1000);
i=i+1;
}
}
fadeMessages(messages,msgdiv);
What I want to do is, to show one by one elements of array. But, function above shows only last element of array messages.length time. Where I did wrong?
Live example: http://jsfiddle.net/QQy6X/
The while executes much much faster than the fadeOut/fadeIn calls, so you only see the last result. You need to make each animation wait for the previous ones to finish.
I like to solve these problems recursively. Note, it does alter the messages array, but it's not too hard to convert this to use a counter instead (like your original version). Here you go:
var $msgdiv = $('#message'),
messages = ['Xoş gəlmişsiniz!', 'Welcome!', 'Добро пожаловать!'];
function showNext() {
var msg = messages.shift();
if (msg) {
$msgdiv.fadeOut(1000, function () {
$(this).text(msg).fadeIn(1000, showNext);
});
}
}
showNext();
Demo: http://jsfiddle.net/mattball/Exj95/
Here's a version that leaves messages intact:
var $msgdiv = $('#message'),
messages = ['Xoş gəlmişsiniz!', 'Welcome!', 'Добро пожаловать!'],
i = 0;
function showNext() {
if (i < messages.length) {
var msg = messages[i];
$msgdiv.fadeOut(1000, function () {
i++;
$(this).text(msg).fadeIn(1000, showNext);
});
}
}
showNext();
Demo: http://jsfiddle.net/mattball/wALfP/
Your while loop finishes executing before the div has had a chance to fade out. Use an if statement and recursion:
var msgdiv = $("#message");
var i = 0;
var messages = ["Welcome!", "Добро пожаловать!"];
(function fadeMessages() {
if (i in messages) {
msgdiv.fadeOut(1000, function() {
$(this).html('').append(messages[i++]).fadeIn(1000, fadeMessages);
});
}
})();
http://jsfiddle.net/QQy6X/6/
Your while loop finishes very quickly; instead you should wait for the animation to finish before starting the next one. This is easy to do by adding a callback function to fadeIn. I would use this:
+function(){
var $msgdiv = $("#message");
var i = -1;
var messages = ["Xoş gəlmişsiniz!","Welcome!","Добро пожаловать!"];
+function fadeNext(){
$msgdiv.fadeOut(1000, function(){
$msgdiv.text(messages[i = (i + 1) % messages.length]);
$msgdiv.fadeIn(1000, fadeNext);
});
}();
}();
http://jsfiddle.net/Paulpro/QQy6X/7/
In jQuery, you can't intertwine animations and non-animations in the way you are doing and expect them to run in the right order. The animations will go into the animation queue and get sequenced one after another there, but the non-animations will all run immediately. Thus, things won't happen in the right order.
To do something like you want to do, you can use code like this.
messages=["Welcome!","Добро пожаловать!"];
function fadeMessages(msgs, div) {
var i = 0;
function next() {
if (i < msgs.length) {
div.fadeOut(1000, function() {
div.html(msgs[i++]).fadeIn(1000, next);
});
}
}
next();
}
fadeMesssages(messages, div);
This uses the completion functions of both fadeIn() and fadeOut() to carry out the next steps. Here's how it works:
It fades out the div.
In the completion function of the fadeOut, it sets the next message and then starts the fadeIn.
It advances the message counter.
In the completion function from the fadeIn, it calls the function to start the next iteration.
If you want a delay before the fadeOut (to make sure a given message displays for a certain amount of time), you can add that with this .delay(2000) added in the right place:
messages=["Welcome!","Добро пожаловать!"];
function fadeMessages(msgs, div) {
var i = 0;
function next() {
if (i < msgs.length) {
div.delay(2000).fadeOut(1000, function() {
div.html(msgs[i++]).fadeIn(1000, next);
});
}
}
next();
}
fadeMesssages(messages, div);
If you want a delay before the next iteration starts, you can do that like this with a setTimeout:
messages=["Welcome!","Добро пожаловать!"];
function fadeMessages(msgs, div) {
var i = 0;
function next() {
if (i < msgs.length) {
div.fadeOut(1000, function() {
div.html(msgs[i++]).fadeIn(1000, function() {
setTimeout(next, 2000);
});
});
}
}
next();
}
fadeMesssages(messages, div);

Stopping increment at specific height

I am animating images within a logo in a slot-machine type of animation. I need it to stop animating once it gets to the top of the image (and send a callback if possible).
Currently, this is how I'm accomplishing the animation:
window.setInterval(function() {
$('#title-1 img').animate({bottom : '-=60px'})
}, 5000);
Any ideas on how I would get it to stop, and to send the callback?
So I assume you have a sprite image containing multiple logos, you want them to slide each 5 seconds until you reach the last one, and then call the callback?
var cnt = 6,
$img = $('#title-1 img'),
i = 0;
function animate_logo(cb) {
if (i < cnt) {
$('#title-1 img').animate({bottom : '-=60px'});
i += 1;
setTimeout(function () {animate_logo(cb)}, 5000);
}
else {
cb();
}
}();
var interval = window.setInterval(function() {
$('#title-1 img').animate({bottom : '-=60px'},
function(){
if(`some stop point`) clearInterval(interval);
}
);
}, 5000);
I would not suggest using a setInterval when dealing with animations due to the way newer browsers are making changes to the way setInterval and setTimeout work when the tab is not the active tab.
var $title1 = $("#title-1");
var $title1img = $title1.find('img');
function anim(){
if ($title1.height() < parseInt($title1img.css("bottom"))) {
setTimeout(function(){
$title1img.animate({bottom : '-=60px'},anim);
},5000);
}
}
$title1img.animate({bottom : '-=60px'},anim);
Edit: another reason not to use setInterval to fire off animations is due to the reqeustAnimationFrame that was implemented in 1.6 and removed in 1.6.3, which will more than likely be added back in 1.7. If you write code now that will be compatible later, that's less maintenance you will have to do later if you end up being required to upgrade.
Here's a jsfiddle http://jsfiddle.net/czUnU/
Edit: function...
function animColumn(title,img){
function anim(){
if (title.height() < parseInt(img.css("bottom")) {
setTimeout(function(){
img.animate({bottom : '-=60px'},anim);
},5000);
}
}
img.animate({bottom : '-=60px'},anim);
}
animColumn($("#title-1"),$("#title-1 img"));
animColumn($("#title-2"),$("#title-2 img"));
animColumn($("#title-3"),$("#title-3 img"));
http://jsfiddle.net/czUnU/1/

window.clearInterval is not working?

I'm using JS to animate two images by having them toggle on and off. I have an initial image which, when the animation is turned on, appears on the screen and stays on. The second image, which has a higher z value, is then set to toggle on and off every 1 second in the same location as the first image, so it appears as if the two are alternating.
I'm using window.setInterval to make the second image blink, but when I need to turn the animation off (and I'm removing both images from the screen), my window.clearInterval is not "working" The first image will be gone, but the second one keeps blinking on and off every second.
Code:
function notebookNotification(setting)
{
$("#lightNotificationContainer").show();
var notificationAnimation = window.setInterval('$("#darkNotificationContainer").toggle()', 1000);
if(setting == 0)
{
window.clearInterval(notificationAnimation);
$("#lightNotificationContainer").hide();
$("#darkNotificationContainer").hide();
}
}
Anyone see why it isn't working?
Reading between the lines, I think what you're saying is this:
You execute notebookNotification(1); and the animation starts
You execute notebookNotification(0); and the animation does not stop.
My guess is that you want notebookNotification(0) to disable the flashing.
In order to do that, you need to rework this function considerably. You need to store the intervalID that comes from setInterval in a variable that survives outside of the scope of this function and can be used for clearInterval on subsequent calls to this function.
For example:
var intervalID;
function notebookNotification(setting)
{
if(setting == 0)
{
if(intervalID) {
window.clearInterval(intervalID);
intervalID = null;
}
$("#lightNotificationContainer").hide();
$("#darkNotificationContainer").hide();
}
else
{
$("#lightNotificationContainer").show();
if(!intervalID) {
intervalID = window.setInterval('$("#darkNotificationContainer").toggle()', 1000);
}
}
}
Here, try this:
http://jsfiddle.net/WGxmy/
Saving the interval to a global variable -- not one inside a function -- lets you clear it later.
var keepflashing = true;
var isShowing = true;
function notebookNotification()
{
if(!isShowing)
$("#lightNotificationContainer").show();
else
$("#lightNotificationContainer").show();
isShowing = !isShowing;
if(keepflashing)
setTimeout( function(){ notebookNotification(setting); },100);
else
{
$("#lightNotificationContainer").hide();
$("#darkNotificationContainer").hide();
}
}
Maybe you can avoid calling clearInterval() generally?
function notebookNotification(setting)
{
if(setting == 0)
{
$("#lightNotificationContainer").hide();
$("#darkNotificationContainer").hide();
}
else
{
$("#lightNotificationContainer").show();
window.setInterval('$("#darkNotificationContainer").toggle()', 1000);
}
}

Categories

Resources