Reputation: 141
I just started learning JavaScript and right now, I'm making a virtual traffic light that lights up red, green and orange. I would like to make a loop by adding a setInterval
to the outside. Is this possible or should i use some other method of making a loop. I tried making a a for(;;){}
but this causes an error and the webpage never loads. Here is my current code.
var red = document.getElementById("circleRed");
var orange = document.getElementById('circleOrange')
var green = document.getElementById('circleGreens');
setInterval(
setTimeout( function(){
red.style.backgroundColor = "red";
}, 2000),
setTimeout(function(){
green.style.backgroundColor = "green";
red.style.backgroundColor = "black";
}, 5000),
setTimeout(function(){
orange.style.backgroundColor = "orange";
green.style.backgroundColor = "black";
}, 10000),
5000);
#circleRed, #circleGreens, #circleOrange {
width: 50px;
height: 50px;
-webkit-border-radius: 25px;
-moz-border-radius: 25px;
border-radius: 25px;
margin-bottom: 10px;
background-color: "black";
}
.back {
width: 60px;
margin: 10px 0px 10px 20px;
padding-left: 10px;
padding-top: 10px;
padding-bottom: 10px;
background-color: black;
}
body{
margin: 0;
}
<div class="back">
<div id="circleRed">
</div>
<div id="circleOrange">
</div>
<div id="circleGreens">
</div>
</div>
Upvotes: 1
Views: 98
Reputation: 3401
Here's an alternative implementation with equal times for every light.
var red = document.getElementById('circleRed');
var orange = document.getElementById('circleOrange');
var green = document.getElementById('circleGreens');
/* Set an array with the desired order to turn on lights */
var lights = [red, green, orange];
function open(light) {
light.classList.add('opened');
}
function close(light) {
light.classList.remove('opened');
}
function change() {
close(lights[i]);
i = (i + 1) % lights.length;
open(lights[i]);
}
/* Start */
var i = 0;
open(lights[i]);
setInterval(change, 1000);
.circle {
width: 50px;
height: 50px;
border-radius: 25px;
margin: 5px;
opacity: 0.2;
transition: opacity 200ms;
}
.circle.opened {
opacity: 1;
}
#circleRed {
background-color: red;
}
#circleOrange {
background-color: orange;
}
#circleGreens {
background-color: green;
}
.back {
width: 60px;
padding: 5px;
background-color: black;
}
<div class="back">
<div id="circleRed" class="circle"></div>
<div id="circleOrange" class="circle"></div>
<div id="circleGreens" class="circle"></div>
</div>
Explanation:
Instead of changing the background color of every circle from black to its own color to light up the circle or viceversa to switch off, in my example all circles have their respective color (red, green or orange) faded to (almost) transparent with opacity: 0.2
(originally I used 0, but I think it looks better with 0.2) See: opacity.
So, all elements with class .circle
have:
.circle {
/* Other properties */
opacity: 0.2;
}
Then, I use a class called opened
to turn the opacity to 1 making the circle visible.
.circle.opened {
opacity: 1;
}
Since .circle.opened
has higher specificity than just .circle
, opacity: 1 prevails on those elements having both classes (circle
and opened
).
To add or remove the class opened
from a light item I use two simple functions open
and close
that manipulate the element's classList. This is important. In general it's more recommended to define element's properties (styles) in classes and use JS to add or remove this classes to alter the element that to modify element's styles directly with JS.
So, it's cleaner and more recommended to do:
/* CSS */
.red { background-color: red }
/* Javascript */
var element = document.getElementById('#element_ID');
element.classList.add('red');
than:
/* Javascript */
var element = document.getElementById('#element_ID');
element.style.backgroundColor = 'red';
Even though it may seem easier this second way.
To change the lights, I made an array with the elements in the desired order:
var lights = [red, green, orange];
As you can see, every element of the lights Array is one of the circles, we already stored in variables with document.getElementById()
(if you're not familiar with arrays, dedicate some time to read and understand what they are and how they work. They're one of the most basic data structures in any programming language, so it's important to master them.)
To start, I initiate a global variable to 0
(var i = 0
) and I light up the first light with:
open(lights[i]);
Since i
equals 0, lights[i]
, so lights[0]
is red
(In JS, as in most languages, arrays start counting their elements from 0). This way, open(lights[i])
is the same as open(red)
.
Then I do a setInterval(change, 1000)
so every second the function change()
is called. And what does this change
function do?
Basically:
// Turn off the current light
close(lights[i]);
// Increment i, so that lights[i] points to the next element...
i = (i + 1) % lights.length;
// Turn on this next element
open(lights[i]);
The rarest thing here may be the increment. Why do I do i = (i + 1) % lights.length
instead of just i++
.
If I do i++
after successive calls to change
, i
will be: 0, 1, 2, 3, 4, 5, 6... so, when I try to access lights[i]
I'll get an error, because there is no element in positions 3, 4, 5... of the lights
array.
I need my sequence to be: 0, 1, 2, 0, 1, 2, 0, 1, 2...
How do I get this desired sequence instead of 0, 1, 2, 3, 4, 5, 6... ?
Maybe a more understandable way could be:
i++;
if (i > 2) {
i = 0;
}
But I'm using the Remainder operator (%
) to achieve the same effect.
I hope this helps!
And another one with easily configurable duration for every light:
var lights = {
red: {
node: document.getElementById('circleRed'),
duration: 4000,
},
green: {
node: document.getElementById('circleGreens'),
duration: 2000,
},
orange: {
node: document.getElementById('circleOrange'),
duration: 800,
}
};
var order = ['red', 'green', 'orange'];
function open(light) {
light.node.classList.add('opened');
}
function close(light) {
light.node.classList.remove('opened');
}
function change() {
close(lights[order[i]]);
i = (i + 1) % order.length;
open(lights[order[i]]);
setTimeout(change, lights[order[i]].duration);
}
/* Start */
var i = 0;
open(lights[order[i]]);
setTimeout(change, lights[order[i]].duration);
.circle {
width: 50px;
height: 50px;
border-radius: 25px;
margin: 5px;
opacity: 0;
transition: opacity 200ms;
}
.circle.opened {
opacity: 1;
}
#circleRed {
background-color: red;
}
#circleOrange {
background-color: orange;
}
#circleGreens {
background-color: green;
}
.back {
width: 60px;
padding: 5px;
background-color: black;
}
<div class="back">
<div id="circleRed" class="circle"></div>
<div id="circleOrange" class="circle"></div>
<div id="circleGreens" class="circle"></div>
</div>
Upvotes: 1
Reputation: 5574
Think to the traffic lights as an object with 3 states, redOn, greenOn and OrangeOn. You need to loop through states, so starting from redOn pass the next one in the sequence and reset in the last one. I think setInterval here is not required as it cause you to care about the total time that's irrelevant.
var red = document.getElementById("circleRed");
var orange = document.getElementById('circleOrange')
var green = document.getElementById('circleGreens');
var redFor = 200 //2000
var greenFor = 500 //5000
var orangeFor = 1000 //10000
let redOn = function(next) {
red.style.backgroundColor = "red";
orange.style.backgroundColor = "black";
setTimeout(next, redFor);
}
let orangeOn = function(next) {
orange.style.backgroundColor = "orange";
green.style.backgroundColor = "black";
setTimeout(next, orangeFor);
}
let greenOn = function(next) {
green.style.backgroundColor = "green";
red.style.backgroundColor = "black";
setTimeout(next, greenFor);
}
let start = function() {
redOn(function() {
greenOn(function() {
orangeOn(start)
})
})
}
start()
#circleRed,
#circleGreens,
#circleOrange {
width: 50px;
height: 50px;
-webkit-border-radius: 25px;
-moz-border-radius: 25px;
border-radius: 25px;
margin-bottom: 10px;
background-color: "black";
}
.back {
width: 60px;
margin: 10px 0px 10px 20px;
padding-left: 10px;
padding-top: 10px;
padding-bottom: 10px;
background-color: black;
}
body {
margin: 0;
}
<html>
<head>
<link rel="stylesheet" type="text/css" href="object2.css">
<meta charset="utf-8">
<title></title>
</head>
<body>
<div class="back">
<div id="circleRed"></div>
<div id="circleOrange"></div>
<div id="circleGreens"></div>
</div>
<script src="objects1.js"></script>
</body>
</html>
Upvotes: 1
Reputation: 3163
put all setTimeout( function(){})
in one function, then it will work
Note: to make
setInterval
work properly, the milliseconds must be at least the total ofsetTimeout
functions.
also you forgot to set the orange
to black when the red
is appearing.
var red = document.getElementById("circleRed");
var orange = document.getElementById('circleOrange')
var green = document.getElementById('circleGreens');
setInterval(function(){ myTimer() }, 17000);
function myTimer() {
setTimeout( function(){
red.style.backgroundColor = "red";
orange.style.backgroundColor = "black";
}, 2000),
setTimeout(function(){
green.style.backgroundColor = "green";
red.style.backgroundColor = "black";
}, 5000),
setTimeout(function(){
orange.style.backgroundColor = "orange";
green.style.backgroundColor = "black";
}, 10000)
}
myTimer();
#circleRed, #circleGreens, #circleOrange {
width: 50px;
height: 50px;
-webkit-border-radius: 25px;
-moz-border-radius: 25px;
border-radius: 25px;
margin-bottom: 10px;
background-color: "black";
}
.back {
width: 60px;
margin: 10px 0px 10px 20px;
padding-left: 10px;
padding-top: 10px;
padding-bottom: 10px;
background-color: black;
}
body{
margin: 0;
}
<div class="back">
<div id="circleRed">
</div>
<div id="circleOrange">
</div>
<div id="circleGreens">
</div>
</div>
Upvotes: 0
Reputation: 631
You can cal your all setTimeout
function in a loop
function. And call this loop function with setInterval
.
Note : I also changed some of the color changing sections in your code .
jsfiddle link : https://jsfiddle.net/zgdx5xan/
var red = document.getElementById("circleRed");
var orange = document.getElementById('circleOrange')
var green = document.getElementById('circleGreens');
loop();
setInterval(loop,11000);
function loop(){
console.log("loop started")
setTimeout( function(){
red.style.backgroundColor = "red";
orange.style.backgroundColor = "black";
green.style.backgroundColor = "black";
console.log("red opened")
}, 2000);
setTimeout(function(){
green.style.backgroundColor = "green";
red.style.backgroundColor = "black";
console.log("green opened")
}, 5000);
setTimeout(function(){
orange.style.backgroundColor = "orange";
green.style.backgroundColor = "black";
red.style.backgroundColor = "black";
console.log("orange opened")
}, 10000);
}
#circleRed, #circleGreens, #circleOrange {
width: 50px;
height: 50px;
-webkit-border-radius: 25px;
-moz-border-radius: 25px;
border-radius: 25px;
margin-bottom: 10px;
background-color: "black";
}
.back{
width: 60px;
margin: 10px 0px 10px 20px;
padding-left: 10px;
padding-top: 10px;
padding-bottom: 10px;
background-color: black;
}
body{
margin: 0;
}
<div class="back">
<div id="circleRed">
</div>
<div id="circleOrange">
</div>
<div id="circleGreens">
</div>
</div>
Upvotes: 1
Reputation: 3688
setInterval, like setTimeout also requires a function to be passed as a first argument, in that function you would then be able to compose your setTimeout's.
var red = document.getElementById("circleRed");
var orange = document.getElementById('circleOrange');
var green = document.getElementById('circleGreens');
setInterval(function () {
red.style.backgroundColor = "black";
orange.style.backgroundColor = "black";
green.style.backgroundColor = "black";
setTimeout(function () {
red.style.backgroundColor = "red";
}, 2000);
setTimeout(function () {
green.style.backgroundColor = "green";
red.style.backgroundColor = "black";
}, 5000);
setTimeout(function () {
orange.style.backgroundColor = "orange";
green.style.backgroundColor = "black";
}, 8000);
}, 10000)
I have adjusted your timings a little as your final timeout was longer than the interval. You can see this working here: codepen example
Upvotes: 1