something wrong with my loop? - javascript

I want to make a box disappear and reappear alternatively by using the loop. The problem is that the box does not appear at all though the loop runs until i=10. Can someone explain this?
for(var i=0;i<10;i++){
if(document.getElementById("box").style.display=="none"){
document.getElementById("box").style.display="block";}
else{
document.getElementById("box").style.display="none";
}
console.log(i);
}

The simplest explanation is that the loop iteration finishes before the rendering takes place, and you only get the end result.
See this modified version of your code, it's kind of forcing the drawing by using timeouts:
for (var i = 0; i < 10; i++) {
setTimeout(toggle, i * 100);
}
function toggle() {
if (document.getElementById("box").style.display == "none") {
document.getElementById("box").style.display = "block";
} else {
document.getElementById("box").style.display = "none";
}
}
#box {
width: 50px;
height: 50px;
background: #eee;
}
<div id="box"></div>

Usually when you loop simply with (while, for, etc ...) it will run in a clock-cicle(nano-seconds, or milli-seconds) ...
For the computer, your box is appearing and disappearing. But not for humans, simply because it's too fast.
I suggest you to try you algorithm with some Javascript animations, like this one
http://www.w3schools.com/jquery/eff_fadein.asp

Related

Why won't my HTML Images fade

I'm trying to create a simple slideshow effect. I have 10 images, and I've created a basic HTML page with 2 buttons to go to the right or left image. On clicking the button, the images change.
Now, I'm trying to add a basic fade functionality to the changing image. But the fade effect isn't getting displayed. When I put alerts, I notice that the fade is taking place, but without the alerts it is too fast to be visible. Also, it is happening on the previous image, instead of the next one.
<html>
<head>
<style>
.main {
text-align: center;
}
.centered {
display: inline-block;
}
#image {
border: solid 2px;
width: 500px;
height: 500px;
}
#number {
font-size: 30px;
}
</style>
<script>
function goLeft() {
var image = document.getElementById("image");
var pos = document.getElementById("number");
if(Number(pos.innerHTML)==1) {
image.src = "Images\\10.jpg"
pos.innerHTML = 10;
} else {
image.src = "Images\\" + (Number(pos.innerHTML)-1).toString() + ".jpg"
pos.innerHTML = (Number(pos.innerHTML)-1).toString();
}
for (var i=0; i<25; i++) {
setTimeout(changeOpacity(image, i), 1000);
}
}
function changeOpacity(image, i) {
alert(parseFloat(i*4/100).toString());
image.style.opacity = (parseFloat(i*4/100).toString()).toString();
}
function goRight() {
var image = document.getElementById("image");
var pos = document.getElementById("number");
if(Number(pos.innerHTML)==10) {
image.src = "Images\\1.jpg"
pos.innerHTML = 1;
} else {
image.src = "Images\\" + (Number(pos.innerHTML)+1).toString() + ".jpg"
pos.innerHTML = (Number(pos.innerHTML)+1).toString();
}
for (var i=0; i<25; i++) {
setTimeout(changeOpacity(image, i), 1000);
}
}
</script>
</head>
<body>
<div class="main">
<div class="centered">
<img id="image" src="Images\1.jpg">
</div>
</div>
<div class="main">
<div class="centered">
<span id="number">1</span>
</div>
</div>
<div class="main">
<div class="centered">
<button onclick="goLeft()" style="margin-right:50px;">Go Left</button>
<button onclick="goRight()" style="margin-left:50px;">Go Right</button>
</div>
</div>
</body>
The problem is this block of code that is in your goLeft method, and goRight method:
for (var i=0; i<25; i++) {
setTimeout(changeOpacity(image, i), 1000);
}
You are creating 25 timers that, and each timer will execute approximately 1 second later.
Creating animations is best left to the CSS.
In your CSS add:
#image {
transition: opacity 0.5s ease;
}
And then in your JavaScript, simply: image.style.opacity = 1.0;
When the opacity changes, CSS will automatically transition the opacity length at the speed defined in the css, e.g 0.5s. Feel free to experiment.
I also added a jsfiddle here: http://jsfiddle.net/dya7L8wq/
You misunderstood setTimeout and the for loop.
Norman's answer provides a good solution with CSS, but he doesn't talk too much about why your code is not working. So I'd like to explain.
for (var i=0; i<25; i++) {
setTimeout(changeOpacity(image, i), 1000);
}
You assumption is:
invoke changeOpacity(image, 0) after 1 second
invoke changeOpacity(image, 1) 1 second after step 1
invoke changeOpacity(image, 2) 1 second after step 2
invoke changeOpacity(image, 3) 1 second after step 3
....
And the last step is invoking changeOpacity(image, 24) 1 second after previous step.
What actually happens is:
The loop is finished almost immediately!
In each iteration, setTimeout queues an asynchronous function invocation, and it's done! That says, it will return right away, rather than wait until changeOpacity returns.
And then, after about 1 second, changeOpacity fires 25 times almost at the same time, because you queued it 25 times in the for loop.
Another problem here is: in changeOpacity invocations, passed-in parameter i are not 1, 2, 3...., they all have the same value that causes for loop to exit (1 second ago) - 25, because JS doesn't have a block scope prior to ES6 (in ES6 we have keyword let for it).
In a pure JS solution, to ensure the time sequence we'd usually queue next invocation at the end of every step:
function changeOpacity() {
// do something here
// before the function returns, set up a future invocation
setTimeout(changeOpacity, 1000)
}
Here's an example to print a list of numbers from 1 to 5:
var go = document.getElementById('go')
var op = document.getElementById('output')
var i = 0
function printNum() {
var p = document.createElement('p')
p.innerHTML = ++i
op.appendChild(p)
// next step
if(i < 5) {
setTimeout(printNum, 500)
}
}
go.onclick = printNum
<button id="go">GO</button>
<div id="output"></div>
Why use pure JavaScript?
Use jQuery.
It has a pretty neat fadeTo() function and a useful fadeIn() function.
Might wanna use that ;)

