I recently try to learn Webdesign and wanted to do a simple fadeout of an image with JavaScript. I Know there is an easier way with jQuery and after some time with this problem i used that way, but i stumbles across it and want to unterstand why this happens.
To break down the process lets say I have an image and want it to reduce its opacity every 0.1 seconds. Therefore I used a for loop and called a fade function with SetTimeout(fade,100). But every of the 10 loops are executed at the same time. I tried it even more simple with
setTimeout(fade,100);
setTimeout(fade,100); ....
Even then all 10 instances of the funtion get executed at once.
So for future projects: why is that and is there a possible workaround?
Thank you very much
You should use setInterval.
setInterval(fade,100);
setInterval is for a repetitive task. It will keep running. Makes sure you clear interval once you are done to prevent memory leakage.
You are using a for loop, which is not the correct way.
Assume at the current instant the time is: 0
You ran a for loop and created 5 setTimeouts. It takes some time to create this but you can't notice it.
So, each setTimeout should execute after 100 ms.
But they are created at:
1. 0.000000001
2. 0.000000002
3. 0.000000003
4. 0.000000004
5. 0.000000005
And they will call the callback after:
1. 100.000000001
2. 100.000000002
3. 100.000000003
4. 100.000000004
5. 100.000000005
(Just for demonstration)
So, you are not going to notice them and these (setTimeout and setInterval) are not absolute. They make take longer.
Hope it helps. :D
The answer is the event loop. To thoroughly understand this behaviour you have to understand all stages of event loop and especially how setTimeout and setInterval is handled
MDN article on event loop
MDN event loop
Rising stack article will help you in getting a clear understanding of event loop along with micro and macro task
rising stack event loop explained
In nutshell all setTimeout gets processed in same tick of the loop.
Also for your case setInterval is much better
There are multiple ways to achieve it. As others have suggested you can use setInterval or use setTimeout to call recursively. If you must use for loop for some reason, that is also possible.
The basic idea is your call to second setTimeout must be given only after the first execution of fade function or otherwise increase the timeout period between successive calls to setTimeout.
//\//\//\// method 1
function fadeRecur() {
var d1 = document.querySelector( ".d1" );
if(d1.style.opacity == '') d1.style.opacity = 1;
//console.log(d1.style.opacity);
if(d1.style.opacity > 0) {
d1.style.opacity -= 0.1;
setTimeout(fadeRecur, 100);
}
}
setTimeout(fadeRecur, 100);
//\//\//\// method 2
var fsi;
function fadeInter() {
var d2 = document.querySelector( ".d2" );
if(d2.style.opacity == '') d2.style.opacity = 1;
// console.log(d2.style.opacity);
if(d2.style.opacity > 0) {
d2.style.opacity -= 0.1;
} else {
clearInterval(fsi);
}
}
fsi = setInterval(fadeInter, 100);
//\//\//\// method 3
function fadeLoop() {
var d3 = document.querySelector( ".d3" );
if(d3.style.opacity == '') d3.style.opacity = 1;
//console.log(d3.style.opacity);
if(d3.style.opacity > 0) {
d3.style.opacity -= 0.1;
}
}
for(var i=1; i<=10; i++) {
setTimeout(fadeLoop, i*100);
}
.d1 {
background-color: rgba(255, 0, 0, 0.5);
}
.d2 {
background-color: rgba(0, 255, 0, 0.5);
}
.d3 {
background-color: rgba(0, 0, 255, 0.5);
}
<div class='d1'>recursive setTimeout</div>
<div class='d2'>single setInterval</div>
<div class='d3'>setTimeout in for loop</div>
If you want to use setTimeout to change opacity for every 100 ms, you can try this
setTimeout(fade,100);
setTimeout(fade,200);
setTimeout(fade,300);
...
or set a timing variable in loop
for(let timing=100,timeing<1000,timing+=100) setTimeout(fade,timing);
The 10 setTimeout function almost start at same time and trigger at every 100ms till 1000ms. Because it is asynchronous function, it doesn't wait for previous command to finish.
Related
I am trying to call a function from within a loop in a way that when the function finishes executing, the loop continues to the next iteration and calls it again. But instead the loop doesn't wait for the function to finish and instead calls 4 instances of the function and runs them at the same time! Should I put the whole function in the loop or is there to make the loop wait for the function to be executed? Thanks
for (var i=2; i<=4; i++){
galleryAnimation(i); //This is executed 3 times at once
}
function galleryAnimation(i){
$("#pic" + i).delay(delayTime).fadeIn(duration);
}
The function is being executed 3 times just like you requested, the problem is that both delay and fadeIn use timers: they set a timer in the future when the function will be executed and return immediately: they are non-blocking calls. So, in your case, because you're calling the function 3 times at, let's say, 0.0001s, 0.0002s, and 0.0003s, the three kick in at, let's say, 5.0001, 5.0002 and 5.0003.
What you had in mind were blocking calls to these functions. You expected the whole execution to stop until the animations were finished. This would stop the whole browser Javascript engine for the duration, meaning no other animation or user javascript interaction would be possible.
To solve it, you have to use callbacks. You can supply a function to fadeIn that will be called once the animation has completed:
http://api.jquery.com/fadeIn/
You can use queues to simulate the same on delay():
Callback to .delay()
Simplistic solution: Increase the timeout by a factor every time.
var i, factor,
duration = 250,
delayTime = 500;
for (i = 2, factor = 0; i <= 4; i++, factor++) {
galleryAnimation(i, factor);
}
function galleryAnimation(i, factor) {
$("#pic" + i).delay(factor * delayTime).fadeIn(duration);
}
This runs the same way your approach does, only the delays get longer every time.
Generic solution 1 - work with setInterval() to have your worker function (the one that does the fadeIn) called in predefined intervals:
var elements = $("#pic2,#pic3,#pic4").toArray(), // or any other way to select your elements
duration = 250,
delayTime = 500,
intervalId = setInterval(function () {
$(elements.shift()).fadeIn(duration);
if (elements.length === 0) {
clearInterval(intervalId);
}
}, delayTime);
Generic solution 2 - work with callbacks that are called when the previous animation finishes:
var elements = $("#pic2,#pic3,#pic4").toArray(), // or any other way to select your elements
duration = 250,
delayTime = 500,
next = function () {
$(elements.shift()).delay(delayTime).fadeIn(duration, next);
};
next(); // start the chain
one thing you can do, is to use an identifier (boolean) and then, in the loop, you test the identifier to decide if the loop can continue or stop.
For example,
function galleryAnimation(i, iBool){
$("#pic" + i).delay(delayTime).fadeIn(duration);
iBool = 0;
}
Then on the return;
for (var i=2; i<=4; i++){
galleryAnimation(i, iBool);
// If iBool = 0 then continue
// Else Break
}
that might be a solution, as the loop will need the returning value to determine the next step, to continue or break.
I came with my own solution that seemed to work perfectly, this is my code:
function fadeInAnimation(index){
$("#pic" + index).delay(delayTime).fadeIn(duration, function(){
photoIndex();
});
}
function photoIndex(){
index++;
fadeInAnimation(index);
}
fadeInAnimation(index);
});
I have realized that using loops is a really bad idea with things like this. This code will allow me to add as many images as I want just by renaming them in the folder rather than coding them in manually as callbacks for each photo.
Thanks for those who answered. Your suggestions are great and will definitely remember them in other applications like this one
I'm learning javascript for fun, and am having a weird problem. I'm trying to create my own fade-in function. However, my code doesn't work, it simply shows the "content" div in full opacity.
The setContentOpacity function does work, I've tested it by itself and it works like a charm.
Ideally what I think should be happening is that 1000 "setTimeout" calls should be placed on the "stack", with the first one setting opacity low with no timeout, the second one setting opacity a little higher with a small timeout, all the way to the last call which sets opacity to 1000 with 3000 timout.
So basically, it should be setting opacity to 0 right away, to ~333 in 1 second, to ~666 in 2 seconds, and to 1000 in 3 seconds. I think my logic is sound here; the calls to setting opacity should resolve in a manner over time that creates a fade in effect.
So here's the relevent code:
<script language='JavaScript' type='text/JavaScript'>
//takes a value from 0-1000
function setContentOpacity(value) {
document.getElementById('content').style.opacity = value/1000;
document.getElementById('content').style.filter = 'alpha(opacity=' + value/10 + ')';
}
function fadeInContent(){
setContentOpacity(0);
for (var i=0;i<=1000;i++)
{
setTimeout(function(){setContentOpacity(i);}, (i*3));
}
}
onload=fadeInContent;
</script>
(note: I tried calling simply setTimeout(setContentOpacity(i), (i*3));, but it didn't seem to work, and I got slightly better results using the anonymous function)
Any idea what's wrong here? Thanks in advance!
You need to capture the value of i when assigning to setTimeout.
Try this
for (var i=0;i<=1000;i++)
{
(function(ind) {
setTimeout(function(){setContentOpacity(ind);}, (ind*3));
})(i);
}
As you know the scope of a variable is function scoped. And the same value of i is shared by all the callbacks of setTimeout. So the value of i will be 1000 . So looks as if it had no effect, this is because the value of the variable scoped will always be the last iteration as it is shared by the same common scope. . By enclosing it in Immediately Invoked Function Expression you are creating a new function with the value of i scoped to it.
Check Fiddle
I think the major issue here is that you're creating a 1000 setTimeout callbacks. An alternative, if you wanted to run something every x seconds would be setInterval.
var i = 0;
var refreshIntervalId = window.setInterval(function(){
setContentOpacity( i * 3 );
i++;
if( i > 1000 ) {
clearInterval( refreshIntervalId );
}
}, 1000);
It will run once a second (1000ms), calling your opacity function each time until it hits a 1000, then turns off again.
I've background image and by using small javascript code, it moves from right to left.
HTML code
<div id="clouds_image"></div>
Javascript code
var g=0;
var speed=30;
function rollClouds() {
document.getElementById('clouds_image').style.backgroundPosition=g+'px 0';
g--;
scroller=setTimeout(function(){rollClouds()},speed);
}
window.addEventListener?
window.addEventListener('load',rollClouds,false):
window.attachEvent('onload',rollClouds);
But i've noticed that, with time my PC CPU memory usage increased ! causing overload on my pc and if i disabled that javascript code, it back to normal.
My question
so i think i need to modify this javascript code that it not keep working forever, i mean, i want to make it to repeat that action only 5 times then stop , maybe i need to define value of g but i'm not good in javascript so any help ~ Thanks.
You need to use a variable to count how many times that function was executed, and use setInterval instead of setTimeout: See example
http://jsfiddle.net/EQDjx/206/ (my counter start from 100 and goes down to 0)
for a more nice effect i recomand you to use jquery. See animate function
http://api.jquery.com/animate/
var g = 1000;
var speed=300;
var counter = 100;
function rollClouds() {
document.getElementById('clouds_image').style.backgroundPosition=g+'px 0';
g--;
if (counter < 1) clearInterval(interval);
}
interval = setInterval(function(){rollClouds()}, speed)
A cleaner solution might be to use jQuery to move the background:
function moveClouds() {
$("#clouds_image").css({left:"-2000px"});
$("#clouds_image").animate({left:"2000px"},10000);
}
Then you might set an interval to trigger it every x milliseconds.
setInterval(moveClouds,10000)
JSFiddle is here: http://jsfiddle.net/qXpVX/
How to set delay in for loop execution
for(i=0; i<=10;i++){
var s=i;//This line should execute for every 2 secs only
}
How to give loop delay in java script....
I dont want like below..I want without using setTimeout...
for(i=0; i<=10;i++){
setTimeout("setvalue()",2000); //This alert should display for every 2 secs only
}
function setvalue()
{
var s=i;
}
please help me...
Use setInterval()
var i = 0;
var interval = setInterval(function(){
setValue();
i += 1;
if(i == 10)
clearInterval(interval);
}, 2000);
There is no way to sleep for 2sec without freezing the whole browser. Javascript is single threaded.
You can't. JS runs in a single thread and any attempt to delay that thread will freeze the entire page. Using setTimeout is your only option.
EDIT: or setInterval; either way, there is no non-hairy way to express "halt execution here for x milliseconds."
Using setTimeout is inevitable, however, a recursive function might be a better solution for this one:
var i=0;
function recurs() {
i = s;
i++;
if (i <= 10) recurs();
}
recurs();
As others have stated, setTimeout can be used very well to handle these sorts of scenarios and setInterval could also be used but is discouraged by some.
You can even recursively call a function that has setTimeout built into it as mentioned in the MDN documentation of setInterval. The heading there mentions 'dangerous usage' but their solution to the danger is the block of code beneath.
There it mentions that to have a loop executing every x seconds (or milliseconds) then you can do the following and know for sure that the functions will only be executing one at a time and in-sequence:
(function loop(){
setTimeout(function(){
// logic here
// recurse
loop();
}, 1000); // repeat loop 1 second after this branch has completed
})();
And if you want it to only do that a limited number of times, then you can create a variable outside of the loop and only recursively execute if the count is smaller than the number of times you want to execute for. Such as this:
var count = 0;
(function loop() {
setTimeout(function() {
// logic
count++;
if (count < 10) {
loop();
}
}, 1000);
})();
I have the following JavaScript code:
var cILo=true;
var img1="images/title-2a.png";
var img2="images/title-2b.png";
function loadblinker() {
for (var i=0,l=Math.floor(Math.random()*10);i<l;++i) {
cILo=!cILo;
if (cILo) {
document.getElementById("lamp").src=img1;
} else {
document.getElementById("lamp").src=img2;
}
!!!! THIS LINE HERE !!!!
}
document.getElementById("lamp").src=img1;
setTimeout("loadblinker()",Math.floor(Math.random()*10000));
}
Where I have marked the code with the phrase "!!!! THIS LINE HERE !!!!", I need some way to pause the execution for a split second. This code, when it is done, is going to give the appearance of a short circuiting light (light in the video games). I was wondering as to how I would pause the code seeing as there appears to be no natural method.
I think a better approach would be to eliminate the for loop by using setInterval. You could then clear the interval after Math.floor(Math.random()*10) iterations. I wouldn't recommend blocking execution by just spinning in a loop. Most browsers freak out when you do that.
Typically this is handled in JavaScript by calling setTimeout, passing it the code to be executed after the delay. Or in other words, instead of pausing within a function, you break your function in two: one part to be executed before the delay and the next part to be executed after.
You are already recursively calling your function via setTimeout, so you are almost there. See if you can restructure your code so that you get rid of the for loop and instead pass in the maximum number of iterations. Decrement that counter on each call. If after the decrement, your counter is greater than zero, call setTimeout to call the function again.
function pause(ms)
{
var d = new Date();
var c = null;
do
{
c= new Date();
}
while(c - d < ms);
}
Use pause(1000); to pause for 1 second.
Courtesy of this website.
Javascript in browsers does not have the ability to do a synchronous pause. You can hack your way around it, as muntoo suggested, but you shouldn't do it.