Why won't my HTML Images fade - javascript

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 ;)

Related

I'm having a difficult time understanding sliders(carousel)

today I decided to learn how to make sliders (carousel) , I must point out that I am pretty much new to JavaScript.
First I tried to think how I should code that myself, I had no inspiration or ideas whatsoever. (Just pointless ideas) , so I went to watch youtube in hope of "enlightment" and there were solutions, but a bit too advanced for me to understand.(and long)
After that I googled "how to make sliders" and I found something simpler on w3schools. At first, I was a bit confused, but after a while I started to understand it bit by bit, of course, not totally.
So here comes the question, can someone explain me what each line does and how it affects the others? Or if there is a better and easier method, I would love to hear it.
Here is the javascript file(followed by CSS and HTML), I only modified a few variable names to understand them better and replaced var with let or const:
let index = 1;
showDivs(index);
function plusSlide(value) {
showDivs(index += value);
}
function showDivs(value) {
let i;
let slider = document.getElementsByClassName("slides");
if (value > slider.length) {
index = 1;
}
if (value < 1) {
index = slider.length;
}
for (i = 0; i < slider.length; i++) {
slider[i].style.display = "none";
}
slider[index - 1].style.display = "block";
}
.sliders {
display: flex;
width: 400px;
height: 200px;
}
input[type="button"] {
width: 100px;
}
<!DOCTYPE html>
<html>
<head>
<title>Test</title>
<link rel="stylesheet" href="css.css" type="text/css">
</head>
<body>
<input type="button" value="Back" onclick="plusSlide(-1)">
<input type="button" value="Forward" onclick="plusSlide(+1)">
<div class="sliders">
<img src="https://via.placeholder.com/150/0000FF/FFFFFF/?text=image1" width="400" height="200" class="slides">
<img src="https://via.placeholder.com/150/FF00FF/FFFFFF/?text=image2" width="400" height="200" class="slides">
<img src="https://via.placeholder.com/150/00FFFF/FFFFFF/?text=image3" width="400" height="200" class="slides">
</div>
<script src="scripts.js"></script>
</body>
</html>
This specific approach works like this:
(Explaining in comments)
let index = 1; // Initializes index variable to point to the first element
// of your slide array we'll see later
showDivs(index);
function plusSlide(value) {
showDivs(index += value); // This function just increments your index value
// and displays the next slide
}
function showDivs(value) {
let i;
/*
You get the array of slides from the dom using the class name slides
*/
let slider = document.getElementsByClassName("slides");
if (value > slider.length) { // in case we completed a full circle, we go
//from the start again
index = 1;
}
if (value < 1) { // in case we try to go left beyond number 1, we display
// the last one ( to achieve the circular ux experience )
index = slider.length;
}
for (i = 0; i < slider.length; i++) {
slider[i].style.display = "none"; // Hides every slider
}
slider[index - 1].style.display = "block"; // Shows only our index slider
}
I don't think this is the best approach. Because every time you want to change slide, every slide from the DOM element is retrieved, and its style is changed. You change the display state of every slide in every click. In my opinion you can
use the document.getElementsByClassName("slides"); only one time outside
of the function, in a greater scope and thus make your changes.
Also I wouldn't try to iterate through every slide and hide it. I would just
nitialize every slide to have their display equal to "none" and in every showDiv
I would just hide my current index ( before the incremention) and just show next one. Like this:
const slider = document.getElementsByClassName("slides");
function showDivs(value) {
slider[index-1].style.display = "none"; // hides our current slider before
// the incremention
if (value > slider.length) {
index = 1;
}
if (value < 1) {
index = slider.length;
}
slider[index - 1].style.display = "block"; // Shows only our index slider
}
.sliders {
display: none;
width: 400px;
height: 200px;
}

How do I delay an execution in Javascript?