Shading a block that my mouse is over

I am writing a code that creates a memory game. I need to make it so that when my mouse is over a block it shades that block to a different color. I am writing the code in javascript. Whenever I try something is just says string is not a function. I am writing in khan academy and the code is mouseOver="this.color='black'"; Can anyone please help me?
draw = function() {
if (delayStartFC && (frameCount - delayStartFC) > 30) {
for (var i = 0; i < tiles.length; i++) {
if (!tiles[i].isMatch) {
tiles[i].drawFaceDown();
}
}
flippedTiles = [];
delayStartFC = null;
// commented no loop because the timer stops working if this is enabled.
mouseOver="this.color='black';"
// noLoop();
}
You need to use this.style.backround='color' instead of this.color='color'. Try the below code:
<div style="background:red;" onmouseover="this.style.background='blue';" onmouseout="this.style.background='red'">Content</div>
When you put the code inline, it can get messy, so extracting it would be helpful like below:
The "selector" is whatever div or class or id you want to turn black on the mouse hover action.
$('selector').hover(function() {
$(this).css('background-color', 'black');
});
One solution could be:
<div onmouseover="Color(this,'black')"></div>
function Color(obj, color){
obj.style.background=color;
}
hope it's useful!
I'm assuming that you are looking at the "Project:Memory++" page, so I'll work from there.
If you look at the function above draw, it's called mouseClicked, you'll notice that it has a way of checking if a card is under the mouse:
mouseClicked = function() {
for (var i = 0; i < tiles.length; i++) {
if (tiles[i].isUnderMouse(mouseX, mouseY)) { //this line designates a tile under the mouse
// draw the tile, tiles[i], with a color over it.
After you've found which tile to color, you'll need to write a function to change it, either by changing the image that the tile shows or by giving it an overlay. There are a few good ideas in the other threads, but I would stick with what you've been given during your other projects on Khan Academy.
I usually put events like mouseover in event listeners, rather than in the HTML mouseover attribute. If you're looking to do it in pure JavaScript, try this:
document.getElementById("test").addEventListener("mouseover",function(e){e.target.style.backgroundColor = "#000";});
document.getElementById("test").addEventListener("mouseout",function(e){e.target.style.backgroundColor = "#FFF";});
You can also just accomplish this with CSS:
#test {
background-color: #FFF;
}
#test:hover {
background-color: #000;
}

How do you make a sound file play in multiple overlapping instances?

I am trying to get a sound file to play faster in order to keep up with the text reveal in the following code. The sound works (in Firefox, anyway), but it only seems to play one instance of the file at a time.
I'd like to have each letter pop onto the screen accompanied by the popping sound. Right now the popping sound is sort of random, and not timed to play with each letter.
I'm wondering if I need to have multiple instances of the sound object, and how to do that.
I already shortened the sound file as much as I could, and the length of the file is shorter than the setTimeout interval I'm using. It just won't overlap multiple copies of the same sound file, for some very good reason that I don't know, I'm sure.
Here is the whole code:
(I tried to JSFiddle it, but couldn't get that to work (I'll save that question for a later date))
<html>
<head>
<style>
#display {
color: white;
font-size: 150%;
padding: 2em 5em;
min-height: 600px;
max-width: 600px;
margin-left: auto;
margin-right: auto;
}
body {
background-color: black;
padding: 0;
margin:0;
}
</style>
<script>
var text = "Test String... 1, 2, 3; Everything seems to be working, but the sound is lagging.";
var charDelay = 40; // Sets the delay time for the character loop
function loadText() {
var i = 0; // Character counter
var myPud = new Audio("http://southernsolutions.us/audio/pud03.ogg");
var displayBox = document.getElementById("display");
displayBox.innerHTML = "<p>";
textLoop();
function textLoop() {
if (i == text.length){ // This condition terminates the loop
displayBox.innerHTML += "</p>";
return;
} else if (i < text.length) { // This condition appends the next character
displayBox.innerHTML += text[i];
i++;
myPud.play();
setTimeout(function(){return textLoop()}, charDelay);
}
}
}
window.onload = function(){loadText()};
</script>
</head>
<body>
<div id="display"></div>
</body>
</html>
I'd suggest that you make the sound loop a little longer, say 1 second. Then, control the sound playing through event listeners so that once the text has finished, you stop the sound playing.
Trying to do it as you're doing it now, you could speed up the sound with how its playing in the audio file. This should give better results. That, or slow down the time out.
Below is some code that I've tested which would let you do it through event listeners. The results are similar to what you had, but if you took your audio file, increased it to 1 second and changed it up to have 24 clicks in there, you'd get the exact effect you were looking for.
Edit: I've also updated the below to take into account the comments.
<script>
var text = "Test String... 1, 2, 3; Everything seems to be working, but the sound is lagging.";
var charDelay = 40; // Sets the delay time for the character loop
function loadText() {
var i = 0; // Character counter
var myPud = new Audio("http://southernsolutions.us/audio/pud03.ogg");
var displayBox = document.getElementById("display");
// Toggle for whether to loop
var stillPlay = true;
displayBox.innerHTML = "<p>";
// Listen for when it ends
myPud.addEventListener("ended", onAudioComplete);
// Begin playing
myPud.play();
// Start the loop
textLoop();
function textLoop() {
if (i == text.length){ // This condition terminates the loop
displayBox.innerHTML += "</p>";
// If we're at the end, we want to stop playing
stillPlay = false;
// Rather than duplicate code, jump straight into the complete function
onAudioComplete(null);
return;
} else if (i < text.length) { // This condition appends the next character
displayBox.innerHTML += text[i];
i++;
// Direct reference to the function to avoid more anony. functions
setTimeout(textLoop, charDelay);
}
}
// On audio complete
function onAudioComplete(e){
// Can we still play? If so, play
if(stillPlay){
myPud.play();
} else {
// Otherwise, remove the event listener, stop and null out.
myPud.removeEventListener("ended", onAudioComplete);
myPud.stop();
myPud = null;
}
}
}
window.onload = loadText;
</script>

