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)
Related
Ok, so I've made a timer that makes parts of my SVG map fadeOut as they cross certain thresholds. However, I want to mess with other parts of the CSS.
I looked at this post, but couldn't make sense of it in terms of my problem.
** Edits Below**
Thanks for the help, I took a look at my code and tried to clean out some of the stuff that didn't need to be there. I also restructured my if statement, putting it inside of the JQuery code. I tried the suggestion below, assigning the var timer outside the interval function, but then my start button no longer worked and the script started running on page load. So, I moved it back to keep things working.
Also, put my code into JSFiddle, but I couldn't get it to work correctly. Will spend some more time familiarizing myself with that in the meantime. Thank you for introducing me to that.
As for my original question:
the .animate() tag works so long as I set it to change the opacity attribute, but has no effect on the other attributes I want to change. I know SVG and CSS have different attribute names, and I've tried both types of names. Here is my code below. I am trying to get the .animate() effect to change the fill color and stroke-width.
var i,timer;
i = 2013;
function start() {
timer = self.setInterval("increment()", 800 )
}
function increment() {
i++;
document.getElementById("timer_out").innerHTML = i ;
$(document).ready( function() {
if (i == 2014) {
$('#AL').animate( {
opacity: 0.3 } , 500 );
}
});
}
function stop() {
clearInterval(timer);
timer = null;
}
function reset() {
stop();
i=2013;
document.getElementById("timer_out").innerHTML = i;
}
I'm really just concerned with the JQuery statement, which works perfectly fine until I replace opacity with a different CSS attribute.
Thanks again for the attention and advice.
1) if you divide any number by 1 you get the original number, your divisions are doing nothing as far as i can tell.
2) setInterval should be written:
timer = setInterval(increment, ( 1000 / divide ))
also note increment() and start() are not good name choices to have in global scope, how many people will think of those names, use anonymous functions maybe to contain scope
(function()
{
// function is now contained within anonymous function scope and not accessible outside
function increment(){}
})()
3) logically step though your code in your head. your code wont work
4) create a fiddle of what you have done so far
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.
Thanks for taking the time to read this post. Finally built up enough courage to ask a question here :) Hope I ask my question in the correct way.
My problem is that I am trying to randomly animate "squares" around a page but am having an issue with the setInterval method.
You can find it here http://jsfiddle.net/xEMXh/
In the Init function I am trying to go though each ghost element and set it a interval function that will then make it animate in a random direction every X seconds.
for (ghostCount = 0; ghostCount < ghosts.length; ghostCount += 1) {
var speed = LesMccutcheon.GhostHunter.GetSpeed($(ghosts[ghostCount]).attr('data-speed')),
activeGhost = ghosts[ghostCount];
window.setInterval(function() {
LesMccutcheon.GhostHunter.MoveInDirection(activeGhost);
}, speed);
}
What is happening is only the last "ghost" element is animating. I know this is because in the for loop I am overriding the variable and by the time the first interval is called activeGhost is the last set in the for loop.
I tried using ghosts[ghostCount] directly in the set.Interval but it seems set interval cannot access this. I tried setting it to a variable inside the set.Interval anom function but it logged as undefined.
My only other half baked idea is to try and set an incremental var identifier but that seems quite counter productive and not a good way to go.
Thanks in advance for any advice!
Les
in your code the variable activeGhost will be overwritten for each loop.
just put the calling of setInterval into a closure like this
( function (activeGhost) {
window.setInterval(function() {
LesMccutcheon.GhostHunter.MoveInDirection(activeGhost);
}, speed);
} ( activeGhost) );
this way the reference to the correct element should be kept.
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.
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