i swear i've being looking for the way to solve this many hours before I ask here, I found some codes but still nothing fits to what I need... the thing is that I have some boxes and 1 button for rolling a random style... when I click the button the following code starts;
function roll(){
for (i = 0; i < 4; i++) {
setInterval(function(){
res = Math.floor(Math.random() * 10) + 1;
/*document.getElementById("slot-" + res).style.border = "5px solid red";
document.getElementById("slot-" + res).style.color = "red";*/
document.getElementById("slot-" + res).className = "col-sm-2 slot-active";
}, 500);
document.getElementById("slot-" + res).className = "col-sm-2 slot";
}
};
It selects at random between 1 and 10 and changes style class to that box... so... in the line after the loop, i pretend to put original stile back so it gives a look of a random selection... but it doesnt executes, if I put it inside the for loop it executes inmediatly and seems like nothing is happening... im Javascript noob and i'm trying this to learn and practice!... thanks in advance for your help!...
You can create a setInterval that will randomly set the selected class, and have a setTimeout to end the execution of said interval, something like this: https://jsfiddle.net/canastro/xxdbjm4n/1/
const refreshIntervalId = setInterval(() => {
$('.selected').removeClass('selected');
const res = Math.floor(Math.random() * 10) + 1;
$(`.slot-${res}`).addClass('selected');
}, 500)
setTimeout(() => {
clearInterval(refreshIntervalId);
}, 3000);
You could use setTimeout(function(){dosomething}, timeout), btw. add console.log('something') to see if the functions are actually executed.
btw. if you're using interval remember you might need to cancel it if you dont want it running forver, you could as well use recurring function (a function calling itself) with some condition on when to do processing or now.
btw2. every time you create interval assign it to some variable or something so you can cancel it!
your answer should be like this.you need to see javascript variable scope.
function roll(){
for (i = 0; i < 4; i++) {
var res = Math.floor(Math.random() * 10) + 1;
function active(res){
setTimeout(function(){
document.getElementById("slot-" + res).className = "col-sm-2 slot-active";
}, 500);
document.getElementById("slot-" + res).className = "col-sm-2 slot";
}
active(res);
}
};
div{
width:50px;
height:50px;
background:#ccc;
float:left;
margin:5px;
}
.slot{
background:#0f0;
}
.slot-active{
background:#f00;
}
<button onclick="roll()" style="display:block;">roll</button>
<div id="slot-1"></div>
<div id="slot-2"></div>
<div id="slot-3"></div>
<div id="slot-4"></div>
<div id="slot-5"></div>
<div id="slot-6"></div>
<div id="slot-7"></div>
<div id="slot-8"></div>
<div id="slot-9"></div>
<div id="slot-10"></div>

setTimeout, JavaScript, no effect

I need to animate something, but my statement has no effect. My code:
var movei=function(img){
img.setAttribute("src", "blank.png");
}
var comp=function() {
...
for(var k=0; k<i; k++) {
var img=document.getElementById(id(k,col));
img.setAttribute("src", "circ1.png");
timer=setTimeout(movei(img),1000);
...
}
}
I read another questions about setTimeout (setInterval) but I couldn`t find an answer.
try timer=setTimeout(function(){movei(img);},1000);
If you want to animate individual elements, you should use recursion instead of loops.
Following code depicts the same:
var count = 0;
function getId(count) {
return "div_" + count;
}
function initTimer() {
setTimeout(function() {
count++;
var _id = getId(count);
var _el = document.getElementById(_id);
show(_el);
if (count < 7)
initTimer();
}, 1000);
}
function show(el) {
el.style.display = "block";
}
initTimer();
div {
display: none;
}
<div id="div_1">1</div>
<div id="div_2">2</div>
<div id="div_3">3</div>
<div id="div_4">4</div>
<div id="div_5">5</div>
<div id="div_6">6</div>
As #deceze has also written, you need to change the call to setTimeout so that you do not call the movei function, but rather pass the function 'name'. Then you would of cause have to change the behavior the function movei so it either gets a simple string name of the image or can lookup the image itself.
timer=setTimeout(movei,1000);
or (in the not recommented way)
timer=setTimeout("movei(\"imgName\")",1000);

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.

Categories