Fade in boxes 1 after the other with jQuery

Im trying to make a 'blanket' of divs containing child divs 150px high and 150px wide.
I want each child div to fade in 1 after the other after after a millisecond or so, opacity changing from 0, to 1.
I cant seem to figure out how this works, or how id do it though?
http://jsfiddle.net/CCawh/
JS
$(function(){
var figure = [];
w = 1500;
h = 450;
for(i = 0, i < 30, i++){
$('div').append(figure[].clone()).fadeIn();
}
});
Here is a working solution.
The problems in your code
in for(i = 0, i < 30, i++), you should use ';', not ',' . Use developer tools in your browser to catch such typos
In your code $('div').append(figure[].clone()).fadeIn(); , The fadeIn applies to $('div') as append() returns the calling object itself. You must replace it with $('<figure></figure>').appendTo('div').fadeIn('slow'); and to fadeIn items one by one you could set a timeout with incrementing delays
Add display: none; style to the figure to keep it hidden initially
Here is the full code.
$(function(){
for(i = 0; i < 30; i++){
setTimeout(function(){$('<figure></figure>').appendTo('div').fadeIn('slow');}, i*200);
}
});
Here is a fiddle to see it working http://jsfiddle.net/CCawh/12/
Try using greensock TweenLite http://www.greensock.com/get-started-js/.
It has staggerTo/staggerFrom action that does exactly what you are asking. TweenLite in conjunction with jQuery makes animation very easy.
This would be a possible solution (DEMO).
Use an immediate function and call it again n times in the fadeIn callback.
$(function(){
var figure = $('figure');
var counter = 0;
(function nextFade() {
counter++;
figure.clone().appendTo('div').hide().fadeIn(500, function() {
if(counter < 30) nextFade();
});
})();
});
You can use the following implementation as an example. Using setTimeout() will do the trick.
I've updated your jsfiddle here: http://jsfiddle.net/CCawh/5/
HTML:
<div class="container">
<div class="box"></div>
</div>
CSS:
.box {
display: none;
float: left;
margin: 10px;
width: 150px;
height: 150px;
background-color: #000;
}
JS:
$(function() {
var box = $('.box');
var delay = 100;
for (i = 0; i < 30; i++) {
setTimeout(function() {
var new_box = box.clone();
$('.container').append(new_box);
new_box.fadeIn();
}, delay);
delay += 500; // Delay the next box by an extra 500ms
}
});
Note that in order for the element to actually fade in, it must be hidden in the first place, i.e. display: none; or .hide()
Here's perhaps a more robust solution without counters:
http://jsfiddle.net/CCawh/6/
for(var i = 0; i < 30; i++){
$('div').append($('<figure>figure</figure>'));
}
(function fade(figure, duration) {
if (figure)
figure.fadeIn(duration, function() { fade(figure.next(), duration); });
})($('figure').first(), 400);
By the way, clauses in for loops are separated using semicolons, not commas.

