Scenario:
I want to create a jQuery controllable jackpot "spinner" that will rapidly sequence a number of random images through a div before settling on one, with the delay interval between each equal but changeable. For mockup purposes, I'm simply changing CSS color classes to a box, although in the final I'll use background images.
I thought this would be a no-brainer to do with a loop. I'm sure there's a more efficient way to do this, but guessed the below would work fine. However, I discovered I have no way to control the CSS color swap speed. This whips through the color class changes instantly and just shows the last one. What I'd like is a delay where indicated.
jQuery delay() doesn't seem to work when chained with addClass(), though it works fine with effects. So I tried using window.setTimeout, but as far as I can see, in this context it requires a kludgey function call. The code as written below executes all the function calls after the loop has run. Is this a closure issue? Don't want to use setInterval because these will be limited iterations.
Thanks for any advice!
for (var j= 9; j >= 0; j--) {
$('#box1').attr('class', 'boxes'); // strips all current classes, replaces them with class 'boxes', which has general CSS characteristics
var numRand = Math.floor(Math.random() * 6);
var randomClass = colorArray1[numRand]; // pull random class from an array of six choices
$('#box1').addClass(randomClass);
// Everything above here works fine, would like loop delay here
// Tried using straight-up setTimeout -- doesn't appear to like loops
window.setTimeout(outerFunc, 1000);
};
function outerFunc() {
alert('nobody here but us chickens!');
};
If you want to use .delay() with a method like .addClass(), you can add it to the queue with jQuery's .queue() method.
$('#box1').delay(1000)
.queue(function( nxt ) {
$(this).addClass(randomClass);
nxt(); // allow the queue to continue
});
Otherwise, if I get what you want, you could multiply the 1000 ms for the setTimeout() by the current value of j, so that each time the duration increases.
window.setTimeout(outerFunc, (1000 * j));
setTimeout and setInterval work differently in javascript to the way you want to use them.
Both functions take the function that you pass in and attach them to the window DOM object. Then, after the delay you have passed in has passed, and when there is no other script currently running, they get called from the window object.
To get the functionality you are after, you will need to convert your code so that the jQuery addclass call is inside the function you are passing to setTimeout.
Perhaps recursion would work?
// this code is not tested
var j = 9;
function myFunc() {
// code here
j--;
if(j >= 0) setInterval(myFunc, 1000);
}
I haven't used the queue class in jQuery myself (first I've heard of it, but it sounds cool). That might be the better answer, but this should be a decent alternative if the queue doesn't work as expected.
UPDATE: I just noticed that in your code it looks like you are expecting setTimeout to work like Thread.Sleep in .Net. setTimeout doesn't work that way. It works more like Thread.Start where your code continues on as soon as you call it.
Related
So it's probably some mis-understanding on the best way to use the setTimeout method provided by javascript but im having trouble implementing it in a way that makes sense.
Essentially I have an Array with numbers between 1-4 and each number corresponds to a button getting let up.
for(let i = 0;i < arr.length;i++){
view.renderPane(arr[i]) //All this does is set the .css
view.renderPane is pretty simple:(I have a separate function that clears(sets opacity back to .5) it, but if possible i'd like to just put that in here.
renderPane(pane){
$("."+pane).css("opacity", "1");
console.log("Activating Pane "+ pane)
}
So I tried setting up a timeout thinking I could call the renderPane within the timeout, but all it did was set up a bunch of timeouts that basically fired off after X seconds (or milliseconds). Is there a way I can call the renderPane(pane) function every 1 second (to set up a delay) inside this for loop? or will I need to set up something else?
No need to use a loop, just create a function which continuously schedules itself with setTimeout until it's done — in this case, it removes an item from the array in each call and stops when the array is empty:
(function callee() {
view.renderPane(arr.shift());
if (arr.length)
setTimeout(callee, 1000);
})();
Example: https://jsfiddle.net/2fwht35d/
There are many other ways to implement this behaviour, but this should give you a good starting point.
I am making a simple JS program and am having some trouble. You can view it here
http://codepen.io/TheAndersMan/pen/mOGVEy?editors=0010
Enter in your name and press enter, it will animate your name, but the letters disappear after the animation, which is understandable. what I don't understand is why my setTimeout isn't working and making them re-appear.
So here is the basic problem:
var timeOut = (a / 2 + 1) * 1000;
document.querySelector(".spanWrap").style.width = char.length * 60 + "px";
setTimeout(function() {
thang.style.opacity = "1"
thang.style.marginTop = "0";
}, timeOut);
So hopefully that is all the info you will need, if not reference my pen, this is all in a for loop and if you see undefined variables here, they are defined in my pen.
So if someone can tell me what I did wrong that would be great.
Thanks in advance!
You have the infamous closure bug.
I noticed that you are transpiring using Babel. Using let instead of var for your variables local to your loop should fix the issue. Notice that in your broken CodePen, the last letter stays while the rest disappear. That is because your thang is always equal to the last letter by the time the timeout handlers execute (the loop has concluded long before).
See http://codepen.io/anon/pen/ObaVyb.
Also, a better idea might be to take a look at animation-fill-mode: forwards, which allows you to retain styles after animations have been run.
Finally, for those of you not using ES6, this code will allow you to achieve the same functionality without creating another wrapper function. (Essentially, setTimeout allows you to pass arguments to your callback when you register each handler.)
setTimeout(function (thang) {
thang.style.opacity = "1"
thang.style.marginTop = "0";
}, timeOut, thang);
The problem is, that you have several timeouts in for loop, that needs references to thang variables, but when your timeouts will be executed thang variable will be equal to the last thang in the cycle, so all the timeout would have the same reference. Hope it's clear.
So, to fix that, you need to bind your timeouts with thangs variables, one by one.
For example, you can do it with closures:
(function(thang) {setTimeout(function() {
thang.style.opacity = "1"
thang.style.marginTop = "0";
}, timeOut);})(thang)
In the below code I am trying to loop three functions that only fire once the previous function is complete, with the last function then calling the first to start the process all over again. Using setInterval/setTimout are not going to be good answers for this because of RequestAnimationFrame taking their place as a cleaner way of doing things but I dont know how to apply RequestAnimationFrame to this code. Also the question of why the third function does not call the first wouldn't be answered by using those two methods as well.
<body onload="runOne()">
function runOne(){
var x = document.getElementById("rightBox");
document.getElementById("rightBox").style.animation = "scrollTextTwo 10s";
x.addEventListener("animationend",runTwo);
};
function runTwo(){
var x = document.getElementById("rightBoxTwo");
document.getElementById("rightBoxTwo").style.animation = "scrollTextTwo 10s";
x.addEventListener("animationend",runThree);
};
function runThree(){
var x = document.getElementById("rightBoxThree");
document.getElementById("rightBoxThree").style.animation =
"scrollTextTwo 10s";
x.addEventListener("animationend",runOne);
};
The above code works only once, it will play/animate all three functions but then stops after "runThree()" is complete. I would like to know how "runThree()" can call "runOne()" once run three is completed with its animation?
So, I think you have several options: What could work is that you reset the the animation of rightBox in function runTwo with animation: none. If you assign scrollTextTwo 10s back to the rightBox it should start again. Equivalent for the other ones.
See the following Codepen, where I implemented an endless CSS animation using JavaScript.
Alternatively it's also possible to do it without JavaScript: You can use animation-delay, infinite repeating and some other tricks to create really complex animation timelines, maybe also take a look at the following question.
In fact, when I use
setTimeout(a(),60);
setTimeout(a(),120);
setTimeout(a(),180);
setTimeout(a(),240);
It is supposed to be 60ms gap between calling's of a functions.
But it isnt, especially when it is fired during page loading or animating elements. In fact that gap gets even 2x longer when browser 'has hard work to do'. In some cases it can be visible easly.
The point of question is - is there any other way to synchronize events or functions in time in javascript?
The timing in setTimeout(a(),60) in simple terms translates to I will run this function no earlier than 60ms, but if I get busy it could be later than that.
Therefore, setTimeout does not promise when the execution will take place, only that it will take place sometime after the given time in milliseconds.
So to answer your question, no there is no way to guarantee execution time with setTimeout but you can load your script after the DOM has loaded so that JavaScript is not busy anymore loading other things. In jQuery you can use the $(document).ready() function for that purpose.
Read this article by John Resig for more information about timing in JavaScript: http://ejohn.org/blog/how-javascript-timers-work/
Try this:
setTimeout(a,60);
setTimeout(a,120);
setTimeout(a,180);
setTimeout(a,240);
Note that the function doesn't have the ()s.
In your particular case, setInterval() might work:
var count = 0, interval = setInterval(function() {
count += 1;
if (count > 4) {
clearInterval(interval);
} else {
a();
}
}, 60);
Note that jQuery has a built-in animation feature that uses the different, better approach of simply treating an animation as a function of time and frequently checking the clock, so an unexpected delay would simply make the animation a bit less smooth.
I'm calling a javascript function that sets the opacity of an iframe an unknown amount of times in rapid succession. Basically this tweens the alpha from 0 to 100.
here is the code
function setAlpha(value)
{
iframe.style.opacity = value * .01;
iframe.style.filter = 'alpha(opacity =' + val + ')';
}
My problem is that for the first time it is working in ie (7) and not in firefox (3.02). in Firefox I get a delay and then the contentdocument appears with an opacity of 100. If I stick an alert in it works, so I'm guessing it is a race condition (although I thought javascript was single threaded) and that the setAlpha function is being called before the last function has finished executing.
Any help would be greatly appreciated. I've read the 'avoiding a javascript race condition post' but I think this qualifies as something different (plus I can't figure out how to apply that example to this one).
The issue is that most browsers don't repaint until there is a pause in the javascript execution.
This can be solved by using setTimeout, as others have suggested. However, I recommend using something like jQuery, or any of the javascript libraries to do animations. Running setTimeout 100 times is a bad idea because the length of the animation will vary based on the browser and speed of the user's computer. The correct way to do animations, is to specify how long they should last and check the system time to determine how far the animation should progress.
function fadeIn(elem,animation_length) {
var start = (new Date()).getTime();
var step = function() {
window.setTimeout(function() {
var pct = ((new Date()).getTime() - start)/animation_length;
elem.style.opacity = Math.min(pct,1);
if (pct < 1)
step();
},20);
};
step();
}
[edit:] The code above is only to illustrate how to do animations based on the system clock instead of simple intervals. Please use a library to do animations. The code above will not work on IE, because IE uses "filter:opacity(xx)" instead of "opacity". Libraries will take care of this for you and also provide nice features such as completion events, and the ability to cancel the animation.
Javascript doesn't run across multiple threads so you're safe from race conditions (ignoring upcoming Worker thread support in Safari and Firefox :D ).
Simple question, how are you calling setAlpha multiple times, firefox, safari and opera all coalesce style sheet updates -- eg. they won't repaint or even recalc style info while js is running unless they have to. So they will only paint if JS has completed.
So if you're doing
while(...) setAlpha(...)
they won't update, you'll probably need to use setTimeout to trigger multiple distinct calls to update the style.
An alternative would be to use a library such as jQuery, mootools,etc that i vaguely recall provide a simplified mechanism to do these types of animations and transitions. As an added bonus i believe at least a few libraries will also use webkit transition and animation css rules when available (eg. Safari, and i think the latest firefox builds)
[edit: caveat: i haen't actually used any of these libraries, i only read about what they're supposed to do. My sites render the same in lynx as any other browser because i couldn't design my way out of a paper bag :D ]
Are you using setTimeout or a tight loop? If you're using just a loop to call the function, then switch to using setTimout.
example:
function setAlpha(value)
{
iframe.style.opacity = value * .01;
iframe.style.filter = 'alpha(opacity =' + val + ')';
if(value < 100 ) {
setTimeout(function () {setAlpha(value+1)},20);
}
}
setAlpha(0);
Because you see, it's not just javascript that's single threaded. It's the whole damn browser. If your javascript goes into a tightloop, you hang the whole browser. So the browser pauses waiting for javascript to finish, and doesn't even have a chance to update the screen, while your code is rapidly changing some dom values.
Some browsers are smart enough to delay changes to the DOM until the call stack is empty.
This is a generally a smart thing to do. For example, if you call a function that changes an element to yellow, and immediately call a function that changes the same element back to it's original state, the browser shouldn't waste time making the change, since it should happen so quickly as to be imperceptible to a user.
The setTimeout(func, 0) trick is commonly used to force Javascript to delay execution of func until the call stack is empty.
In code:
function setAlpha(opacity){
some_element.style.opacity = opacity;
}
/**
* This WON'T work, because the browsers won't bother reflecting the
* changes to the element's opacity until the call stack is empty,
* which can't happen until fadeOut() returns (at the earliest)
**/
function fadeOut(){
for (var i=0; i<10; i++){
setAlpha(0.1*i);
}
}
/**
* This works, because the call stack will be empty between calls
* to setAlpha()
**/
function fadeOut2(){
var opacity = 1;
setTimeout(function setAlphaStep(){
setAlpha(opacity);
if (opacity > 0){
setTimeout(setAlphaStep, 10);
}
opacity -= 0.1;
}, 0);
}
All this boils down to being a wonderful excuse to use one of many javascript libraries that handle this tricky stuff for you.
Edit: and here's a good article on the tricky Javascript call stack