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>
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>
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
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>
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>
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 of setTimeout 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>
Related
I'm creating a tab menu like this:
function clear_selected() //sets all columns color black
{
var parent = document.querySelector("#container")
var items = document.querySelectorAll(".item")
var n = items.length;
for (var i = 0; i < n; i++)
items[i].style.backgroundColor = "";
}
function plus(itself) //adds another column
{
var parent = itself.parentElement;
var n = parent.childElementCount;
clear_selected();
var n = parent.querySelectorAll(".item").length;
var page = document.createElement("button");
page.className = "item";
page.style.backgroundColor = "blue";
page.textContent = "column"
page.onclick = function() {
clear_selected();
this.style.backgroundColor = "blue";
};
var temp = document.createElement("span");
temp.className = "del"
temp.innerHTML = "×"
temp.onclick = function() { //it's suppose to remove a column and color default as blue
document.querySelector("#main_item").style.backgroundColor = "blue" //THIS LINE ISN'T WORKING
this.parentElement.remove();
};
page.appendChild(temp);
parent.insertBefore(page, parent.childNodes[n]);
}
function see(el) {
clear_selected();
el.style.backgroundColor = "blue";
}
#container {
display: flex;
width: 100%;
height: 50px;
text-align: center;
background-color: yellow;
}
.item {
background-color: black;
color: white;
border: none;
outline: none;
cursor: pointer;
margin: 0.1rem;
padding: 0.1rem;
max-width: 100%;
}
.del {
background-color: red;
display: inline-block;
cursor: pointer;
border-radius: 50%;
width: 0.7rem;
margin-left: 2rem;
}
<div id="container">
<button class="item" id="main_item" style="background-color:blue;" onclick="see(this)">default column </button>
<button class="item" onclick="plus(this)">+</button>
</div>
but when I press the 'x' to remove a column, I want the default column to color blue, but the line of code which is suppose to achieve that isn't working
document.querySelector("#main_item").style.backgroundColor = "blue"
Before pressing 'x':
After pressing 'x' on the last column:
What it SHOULD look like:
I've losing sleep over this, can someone PLEASE tell me why isn't it working?
When you click on the "X", both of your onclick handlers are getting called, including the one that runs clear_selected, which sets the background color to "".
You can fix this by using stopPropagation on the event passed into the onclick function for the "x". That will stop the click event from going up the chain to the parent element of the "x".
temp.onclick = function(e) {
document.querySelector("#main_item").style.backgroundColor = "blue"
this.parentElement.remove();
e.stopPropagation();
};
I'm trying to make a count down so that every 20 sec an alert pops up. I want it to go from 20 to 0 to 20 over and over. I have it working, but it only works once. The rage function dosen't need to be changed, it's the msg function i'm having trouble with. Here's my code.
function rage() {
var i = document.getElementsByName("msg")[6];
var message = document.getElementById("message");
const affichemsg = document.querySelector("msg");
for (i = 0; i < 1; i++) {
message.innerHTML += "Es-tu près ";
}
}
setInterval(rage, 350);
var decompteur = setInterval(msg, 1000);
var temps;
function msg() {
var idTemps = document.getElementById("temps");
temps = parseInt(idTemps.innerHTML);
temps = temps - 1;
idTemps.innerHTML = temps;
if (--temps <= 0) {
alert("!!!!!ES-TU PRÈS!!!!!");
clearInterval(temps);
msg();
idTemps = 20;
var decompteur = setInterval(msg, 1000);
}
}
.titre2 {
width: 650px;
margin: auto;
text-align: center;
border-radius: 35px;
color: #000000;
background-color: #ff0000;
padding: 5px;
margin-bottom: 30px;
}
.bouton {
transition-duration: 0.4s;
border-width: 1px;
cursor: pointer;
}
.boutonOui {
background-color: rgb(234, 234, 234);
}
.boutonOui:hover {
background-color: rgb(177, 177, 177);
}
<div class="header">
<h1 class="titre2">Es-tu près</h1>
</div>
<button
class="bouton boutonOui"
id="boutonOui"
onclick="window.location.href='jeu_educatifs2.html'"
>
Oui
</button>
<div id="temps">20</div>
<div id="message"></div>
<div id="msg" value="6"></div>
the first problem here is that you don't use correctly the method clearInterval. It takes as argument the id of the "timer" created with setInterval. setInterval return directly the id so actually stored it in your variable decompteur and you should use something like that to clear the timer : clearInterval(decompteur).
Also be sure to reset the idTemps with idTemps.innerHTML = 20. Then I don't really understand... Why would you clear the interval then rebuild the same again when you can just set the idTemps.innerHTML so your interval will use the 20 for temps at the next iteration ?
PS: ça fait plaisir de voir un peu de français sur stack :)
You should rewrite your code this way :
function rage() {
var i = document.getElementsByName("msg")[6];
var message = document.getElementById("message");
const affichemsg = document.querySelector("msg");
for (i = 0; i < 1; i++) {
message.innerHTML += "Es-tu près ";
}
}
setInterval(rage, 350);
var decompteur = setInterval(msg, 1000);
function msg() {
var idTemps = document.getElementById("temps");
var temps = parseInt(idTemps.innerHTML) - 1;
idTemps.innerHTML = temps;
if (--temps == 0) {
alert("!!!!!ES-TU PRÈS!!!!!");
idTemps.innerHTML = "20";
}
}
// when you need to, stop decompteur with :
// clearInterval(decompteur);
.titre2 {
width: 650px;
margin: auto;
text-align: center;
border-radius: 35px;
color: #000000;
background-color: #ff0000;
padding: 5px;
margin-bottom: 30px;
}
.bouton {
transition-duration: 0.4s;
border-width: 1px;
cursor: pointer;
}
.boutonOui {
background-color: rgb(234, 234, 234);
}
.boutonOui:hover {
background-color: rgb(177, 177, 177);
}
<div class="header">
<h1 class="titre2">Es-tu près</h1>
</div>
<button class="bouton boutonOui" id="boutonOui" onclick="window.location.href='jeu_educatifs2.html'">
Oui
</button>
<div id="temps">20</div>
<div id="message"></div>
<div id="msg" value="6"></div>
This keep as much of your code as possible, but as Rojo said, your code is very inefficent and there are a lot of things to improve.
Currently, your code is very inefficient. Rather than having your variable temps read from the DOM, you should have the variable update itself:
var temps = 20;
function msg() {
--temps;
}
setInterval(msg, 1000);
Second, you shouldn't be including var inside of that if statement:
if (countdown === 0) {
alert("!!!!!ES-TU PRÈS!!!!!");
clearInterval(decompteur);
// msg(); // You don't need this
temps = 20;
decompteur = setInterval(msg, 1000); // I removed the var
}
Also, you had your variables mixed up (which I fixed)
function rage() {
var i = document.getElementsByName("msg")[6];
var message = document.getElementById("message");
const affichemsg = document.querySelector("msg");
for (i = 0; i < 1; i++) {
message.innerHTML += "Es-tu près ";
}
}
setInterval(rage, 350);
var decompteur = setInterval(msg, 1000);
var temps = 20;
var idTemps = document.getElementById("temps"); // I also moved this outside
function msg() {
--temps;
idTemps.innerHTML = temps;
if (temps === 0) {
alert("!!!!!ES-TU PRÈS!!!!!");
clearInterval(decompteur);
//msg(); //You don't need this
temps = 20;
decompteur = setInterval(msg, 1000);
}
}
.titre2 {
width: 650px;
margin: auto;
text-align: center;
border-radius: 35px;
color: #000000;
background-color: #ff0000;
padding: 5px;
margin-bottom: 30px;
}
.bouton {
transition-duration: 0.4s;
border-width: 1px;
cursor: pointer;
}
.boutonOui {
background-color: rgb(234, 234, 234);
}
.boutonOui:hover {
background-color: rgb(177, 177, 177);
}
<div class="header">
<h1 class="titre2">Es-tu près</h1>
</div>
<button
class="bouton boutonOui"
id="boutonOui"
onclick="window.location.href='jeu_educatifs2.html'"
>
Oui
</button>
<div id="temps">20</div>
<div id="message"></div>
<div id="msg" value="6"></div>
The following code prints the counter value each second and every 20 seconds it prints ALERT. Try to use a similar code for your example because your code is not very clear.
let counter = 0;
setInterval(() => {
counter++;
if (counter % 20 == 0) {
console.log("ALERT! ");
}
console.log("Counter Value: " + (counter % 20));
}, 1000);
i'm trying to develop a game using html, css and js. At the moment I'm focusing on manipulating DOM elements without using the canvas tag. My idea is to create a pseudo graphical programming language, similar to the Blockly environment. So far I have inserted 3 clickable elements inside #toolbox that create their copies in #workspace.
Now, I am trying to assign functions to the elements present in #workspace, which once pressed the Run button are executed in order of appearance, so as to create a queue of commands that is able to move the pink square inside #output_section.
Therefore I cannot understand how to write the function that is able to verify the presence of the elements and then be able to perform the different functions assigned to these elements.
Any ideas? :D
I'm using Jquery 3.3.1
function addRed() {
var redWorkspace = document.createElement("DIV");
redWorkspace.className = "remove-block block red";
document.getElementById("workspace").appendChild(redWorkspace);
};
function addBlue() {
var blueWorkspace = document.createElement("DIV");
blueWorkspace.className = "remove-block block blue";
document.getElementById("workspace").appendChild(blueWorkspace);
};
function addGreen() {
var greenWorkspace = document.createElement("DIV");
greenWorkspace.className = "remove-block block green";
document.getElementById("workspace").appendChild(greenWorkspace);
};
$("#clear_workspace").click(function () {
$("#workspace").empty();
});
$(document).on("click", ".remove-block", function () {
$(this).closest("div").remove();
});
html,
body {
margin: 0;
padding: 0;
}
#workspace {
display: flex;
height: 100px;
padding: 10px;
background: black;
}
#toolbox {
display: flex;
padding: 10px;
width: 300px;
}
#output_section {
height: 500px;
width: 500px;
border: solid black;
margin: 10px;
position: relative;
}
#moving_square {
position: absolute;
bottom: 0;
right: 0;
width: 100px;
height: 100px;
background: pink;
}
.block {
height: 100px;
width: 100px;
}
.red {
background: red;
}
.blue {
background: cyan;
}
.green {
background: green;
}
.grey {
background: #ccc;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<html>
<body>
<div id="workspace"></div>
<div id="workspace-menu">
<button id="run_workspace">Run</button>
<button id="clear_workspace">Clear</button>
</div>
<div id="toolbox" class="grey">
<div onclick="addRed()" class="block red">Left</div>
<div onclick="addBlue()" class="block blue">Up</div>
<div onclick="addGreen()" class="block green">Right</div>
</div>
<div id="output_section">
<div id="moving_square"></div>
</div>
</body>
</html>
Completely untested but run button does something along the lines of:
$("#run_workspace").click(function() {
$("#workspace .block").each(function(elem) {
if (elem.hasClass("red")) {
moveObjectLeft();
} else if (elem.hasClass("green")) {
moveObjectRight();
} else if (elem.hasClass("blue")) {
moveObjectUp();
}
});
});
Commonly, it's a good idea to store all required information in arrays and objects, and use HTML only to display your data.
Also, if you are already using jQuery - use it for all 100%)
Made some improvements:
let mobs = {
pinky: {
node: $('#moving_square'),
coors: { top: 400, left: 400 },
step: 30,
moveQueue: [],
// moveTimeout ???
},
}; // storing here all created objects, that must move.
/* Each [moveQueue] array will store the chain of moves, like ["up", "up", "left"]
You can take each "key-word" of move, and get required function buy that key,
from the 'move' object */
let move = { // Think about how to simlify this object and functions. It's possible!)
left: function (obj) {
let left = obj.coors.left = (obj.coors.left - obj.step);
obj.node.css('left', left + 'px');
},
up: function (obj) {
let top = obj.coors.top = (obj.coors.top - obj.step);
obj.node.css('top', top + 'px');
},
right: function (obj) {
let left = obj.coors.left = (obj.coors.left + obj.step);
obj.node.css('left', left + 'px');
}
};
let stepTimeout = 1000;
let running = false;
let timeouts = {}; // store all running timeouts here,
// and clear everything with for( key in obj ) loop, if required
$('#toolbox .block').on('click', function () {
let color = $(this).attr('data-color');
let workBlock = '<div class="remove-block block ' + color + '"></div>';
$('#workspace').append(workBlock);
mobs.pinky.moveQueue.push( $(this).text().toLowerCase() ); // .attr('data-direction');
// instead of pinky - any other currently selected object
// $(this).text().toLowerCase() — must be "left", "up", "right"
});
$('#run_workspace').on('click', function () {
running = true;
runCode();
function runCode() {
for (let obj in mobs) { // mobile objects may be multiple
// Inside the loop, obj == mobs each key name. Here it's == "pinky"
let i = 0;
let pinky = mobs[obj];
localRun();
function localRun() {
let direction = pinky.moveQueue[i]; // getting direction key by array index.
move[direction](pinky); // calling the required function from storage.
if (pinky.moveQueue[++i] && running ) {
// self-calling again, if moveQueue has next element.
// At the same time increasing i by +1 ( ++i )
timeouts[obj] = setTimeout(localRun, stepTimeout);
}
}
}
}
});
$("#clear_workspace").click(function () {
$("#workspace").empty();
});
$('#workspace').on("click", ".remove-block", function () {
$(this).closest("div").remove();
});
html,
body {
margin: 0;
padding: 0;
}
#workspace {
display: flex;
height: 100px;
padding: 10px;
background: black;
}
#toolbox {
display: flex;
padding: 10px;
width: 300px;
}
#output_section {
height: 500px;
width: 500px;
border: solid black;
margin: 10px;
position: relative;
}
#moving_square {
position: absolute;
top: 400px;
left: 400px;
width: 100px;
height: 100px;
background: pink;
}
.block {
height: 100px;
width: 100px;
}
.red {
background: red;
}
.blue {
background: cyan;
}
.green {
background: green;
}
.grey {
background: #ccc;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="workspace"></div>
<div id="workspace-menu">
<button id="run_workspace">Run</button>
<button id="clear_workspace">Clear</button>
</div>
<div id="toolbox" class="grey">
<div data-color="red" class="block red">Left</div>
<div data-color="blue" class="block blue">Up</div>
<div data-color="green" class="block green">Right</div>
</div>
<div id="output_section">
<div id="moving_square"></div>
</div>
But... jQuery was used only for clicks... Translation to JS:
let mobs = {
pinky: {
node: document.getElementById('moving_square'),
coors: { top: 400, left: 400 },
step: 30,
moveQueue: [],
},
};
let move = {
left: function (obj) {
let left = obj.coors.left = (obj.coors.left - obj.step);
obj.node.style.left = left + 'px';
},
up: function (obj) {
let top = obj.coors.top = (obj.coors.top - obj.step);
obj.node.style.top = top + 'px';
},
right: function (obj) {
let left = obj.coors.left = (obj.coors.left + obj.step);
obj.node.style.left = left + 'px';
}
};
let stepTimeout = 1000;
let running = false;
let timeouts = {};
let blocks = document.querySelectorAll('#toolbox .block');
let workSpace = document.getElementById('workspace');
blocks.forEach(function(block){
block.addEventListener('click', function(){
let color = this.dataset.color;
let workBlock = '<div class="remove-block block ' + color + '"></div>';
workSpace.insertAdjacentHTML('beforeend', workBlock);
mobs.pinky.moveQueue.push( this.textContent.toLowerCase() );
});
});
document.getElementById('run_workspace').addEventListener('click', function () {
running = true;
runCode();
function runCode() {
for (let obj in mobs) { // mobile objects may be multiple
// Inside the loop, obj == mobs each key name. Here it's == "pinky"
let i = 0;
let pinky = mobs[obj];
localRun();
function localRun() {
let direction = pinky.moveQueue[i]; // getting direction key by array index.
move[direction](pinky); // calling the required function from storage.
if (pinky.moveQueue[++i] && running ) {
// self-calling again, if moveQueue has next element.
// At the same time increasing i by +1 ( ++i )
timeouts[obj] = setTimeout(localRun, stepTimeout);
}
}
}
}
});
document.getElementById("clear_workspace").addEventListener('click', function () {
workSpace.textContent = "";
});
workSpace.addEventListener('click', function (e) {
if( e.target.classList.contains('remove-block') ){
e.target.remove();
}
});
html,
body {
margin: 0;
padding: 0;
}
#workspace {
display: flex;
height: 100px;
padding: 10px;
background: black;
}
#toolbox {
display: flex;
padding: 10px;
width: 300px;
}
#output_section {
height: 500px;
width: 500px;
border: solid black;
margin: 10px;
position: relative;
}
#moving_square {
position: absolute;
top: 400px;
left: 400px;
width: 100px;
height: 100px;
background: pink;
}
.block {
height: 100px;
width: 100px;
}
.red {
background: red;
}
.blue {
background: cyan;
}
.green {
background: green;
}
.grey {
background: #ccc;
}
<div id="workspace"></div>
<div id="workspace-menu">
<button id="run_workspace">Run</button>
<button id="clear_workspace">Clear</button>
</div>
<div id="toolbox" class="grey">
<div data-color="red" class="block red">Left</div>
<div data-color="blue" class="block blue">Up</div>
<div data-color="green" class="block green">Right</div>
</div>
<div id="output_section">
<div id="moving_square"></div>
</div>
<!DOCTYPE>
<html>
<body>
<img id="traffic" src="assets/red.gif">
<button type="button" onclick="ChangeLights()">Change Lights</button>
<script>
var lights = [
"assets/red.gif",
"assets/yellow.gif",
"assets/green.gif",
"assets/yellow.gif",
];
var index = 0;
function ChangeLights() {
setInterval(function () {ChangeLights();}, 1000);
index = index + 1;
if (index == lights.length) index = 0;
var image = document.getElementById('traffic');
image.src=lights[index];
}
</script>
</body>
</html>
Hi, I am trying to make an animation script using JavaScript so that a traffic light sequence changes from red - yellow - green - yellow on a timer once a button is pressed. I only want the sequence to loop once. However, when I implemented the setInterval function into the function, the lights only change from red - yellow - green - red.
Thank you for any help!
var lights = {
red: "https://upload.wikimedia.org/wikipedia/commons/thumb/4/45/Traffic_lights_red.svg/200px-Traffic_lights_red.svg.png",
yellow: "https://upload.wikimedia.org/wikipedia/commons/thumb/1/19/Traffic_lights_yellow.svg/200px-Traffic_lights_yellow.svg.png",
green: "https://upload.wikimedia.org/wikipedia/commons/thumb/b/b2/Traffic_lights_dark_green.svg/200px-Traffic_lights_dark_green.svg.png"
};
var sequence = ['red', 'yellow', 'green', 'yellow'];
function startChangeLights() {
for (var index = 0; index < sequence.length; index++) {
changeLight(index, sequence[index]);
}
function changeLight(index, color) {
setTimeout(function() {
var image = document.getElementById('traffic');
image.src = lights[color];
}, index * 1000);
}
}
<div>
<img height=100px id="traffic" src="https://upload.wikimedia.org/wikipedia/commons/thumb/4/45/Traffic_lights_red.svg/200px-Traffic_lights_red.svg.png">
</div>
<div>
<button type="button" onclick="startChangeLights()">Change Lights</button>
</div>
https://codepen.io/anon/pen/VbKQNj?editors=1011
If you are looking for one time sequence, you have to use "setTimeout" method in javascript and besides, define an inner function like the following:
var lights = [
"assets/red.gif",
"assets/yellow.gif",
"assets/green.gif",
"assets/yellow.gif",
];
var index = 0;
function ChangeLights() {
function innerChangeLight(){
index = index + 1;
if (index == lights.length) index = 0;
var image = document.getElementById('traffic');
image.src=lights[index];
}
innerChangeLight();
setTimeout(function () {
innerChangeLight();
}, 1000);
}
<!DOCTYPE>
<html>
<body>
<img id="traffic" src="assets/red.gif">
<button type="button" onclick="ChangeLights()">Change Lights</button>
</body>
</html>
Try this:
var lights = [
"assets/red.gif",
"assets/yellow.gif",
"assets/green.gif",
"assets/yellow.gif",
];
var index = 0;
function ChangeLights(){
setInterval(function() {
if(index == lights.length) {
return;
}
var image = document.getElementById('traffic');
image.src=lights[index];
index = index + 1;
}, 1000);
}
<img id="traffic" src="assets/red.gif"><br>
<button onclick="ChangeLights()">Change Lights</button>
New instance of 'Traffic Light':
Traffic Lights can't always be the same duration in every light....
So, i started to expand this html code..
The improved code with different seconds in every light:
// Traffic Light
// Improved with different durations in every light!
// But in this html code, i will use input tag instead
var TrafficLights = (function() {
// The image
var imageTag = document.getElementById("lightImg");
// Keep track of whether the sequence is running
var running = false;
// Different stages of the traffic light (Also defines the light)
var stages = [
{
"name": "red",
"path": "https://upload.wikimedia.org/wikipedia/commons/thumb/4/45/Traffic_lights_red.svg/200px-Traffic_lights_red.svg.png",
},
{
"name": "green",
"path": "https://upload.wikimedia.org/wikipedia/commons/thumb/b/b2/Traffic_lights_dark_green.svg/200px-Traffic_lights_dark_green.svg.png"
},
{
"name": "yellow",
"path": "https://upload.wikimedia.org/wikipedia/commons/thumb/1/19/Traffic_lights_yellow.svg/200px-Traffic_lights_yellow.svg.png"
}
];
// Different amount of seconds in every light change (Must be an positive integer!)
var seconds_every_step = [
18,
24,
3
];
// Current stage of the traffic light
var stage = 0;
// Current steps of the traffic light
var steps = 0;
// Timer for automatically changing light
var timer = null;
/** * Start the traffic light sequence * */
function start() {
// Mark that the light sequence is running
running = true;
// Tell the light to change
changeLight();
}
/** * Stop the sequence from running * */
function stop() {
// Mark that the sequence is not running
running = false;
// Stop the automatic timer from running
clearInterval(timer);
}
/** * Change the light to the next one in the sequence * */
function changeLight() {
// If the timer is not running, this function does not need to do anything
if (running === false) {
clearInterval(timer);
return;
} else {};
// If the current stage gets higher than the number of stages there are, reset to 0
if (stage >= stages.length) {
stage = 0;
} else {};
// If the current steps gets higher than the number of seconds in a step there are, reset to 0
if (steps >= seconds_every_step.length) {
steps = 0;
} else {};
// Get the image from the list of stages
var image = stages[stage];
var wait_seconds = seconds_every_step[steps];
// Update the image tag and defines the light name
imageTag.src = image.path;
imageTag.alt = String("Traffic light color is " + image.name + ".");
// Increase the current stage by 1
stage++;
// Increase the current steps by 1
steps++;
// Set a timeout to change the light at the next interval
timer = setTimeout(changeLight, wait_seconds * 1000);
}
// These functions will be available on the `TrafficLights` object to allow interaction
return {
start: start,
stop: stop
}
})();
<input type="image" width="20px" id="lightImg" src="" alt="">
<br/>
<p>
<button type="button" onclick="TrafficLights.start()">Start Sequence</button> <button type="button" onclick="TrafficLights.stop()">Stop Sequence</button>
</p>
You may see an error code. Just ignore it...
var red = document.getElementById("red");
var yellow = document.getElementById("yellow");
var green = document.getElementById("green");
var btn = document.createElement("BUTTON");
btn.innerHTML = "CLICK ME";
document.body.appendChild(btn);
red.style.opacity = "1";
yellow.style.opacity = "0.2";
green.style.opacity = "0.2";
btn.onclick = function () {
setTimeout(function(){red.style.opacity = "0.2";yellow.style.opacity = "1"; setTimeout(function(){yellow.style.opacity = "0.2";green.style.opacity = "1"; setTimeout(function(){green.style.opacity = "0.2";red.style.opacity = "1"}, 1000);}, 1000);
}, 1000);
}
html{
background: linear-gradient(#08f, #fff);
padding: 40px;
width: 170px;
height: 100%;
margin: 0 auto;
}
.trafficlight{
background: #222;
background-image: linear-gradient(transparent 2%, #111 2%, transparent 3%, #111 30%);
width: 170px;
height: 400px;
border-radius: 20px;
position: relative;
border: solid 5px #333;
}
#red{
background: red;
background-image: radial-gradient(brown, transparent);
background-size: 5px 5px;
width: 100px;
height: 100px;
border-radius: 50%;
position: absolute;
top: 20px;
left: 35px;
animation: 13s red infinite;
border: dotted 2px red;
box-shadow:
0 0 20px #111 inset,
0 0 10px red;
}
#yellow{
background: yellow;
background-image: radial-gradient(orange, transparent);
background-size: 5px 5px;
width: 100px;
height: 100px;
border-radius: 50%;
border: dotted 2px yellow;
position: absolute;
top: 145px;
left: 35px;
animation: 13s yellow infinite;
box-shadow:
0 0 20px #111 inset,
0 0 10px yellow;
}
#green{
background: green;
background-image: radial-gradient(lime, transparent);
background-size: 5px 5px;
width: 100px;
height: 100px;
border-radius: 50%;
border: dotted 2px lime;
position: absolute;
top: 270px;
left: 35px;
box-shadow:
0 0 20px #111 inset,
0 0 10px lime;
animation: 13s green infinite;
}
<div class="trafficlight">
<div id="red"></div>
<div id="yellow"></div>
<div id="green"></div>
</div>
I make a simple HTML and CSS Traffic light from an online example. Then I just create the condition for it to loop red-yellow-green-red.
I'm trying to build a javascript 'Simon says' game.
So far, I made some basic tests with buttons.
First button 'Start' generates a sequence. Second button 'Show' will show what was generated.
Everything works except when the same color, in the random generated sequence, appears in a row.
Ex: = [ 'blue', 'yellow', 'yellow']
Only in this case the fade-in / fade-out effect won't work.
I checked with DevTools, what was wrong and it seems that the loop adds the class 'fade-out' to both indexes (in the case above..to index 1 and 2).
Why is that? And how can I fix it?
HTML
<div id="container">
<div id="blue" class="btn"></div>
<div id="red" class="btn"></div>
<div id="yellow" class="btn"></div>
<div id="green" class="btn"></div>
</div>
<button id="startBtn">Start</button>
<button id='showMe'>Show</button>
CSS
.btn {
height: 100px;
width: 25vw;
border: 1px solid black;
opacity: 1;
transition: opacity 1s ease;
}
.fade-out {
opacity: 0.5;
transition: opacity 1s ease;
}
#container {
display: flex;
flex-wrap: nowrap;
}
#blue {
background-color: blue;
}
#yellow {
background-color: yellow;
}
#red {
background-color: red;
}
#green {
background-color: green;
}
JS
var color = container.querySelectorAll('div.btn');
var startBtn = document.getElementById('startBtn');
var showBtn = document.getElementById('showMe');
var pcSequence = [],
mySequence = [],
i,
theLoop;
startBtn.addEventListener('click', oneMore, false);
showBtn.addEventListener('click', showSeq, false);
function oneMore(){
pcSequence.push(color[Math.floor(Math.random() * 4)]);
}
function showSeq(){
i = 0;
theLoop = setInterval(function(){
if (i > 0){ pcSequence[i - 1].classList.remove('fade-out'); }
if (i >= pcSequence.length){
clearInterval(theLoop);
} else {
pcSequence[i].classList.add('fade-out');
}
i++;
}, 2000);
}
While I am not seeing the problem with fade-out appearing on two of the squares, I do see that, for example, in your case of blue, yellow, yellow, the yellow isn't doesn't become unfaded-out at all.
If that is the problem you are concerned about, the reason is that you are removing the class and then immediately re-adding it, so the ui is essentially not doing the fade-in.
Another strategy here might be to use the transitionend event to do the fade in, rather than doing that in the interval. Something like this (I put a hack in to force the red and yellow to be set each time):
var color = container.querySelectorAll('div.btn');
var startBtn = document.getElementById('startBtn');
var showBtn = document.getElementById('showMe');
var pcSequence = [],
mySequence = [],
i,
theLoop;
startBtn.addEventListener('click', oneMore, false);
showBtn.addEventListener('click', showSeq, false);
// Force red initially
pcSequence.push(color[1]);
function oneMore(){
// Force yellow each time they press Start
pcSequence.push(color[2]);
//Math.floor(Math.random() * 4)]);
}
function clearTransition() {
var colorSquare = pcSequence[i - 1];
colorSquare.removeEventListener('transitionend', clearTransition);
colorSquare.classList.remove('fade-out');
}
function showSeq(){
i = 0;
theLoop = setInterval(function(){
if (i >= pcSequence.length){
clearInterval(theLoop);
} else {
var colorSquare = pcSequence[i];
colorSquare.classList.add('fade-out');
colorSquare.addEventListener('transitionend', clearTransition);
}
i++;
}, 2000);
}
.btn {
height: 100px;
width: 25vw;
border: 1px solid black;
opacity: 1;
transition: opacity 1s ease;
}
.fade-out {
opacity: 0.5;
transition: opacity 1s ease;
}
#container {
display: flex;
flex-wrap: nowrap;
}
#blue {
background-color: blue;
}
#yellow {
background-color: yellow;
}
#red {
background-color: red;
}
#green {
background-color: green;
}
<div id="container">
<div id="blue" class="btn"></div>
<div id="red" class="btn"></div>
<div id="yellow" class="btn"></div>
<div id="green" class="btn"></div>
</div>
<button id="startBtn">Start</button>
<button id='showMe'>Show</button>