A JS fade effect doesn't work (for loop)

I tried doing a JS fade effect with the setTimeout() function and it appeared to work to some extent, but I can't seem to figure out what's wrong with the code below:
<html>
<head>
<title></title>
<script type="text/javascript">
function FadeEffect(n)
{
var i=1;
fade = document.getElementById("box");
if (n===1)
{
fade.style.opacity=i/10;
i++;
setTimeout("FadeEffect(1)",50);
if (fade.style.opacity=1)
{
var i=1;
}
}
else if (n===0)
{
fade.style.opacity=1-i/10;
i++;
setTimeout("FadeEffect(0)",50);
if (fade.style.opacity=0)
{
var i=1;
}
}
}
</script>
<style type="text/css">
#box{
width: 200px;
height: 50px;
border: 1px solid black;
background-color: #ccc;
opacity: 0;
}
</style>
</head>
<body>
<div onMouseOver="FadeEffect(1)" onMouseOut="FadeEffect(0)" id="box">Menu</div>
</body>
</html>
Edit: updated the code with setTimeout() functions.
There are two problems with the function that I can see.
First, your if statements both do an assignment rather than a comparison. You are saying if (n=0) (one =, assignment) when you should be saying if (n===0) (three ===, comparison, or you can use two == for a type-converting comparison).
Second, using a for loop to repeatedly change the style isn't going to fade because the browser doesn't update the display at the same time as your code is executing - essentially it uses the same thread for display and for JavaScript. So the page will be updated after the function exits. You need to give the browser a chance to update after each iteration by using setTimeout() - something like:
function fadeEffect(element,startValue,target,delay){
element.style.opacity = startValue;
if (startValue < target)
startValue = Math.min(startValue + 0.1, target);
else
startValue = Math.max(startValue - 0.1, target);
if (startValue != target)
setTimeout(function(){fadeEffect(element,startValue,target,delay);}, delay);
}
<div onMouseOver="fadeEffect(this, 1, 0, 100);"
onMouseOut="fadeEffect(this, 0, 1, 100);" id="box">Menu</div>
Demo: http://jsfiddle.net/hLQ6y/2/
EDIT: Note that this code doesn't cope all that brilliantly if you move the mouse in and out too quickly, i.e., if you trigger the fade in before the fade out has finished. (You can see what I mean in my jsfiddle.) You can solve this by saving the return from .setTimeout() and calling .clearTimeout() if required. Given that I've already covered the essence of the question I'll leave the fine-tuning as an exercise for the reader...
UPDATE: Your updated code has introduced new if statements with the same assignment-instead-of-comparison problem. Also it is calling setTimeout() forever - you should do that conditionally as in the answers Pointy and I gave. Also you seem to be relying on the variable i which, as a local variable, will not retain its value between calls - you could make it a global, but better to manage it as a parameter like I did or as a local variable in an outer function like Pointy did.
The problem is that all those changes to the style will happen before the browser bothers to update the display. What you need to do is space out the changes over a much, much longer period of time using "setTimeout()".
function fadeIn() {
function increment() {
box.style.opacity = Math.min(1.0, (opacity += 0.1));
if (opacity < 1.0) {
setTimeout(increment, 100);
}
}
var box = document.getElementById('box'), opacity = 0;
box.style.opacity = opacity;
setTimeout(increment, 100);
}
edit — Here is a jsfiddle to demonstrate.
If you want it to fade, you need to create a function that sets the opacity -0.1 or +0.1 and then calls itself with setTimeout in 100ms (or fewer). If you don't let the script wait, it will be too fast and immediately set the opacity to 1.0.
Anyways, you could do this a lot easier using jQuery.

Categories