I'm working on the "Simon Game" project.
I want it to lighten buttons in the proper sequence. But now by far the code works properly until the 2-nd level.
If I am right the checkButton(randIndexArr, counter) should be included to the promise, so that if counter === index then it should call checkButton and maybe there are some more errors that I missed.
Here's a link with the video: How the code should work to be more clear Zipline: Build a Simon Game
and here is my code:
document.addEventListener("DOMContentLoaded", function () {
'use strict';
var checkOn = document.querySelector("input[type=checkbox]");
var gameCount = document.getElementsByClassName("innerCount")[0];
var startButton = document.getElementById("innerStart");
var strictButton = document.getElementById("strictButton");
var strictInd = document.getElementById("strictIndicator");
var strictMode = false;
var soundArray = document.getElementsByTagName("audio");
var buttons = document.querySelectorAll(".bigButton");
var buttonArray = [].slice.call(buttons, 0);
checkOn.addEventListener("change", function () {
if (checkOn.checked) {
gameCount.innerHTML = "--";
} else {
gameCount.innerHTML = "";
}
});
strictButton.addEventListener("click", function () {
strictMode = !strictMode;
strictMode ? strictInd.style.backgroundColor = "#FF0000" :
strictInd.style.backgroundColor = "#850000";
});
function getRandArray() {
var array = [];
for (var i = 0; i < 22; i++) {
array[i] = Math.floor(Math.random() * 4);
}
document.getElementsByClassName("randArray")[0].innerHTML = array;
return array;
}
startButton.addEventListener("click", function () {
var level = 0;
var randIndexArr = getRandArray();
playGame(randIndexArr, level);
});
function sleep(time) {
return new Promise(resolve => {
setTimeout(resolve, time)
})
}
function checkButton(randIndexArr, counter) {
console.log('checkButton');
var checker = function checker(e) {
var clickedButtonId = e.target.dataset.sound;
lightenButton(clickedButtonId);
sleep(1000);
for (let index = 0; index <= counter; index++) {
if (+(clickedButtonId) === randIndexArr[index]) {
if (index === counter) {
console.log('checking passed - next level :', (counter + 1));
counter++;
for (var i = 0; i < 4; i++) {
buttonArray[i].removeEventListener("click", checker, false)
}
playGame(randIndexArr, counter);
}
}
}
}
;
for (var i = 0; i < 4; i++) {
buttonArray[i].addEventListener("click", checker, false)
}
}
function playGame(randIndexArr, counter) {
if (counter === 22) {
return;
}
//Show the level of the Game
gameCount.innerHTML = counter + 1;
//Light and play random buttons according to the level
//Light and play user's input then check if input is correct
randIndexArr.slice(0, counter + 1).reduce(function (promise, div, index) {
return promise.then(function () {
console.log("slice reduce");
lightenButton(div);
return new Promise(function (resolve, reject) {
setTimeout(function () {
resolve("reduce Resolve");
}, 1000);
})
})
}, Promise.resolve()).then(function (value) {
console.log(value);
checkButton(randIndexArr, counter);
});
}
function lightenButton(id) {
var lightColorsArr = ["liteGreen", "liteRed", "liteYell", "liteBlue"];
var promise = new Promise((resolve, reject) => {
soundArray[id].play();
buttonArray[id].classList.add(lightColorsArr[id]);
setTimeout(function () {
resolve("lighten");
}, 500);
});
promise.then(function (value) {
console.log(value);
buttonArray[id].classList.remove(lightColorsArr[id]);
});
}
});
#font-face {
font-family: myDirector;
src: url('https://raw.githubusercontent.com/Y-Taras/FreeCodeCamp/master/Simon/fonts/myDirector-Bold.otf');
}
#outerCircle {
display: flex;
flex-wrap: wrap;
margin: 0 auto;
width: 560px;
border: 2px dotted grey;
position: relative;
}
.bigButton {
height: 250px;
width: 250px;
border: solid #464646;
transition: all 1s;
-webkit-transition: all 1s;
-moz-transition: all 1s;
-o-transition: background-color 0.5s ease;
}
#greenButton {
background-color: rgb(9, 174, 37);
border-radius: 100% 0 0 0;
border-width: 20px 10px 10px 20px;
}
.liteGreen#greenButton {
background-color: #86f999;
}
#redButton {
background-color: rgb(174, 9, 15);
border-radius: 0 100% 0 0;
border-width: 20px 20px 10px 10px;
}
.liteRed#redButton {
background-color: #f9868a;
}
#yellowButton {
background-color: rgb(174, 174, 9);
border-radius: 0 0 0 100%;
border-width: 10px 10px 20px 20px;
}
.liteYell#yellowButton {
background-color: #f9f986;
}
#blueButton {
background-color: rgb(9, 37, 174);
border-radius: 0 0 100% 0;
border-width: 10px 20px 20px 10px;
}
.liteBlue#blueButton {
background-color: #8699f9;
}
div#innerCircle {
border: 15px solid #464646;
border-radius: 50%;
position: absolute;
top: 25%;
right: 25%;
background-color: #c4c7ce;
}
div.additionalBorder {
margin: 4px;
border-radius: 50%;
height: 242px;
width: 242px;
overflow: hidden;
}
p#tradeMark {
margin: auto;
height: 104px;
text-align: center;
font-size: 68px;
font-family: myDirector;
color: #c4c7ce;
background-color: black;
border-color: antiquewhite;
line-height: 162px;
}
span#reg {
font-size: 12px;
}
.partition {
height: 6px;
}
.buttons {
height: 128px;
border-radius: 0 0 128px 128px;
border: 2px solid black;
}
/* Start and Strict buttons*/
table {
margin-left: 5px;
}
td {
text-align: center;
width: auto;
padding: 2px 10px;
vertical-align: bottom;
}
div.innerCount {
width: 54px;
height: 40px;
background-color: #34000e;
color: crimson;
border-radius: 11px;
font-size: 28px;
line-height: 42px;
text-align: center;
font-family: 'Segment7Standard', italic;
}
button#innerStart {
width: 27px;
height: 27px;
border: 4px solid #404241;
border-radius: 50%;
background: #a50005;
box-shadow: 0 0 3px gray;
cursor: pointer;
}
div.strict {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
button#strictButton {
width: 27px;
height: 27px;
border: 4px solid #404241;
border-radius: 50%;
background: yellow;
box-shadow: 0 0 3px gray;
cursor: pointer;
}
div#strictIndicator {
width: 6px;
height: 6px;
margin-bottom: 2px;
background-color: #850000;
border-radius: 50%;
border: 1px solid #5f5f5f;
}
#switcher {
display: flex;
justify-content: center;
align-items: center;
}
.labels {
font-family: 'Roboto', sans-serif;
margin: 4px;
}
/* toggle switch */
.checkbox > input[type=checkbox] {
visibility: hidden;
}
.checkbox {
display: inline-block;
position: relative;
width: 60px;
height: 30px;
border: 2px solid #424242;
}
.checkbox > label {
position: absolute;
width: 30px;
height: 26px;
top: 2px;
right: 2px;
background-color: #a50005;
cursor: pointer;
}
.checkbox > input[type=checkbox]:checked + label {
right: 28px;
}
<div id="outerCircle">
<div class="bigButton" id="greenButton" data-sound = "0" >0
<audio src="https://s3.amazonaws.com/freecodecamp/simonSound1.mp3"></audio>
</div>
<div class="bigButton" id="redButton" data-sound = "1">1
<audio src="https://s3.amazonaws.com/freecodecamp/simonSound2.mp3" ></audio>
</div>
<div class="bigButton" id="yellowButton" data-sound = "2">2
<audio src="https://s3.amazonaws.com/freecodecamp/simonSound3.mp3"></audio>
</div>
<div class="bigButton" id="blueButton" data-sound = "3">3
<audio src="https://s3.amazonaws.com/freecodecamp/simonSound4.mp3"></audio>
</div>
<div id="innerCircle">
<div class="additionalBorder">
<p id="tradeMark">simon<span id="reg">®</span></p>
<div class="partition"></div>
<div class="buttons">
<table>
<tr class="firstRow">
<td>
<div class="innerCount"></div>
</td>
<td>
<button type="button" id="innerStart"></button>
</td>
<td>
<div class="strict">
<div id="strictIndicator"></div>
<button type="button" id="strictButton"></button>
</div>
</td>
</tr>
<tr class="labels">
<td>
<div id="countLabel">COUNT</div>
</td>
<td>
<div id="startLabel">START</div>
</td>
<td>
<div id="strictLabel">STRICT</div>
</td>
</tr>
</table>
<div id="switcher">
<span class="labels">ON</span>
<div class="checkbox">
<input id="checkMe" type="checkbox">
<label for="checkMe"></label>
</div>
<span class="labels">OFF</span>
</div>
</div>
</div>
</div>
</div>
<div class="randArray"></div>
One of the problem (along many others) is in checkButton function itself, where you checking buttons against array, but doesn't check the series of button presses, or "attempts".
For example, if randIndexArr contains values [2,2,1,1...], your code is okay with checking clickeBbuttonId with value 2 against both array's first two values, and so on.
I did rewrote only one function checkButton just to show you one of possible approaches:
var currentAttempt = 1
function checkButton(randIndexArr, counter) {
var checker = function checker(e) {
var clickedButtonId = e.target.dataset.sound;
lightenButton(clickedButtonId);
if (randIndexArr[currentAttempt -1] === +(clickedButtonId)){
if (currentAttempt - 1 === counter) {
counter++
currentAttempt = 1
for (var i = 0; i < 4; i++) {
buttonArray[i].removeEventListener("click", checker, false)
}
playGame(randIndexArr, counter);
}
currentAttempt++
} else {
currentAttempt = 1
}
};
for (var i = 0; i < 4; i++) {
buttonArray[i].addEventListener("click", checker, false)
}
}
But to be honest, whole code should be redesigned.
Related
I have a code that gets characters names and gender after clicking on film name. This information appears in modal window and my goal is to delete HTML element with characters every time I close modal, so if I select another film the new set of characters will appear, and the old one is already deleted.
The problem is that removeChild method doesn't work as expected. I've tried different variations including parentNode but nothing helped.
Here is the code:
window.addEventListener('DOMContentLoaded', function() {
let btn = document.querySelector('.sw-btn');
let content = document.querySelector('.content');
let modal = document.querySelector('.modal');
let modalBody = document.querySelector('.modal-body');
let closeBtn = document.querySelector('.close-btn');
let filmsList = document.createElement('ul');
let charsList = document.createElement('ol');
function getFilms() {
axios.get('https://swapi.co/api/films/').then(res => {
content.appendChild(filmsList);
for (var i = 0; i < res.data.results.length; i++) {
res.data.results.sort(function(a, b) {
let dateA = new Date(a.release_date),
dateB = new Date(b.release_date);
return dateA - dateB;
});
(function updateFilms() {
let addFilm = document.createElement('li');
filmsList.appendChild(addFilm);
let addFilmAnchor = document.createElement('a');
let addFilmId = document.createElement('p');
let addFilmCrawl = document.createElement('p');
let addFilmDirector = document.createElement('p');
let addFilmDate = document.createElement('p');
addFilmAnchor.textContent = res.data.results[i].title;
addFilmId.textContent = `Episode ID: ${res.data.results[i].episode_id}`;
addFilmCrawl.textContent = `Episode description: ${res.data.results[i].opening_crawl}`;
addFilmDirector.textContent = `Episode director: ${res.data.results[i].director}`;
addFilmDate.textContent = `Episode release date: ${res.data.results[i].release_date}`;
addFilm.append(addFilmAnchor, addFilmId, addFilmCrawl, addFilmDirector, addFilmDate);
})();
}
let links = document.getElementsByTagName('a');
for (let j = 0; j < links.length; j++) {
links[j].onclick = function() {
modal.style.display = 'block';
modalBody.appendChild(charsList);
let chars = res.data.results[j].characters;
for (let k = 0; k < chars.length; k++) {
const element = chars[k];
axios.get(element).then(res => {
let addChar = document.createElement('li');
charsList.appendChild(addChar);
let addCharName = document.createElement('p');
let addCharGender = document.createElement('p');
addCharName.textContent = `Character name: ${res.data.name}`;
addCharGender.textContent = `Character gender: ${res.data.gender}`;
addChar.append(addCharName, addCharGender);
})
}
closeBtn.addEventListener('click', () => {
modal.style.display = 'none';
console.log(modalBody.childNodes[0]);
// Problem is here
modalBody.removeChild(modalBody.childNodes[0]);
});
window.addEventListener('click', (e) => {
if (e.target == modal) {
modal.style.display = 'none';
console.log(modalBody.childNodes[0]);
modalBody.removeChild(modalBody.childNodes[0]);
}
})
}
}
}).catch(err => {
console.log("An error occured");
})
};
btn.addEventListener('click', getFilms);
closeBtn.addEventListener('click', () => {
modal.style.display = 'none';
});
window.addEventListener('click', (e) => {
if (e.target == modal) {
modal.style.display = 'none';
}
})
});
body {
max-height: 100vh;
padding: 0;
margin: 0;
font-family: Muli;
}
body::before {
background: url('https://upload.wikimedia.org/wikipedia/commons/thumb/6/6c/Star_Wars_Logo.svg/1200px-Star_Wars_Logo.svg.png') no-repeat center / cover;
background-size: cover;
content: "";
display: block;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -2;
opacity: 0.1;
}
h1 {
text-align: center;
color: #660d41;
font-size: 3em;
margin-top: 10px;
letter-spacing: 1px;
}
main {
display: flex;
align-items: center;
flex-direction: column;
}
.content {
max-width: 55%;
overflow-y: scroll;
max-height: 75vh;
}
.modal {
display: none;
position: fixed;
z-index: 1;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgba(0, 0, 0, 0.5);
}
.modal-content {
background-color: #f4f4f4;
margin: 20% auto;
width: 40%;
box-shadow: 0 5px 8px 0 rgba(0, 0, 0, 0.2), 0 7px 20px 0 rgba(0, 0, 0, 0.2);
animation: modalOpen 1s;
}
.modal-header {
background: coral;
padding: 15px;
color: white;
letter-spacing: 1px;
position: relative;
}
.modal-header h2 {
margin: 0;
}
.modal-body {
padding: 10px 20px;
}
.close-btn {
color: white;
float: right;
font-size: 30px;
position: absolute;
top: 0px;
right: 10px;
}
.close-btn:hover,
.close-btn:focus {
color: black;
text-decoration: none;
cursor: pointer;
transition: all 0.4s ease-in;
}
ul {
list-style-type: none;
padding: 10px 20px;
}
li {
border-bottom: 1px solid orangered;
margin-bottom: 30px;
}
li:last-child {
border-bottom: none;
margin-bottom: 0;
}
a {
font-size: 1.7em;
color: #b907d9;
cursor: pointer;
margin-bottom: 10px;
}
p {
font-size: 1.2rem;
color: #0f063f;
margin: 10px 0;
}
button {
padding: .5em 1.5em;
border: none;
color: white;
transition: all 0.2s ease-in;
background: #da2417;
border-radius: 20px;
font-size: 1em;
cursor: pointer;
margin-top: 15px;
}
button:focus {
outline: none;
}
button:hover {
background: #e7736b;
}
button:active {
box-shadow: 0px 0px 15px rgba(0, 0, 0, 0.7) inset;
}
#keyframes modalOpen {
from {
opacity: 0
}
to {
opacity: 1
}
}
<link href="https://fonts.googleapis.com/css?family=Muli&display=swap" rel="stylesheet">
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.19.0/axios.min.js"></script>
<h1>Star wars films</h1>
<main>
<div class="content"></div>
<div class="modal">
<div class="modal-content">
<div class="modal-header">
<span class="close-btn">×</span>
<h2>Episode Characters</h2>
</div>
<div class="modal-body"></div>
</div>
</div>
<button class="sw-btn">Find Films</button>
</main>
Any help would be appreciated.
The problem is that you are using the same ol element when appending the characters to the list, since you instantiate it at the beginning and re-use it later: let charsList = document.createElement('ol');
When you close the modal, you will remove that ol element from the modal, but you will not remove its content. When opening the modal again, the ol will be added again - with your old content.
If you move the declaration of charsList inside your onclick handler, it will work.
Also, you should register the close handlers of your modal only once. Otherwise, it will be called as often as you open your modal.
Demo:
window.addEventListener('DOMContentLoaded', function () {
let btn = document.querySelector('.sw-btn');
let content = document.querySelector('.content');
let modal = document.querySelector('.modal');
let modalBody = document.querySelector('.modal-body');
let closeBtn = document.querySelector('.close-btn');
let filmsList = document.createElement('ul');
closeBtn.addEventListener('click', () => {
modal.style.display = 'none';
console.log(modalBody.childNodes[0]);
modalBody.removeChild(modalBody.childNodes[0]);
});
window.addEventListener('click', (e) => {
if (e.target == modal) {
modal.style.display = 'none';
console.log(modalBody.childNodes[0]);
modalBody.removeChild(modalBody.childNodes[0]);
}
})
function getFilms() {
axios.get('https://swapi.co/api/films/').then(res => {
content.appendChild(filmsList);
for (var i = 0; i < res.data.results.length; i++) {
res.data.results.sort(function (a, b) {
let dateA = new Date(a.release_date),
dateB = new Date(b.release_date);
return dateA - dateB;
});
(function updateFilms() {
let addFilm = document.createElement('li');
filmsList.appendChild(addFilm);
let addFilmAnchor = document.createElement('a');
let addFilmId = document.createElement('p');
let addFilmCrawl = document.createElement('p');
let addFilmDirector = document.createElement('p');
let addFilmDate = document.createElement('p');
addFilmAnchor.textContent = res.data.results[i].title;
addFilmId.textContent = `Episode ID: ${res.data.results[i].episode_id}`;
addFilmCrawl.textContent = `Episode description: ${res.data.results[i].opening_crawl}`;
addFilmDirector.textContent = `Episode director: ${res.data.results[i].director}`;
addFilmDate.textContent = `Episode release date: ${res.data.results[i].release_date}`;
addFilm.append(addFilmAnchor, addFilmId, addFilmCrawl, addFilmDirector, addFilmDate);
})();
}
let links = document.getElementsByTagName('a');
for (let j = 0; j < links.length; j++) {
links[j].onclick = function () {
modal.style.display = 'block';
let charsList = document.createElement('ol');
modalBody.appendChild(charsList);
let chars = res.data.results[j].characters;
for (let k = 0; k < chars.length; k++) {
const element = chars[k];
axios.get(element).then(res => {
let addChar = document.createElement('li');
charsList.appendChild(addChar);
let addCharName = document.createElement('p');
let addCharGender = document.createElement('p');
addCharName.textContent = `Character name: ${res.data.name}`;
addCharGender.textContent = `Character gender: ${res.data.gender}`;
addChar.append(addCharName, addCharGender);
})
}
}
}
}).catch(err => {
console.log("An error occured");
})
};
btn.addEventListener('click', getFilms);
closeBtn.addEventListener('click', () => {
modal.style.display = 'none';
});
window.addEventListener('click', (e) => {
if (e.target == modal) {
modal.style.display = 'none';
}
})
});
body {
max-height: 100vh;
padding: 0;
margin: 0;
font-family: Muli;
}
body::before {
background: url('https://upload.wikimedia.org/wikipedia/commons/thumb/6/6c/Star_Wars_Logo.svg/1200px-Star_Wars_Logo.svg.png') no-repeat center / cover;
background-size: cover;
content: "";
display: block;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -2;
opacity: 0.1;
}
h1 {
text-align: center;
color: #660d41;
font-size: 3em;
margin-top: 10px;
letter-spacing: 1px;
}
main {
display: flex;
align-items: center;
flex-direction: column;
}
.content {
max-width: 55%;
overflow-y: scroll;
max-height: 75vh;
}
.modal {
display: none;
position: fixed;
z-index: 1;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgba(0, 0, 0, 0.5);
}
.modal-content {
background-color: #f4f4f4;
margin: 20% auto;
width: 40%;
box-shadow: 0 5px 8px 0 rgba(0, 0, 0, 0.2), 0 7px 20px 0 rgba(0, 0, 0, 0.2);
animation: modalOpen 1s;
}
.modal-header {
background: coral;
padding: 15px;
color: white;
letter-spacing: 1px;
position: relative;
}
.modal-header h2 {
margin: 0;
}
.modal-body {
padding: 10px 20px;
}
.close-btn {
color: white;
float: right;
font-size: 30px;
position: absolute;
top: 0px;
right: 10px;
}
.close-btn:hover,
.close-btn:focus {
color: black;
text-decoration: none;
cursor: pointer;
transition: all 0.4s ease-in;
}
ul {
list-style-type: none;
padding: 10px 20px;
}
li {
border-bottom: 1px solid orangered;
margin-bottom: 30px;
}
li:last-child {
border-bottom: none;
margin-bottom: 0;
}
a {
font-size: 1.7em;
color: #b907d9;
cursor: pointer;
margin-bottom: 10px;
}
p {
font-size: 1.2rem;
color: #0f063f;
margin: 10px 0;
}
button {
padding: .5em 1.5em;
border: none;
color: white;
transition: all 0.2s ease-in;
background: #da2417;
border-radius: 20px;
font-size: 1em;
cursor: pointer;
margin-top: 15px;
}
button:focus {
outline: none;
}
button:hover {
background: #e7736b;
}
button:active {
box-shadow: 0px 0px 15px rgba(0, 0, 0, 0.7) inset;
}
#keyframes modalOpen {
from {
opacity: 0
}
to {
opacity: 1
}
}
<link href="https://fonts.googleapis.com/css?family=Muli&display=swap" rel="stylesheet">
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.19.0/axios.min.js"></script>
<h1>Star wars films</h1>
<main>
<div class="content"></div>
<div class="modal">
<div class="modal-content">
<div class="modal-header">
<span class="close-btn">×</span>
<h2>Episode Characters</h2>
</div>
<div class="modal-body"></div>
</div>
</div>
<button class="sw-btn">Find Films</button>
</main>
Do this charsList.innerText = ''; when click close button.
When you call removeChild you take off the <ol> from modalbody in UI. You can see the modalBody is empty after clicking close button.
But you already store the <ol> element in js variable by let charsList = document.createElement('ol'); so the ol element itself doesn't change actually.
The <li> element still inside the <ol>. Then you add more new <li> and put it back to modalBody.
My solution is remove all element in <ol> when clicking close button.
I am a stuck on following issues:
How to add required error just once after first click on submit button? And not at any subsequent click after.
How can to start function (checkValid()) with RegExp validation only after first function (checkRequired()) implementation with required checking?
How to show every error after RegExp validation in its relative element? Now all errors are displayed in the last block with phone input.
Here is case on jsfiddle:
https://jsfiddle.net/SanchezMalina/hno7v4tf/35/
Or code here:
// regexp pattern functions
function validateEmail(email) {
let re = /^(([^<>()\[\]\\.,;:\s#"]+(\.[^<>()\[\]\\.,;:\s#"]+)*)|(".+"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return re.test(email.toLowerCase());
}
function validatePhone(phone) {
let re = /^[0-9]{7,20}$/;
return re.test(phone.toLowerCase());
}
function validateName(name) {
let re = /^([a-zA-Z0-9А]){3,50}$/;
return re.test(name.toLowerCase());
}
let flag = false;
// check required fields
function checkRequired() {
let notFilled = document.querySelectorAll('.is-required');
notFilled.forEach(function (el) {
if (el.value === '') {
el.parentElement.classList.add('is-empty');
addRequiredError();
} else {
el.parentElement.classList.remove('is-empty');
removeRequiredError();
}
});
}
let formFields = document.querySelectorAll('.form__field');
//add required error message
function addRequiredError() {
let errorRequiredMsg = document.createElement('div');
errorRequiredMsg.classList.add('input-required');
errorRequiredMsg.innerHTML = `<span> This field is required! </span>`;
formFields.forEach( function (elem) {
if (elem.classList.contains('is-empty')) {
elem.appendChild(errorRequiredMsg);
}
});
}
//remove required error message
function removeRequiredError() {
let requiredErrorMsg = document.querySelectorAll('.form__field .input-required');
requiredErrorMsg.forEach(function (elem) {
elem.parentElement.removeChild(elem);
});
flag = true;
}
//check validation inputs
function checkValid() {
let firstName = document.querySelector('#f-name');
let lastName = document.querySelector('#l-name');
let selectCountry = document.querySelector('.form__select');
let phone = document.querySelector('#cell');
let email = document.querySelector('#email');
formFields.forEach(function () {
if(!validateName(firstName.value) && !validateName(lastName.value) && !validatePhone(phone.value) && !validateEmail(email.value)) {
firstName.parentElement.classList.add('is-error');
lastName.parentElement.classList.add('is-error');
selectCountry.parentElement.classList.add('is-error');
phone.parentElement.classList.add('is-error');
email.parentElement.classList.add('is-error');
addValidError();
} else {
firstName.parentElement.classList.remove('is-error');
lastName.parentElement.classList.remove('is-error');
selectCountry.parentElement.classList.remove('is-error');
phone.parentElement.classList.remove('is-error');
email.parentElement.classList.remove('is-error');
removeValidError();
}
});
}
//add invalid error message
function addValidError() {
let errorValidMsg = document.createElement('div');
errorValidMsg.classList.add('input-error');
errorValidMsg.innerHTML = `<span> Input is invalid! </span>`;
formFields.forEach(function (elem) {
if (elem.classList.contains('is-error')) {
elem.appendChild(errorValidMsg);
}
});
// for (let i = 0; i < formFields.length; i++) {
//
// if (formFields[i].classList.contains('is-error')) {
//
// formFields[i].appendChild(errorValidMsg);
//
// }
// }
}
//remove invalid error message
function removeValidError() {
let requiredErrorMsg = document.querySelectorAll('.input-error');
requiredErrorMsg.forEach(function (elem) {
elem.parentNode.removeChild(elem);
})
}
//form submit handler
let formTrial = document.querySelector('.form__main');
formTrial.addEventListener('submit', function (event) {
event.preventDefault();
checkRequired();
console.log(flag);
if(flag !== false) {
checkValid();
}
});
.form__main {
display: block;
margin: 25px auto;
width: 450px;
}
.form__field {
position: relative;
margin: 10px 0;
padding: 5px 15px;
background-color: #fff;
height: 50px;
}
.form__field_select::after {
content: "";
width: 0;
height: 0;
border: 7px solid transparent;
border-color: #000 transparent transparent transparent;
position: absolute;
top: 55%;
right: 17px;
transform: translateY(-50%);
}
.form__input {
position: absolute;
left: 0;
top: 10px;
right: 0;
bottom: 10px;
padding-left: 45px;
background: none;
font-size: 14px;
font-weight: 400;
font-family: "Open Sans", sans-serif;
width: 100%;
color: #333;
line-height: 1.714;
box-sizing: border-box;
}
.form__input:focus ~ .form__label,
.form__input:valid ~ .form__label {
top: 0;
left: 45px;
transform: scale(0.6, 0.6);
color: #000;
}
.form__input:focus ~ .form__label[for=cell],
.form__input:valid ~ .form__label[for=cell] {
top: 0;
left: 125px;
}
.form__input[type=tel] {
padding-left: 125px;
}
.form__input:-webkit-autofill {
-webkit-box-shadow: 0 0 0 100px white inset;
}
.form__label {
position: absolute;
top: 50%;
left: 50px;
transform-origin: left top;
transition: all 0.3s ease;
transform: translateY(-50%);
font-size: 14px;
font-family: "Open Sans", sans-serif;
color: #999999;
line-height: 1.714;
}
.form__label[for=cell] {
left: 135px;
}
.form__select {
position: absolute;
width: 100%;
height: 100%;
top: 50%;
transform: translateY(-50%);
left: 0;
right: 0;
padding-left: 50px;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
-webkit-appearance: none;
-moz-appearance: none;
background-color: transparent;
border-color: #fff;
font-size: 14px;
font-family: "Open Sans", sans-serif;
color: #000;
line-height: 1.714;
}
.form__country-code {
position: absolute;
color: #000;
left: 50px;
font-size: 14px;
font-family: "Open Sans", sans-serif;
line-height: 2;
height: 100%;
top: 0;
bottom: 0;
display: flex;
align-items: center;
border-right: 1px solid;
padding-right: 20px;
z-index: 2;
}
.form .btn {
width: 100%;
margin: 20px 0;
}
.form .btn:hover {
transform: translateY(-5px);
box-shadow: 1px 3px 20px #c5f833;
}
<form method="post" class="form__main" novalidate>
<div class="form__field">
<i class="icon icon-user"></i>
<input class="form__input is-required" id="f-name" type="text" required>
<label class="form__label" for="f-name">First Name</label>
</div>
<div class="form__field">
<i class="icon icon-user"></i>
<input class="form__input is-required" id="l-name" type="text" required>
<label class="form__label" for="l-name">Last Name</label>
</div>
<div class="form__field">
<i class="icon icon-envelop"></i>
<input class="form__input" id="email" type="text" required>
<label class="form__label" for="email">Email</label>
</div>
<div class="form__field form__field_select">
<i class="icon icon-pin"></i>
<select class="form__select" name="country">
<option value="ro" selected>USA</option>
<option value="ua">India</option>
<option value="il">Spain</option>
</select>
</div>
<div class="form__field">
<i class="icon icon-phone"></i>
<span class="form__country-code">+10000</span>
<input class="form__input" id="cell" type="tel" required>
<label class="form__label" for="cell">Phone</label>
</div>
<button class="btn btn_lime">Try for free</button>
</form>
1)
if( !el.parentElement.classList.contains('is-empty') ) {
addRequiredError();
}
2)
not sure to understand the question
3)
function addValidError() {
let errorValidMsg = document.createElement('div');
errorValidMsg.classList.add('input-error');
errorValidMsg.innerHTML = '<span> Input is invalid! </span>';
formFields.forEach(function (elem) { // <-- here formFields is not defined
}
so i want to create an html that will create a 10x10 table with red and green lines.after that a pop up window will occur which will ask the user to put in an word and this word will be displayed only at the red square. I have managed to create the the table and the pop up window but i do not know how to make them interact. Also i can only use html,javascript and php. The code i have written is the following:
<body onload='myFunction()'>
<?php echo "<table border =\"4\" style='border-color:blue;'>";
for ($row=1; $row <= 10; $row++) {
for ($col=1; $col <= 10; $col++) {
if ($row%2==0) {
echo "<td span style='border: 2px solid white;background-color: green' height='50' width='50'></td /span> \n";}
else {
echo "<td span style='border: 2px solid white; background-color: red' height='50' width='50'></td /span> \n";
}
}
echo "</tr>";
}
echo "</table>";?>
<script>
function myFunction() {
var person = prompt("Εισαγεται εναν χαρακτηρα", "tsa");
return person;
}
</script>
</body>
The easiest way is to do it in JavaScript. Btw. do not use old-school events, modern browsers support addEventListener.
document.addEventListener('DOMContentLoaded', evt =>
{
let table = document.createElement('table');
table.innerHTML = ('<tr>' + ('<td></td>'.repeat(10)) + '</tr>').repeat(10);
document.getElementById('table-container').appendChild(table);
let person = prompt("Εισαγεται εναν χαρακτηρα", "tsa");
table.querySelectorAll('tr:nth-child(2n+1)>td').forEach(td => td.innerText = person);
});
table
{
border: 4px solid blue;
}
td
{
border: 2px solid white;
background-color: green;
height: 50px;
width: 50px;
}
tr:nth-child(2n+1)>td
{
background-color: red;
}
<!DOCTYPE html>
<html lang=en>
<head><title>Table</title>
<meta charset="UTF-8">
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="table-container"></div>
<script src="javascript.js"></script>
</body>
</html>
window.onload = buildTable;
const submit = document.getElementById('submit'),
cancel = document.getElementById('cancel'),
myInput = document.getElementsByClassName('myInput')[0],
myInputBg = document.getElementsByClassName('myInputBg')[0];
let currentIndex = '',
currentValue = '';
submit.addEventListener('click', handleSubmit, false);
cancel.addEventListener('click', handleCancel, false);
function buildTable() {
const myTableDiv = document.getElementById('myDynamicTable');
let table = '<table id="myTable"><tbody>';
let tr = '';
for(let i = 0; i < 10; i += 1) {
tr += `<tr data-row=${i} colspan="1">`;
let td = '';
for(let j = 0; j < 10; j += 1) {
td += `<td data-col=${j} id="td-${ (10 * i) + j + 1 }" rowspan="1" data-index=${ (10 * i) + j + 1 }></td>`;
}
tr += td + '</tr>';
}
table += table + tr + '</tbody><table>';
myTableDiv.innerHTML = table;
const myTds = document.querySelectorAll('td');
myTds.forEach( td => td.addEventListener('click', handleTableChange, false) );
}
function handleTableChange(event) {
const myIndex = event.target.dataset.index;
const value = event.target.innerText;
openInputEditor(myIndex, value);
}
function openInputEditor(index, value) {
myInputBg.style.display = 'block';
myInput.style.display = 'flex';
currentIndex = index;
currentValue = value;
document.getElementById('myInputData').value = value;
}
function handleSubmit() {
const inputVal = document.getElementById('myInputData');
if(!inputVal.value) return;
document.querySelector(`#td-${currentIndex}`).innerText = inputVal.value;
handleCancel();
}
function handleCancel() {
myInputBg.style.display = 'none';
myInput.style.display = 'none';
}
*{
box-sizing: border-box;
}
#myDynamicTable {
position: absolute;
z-index: 1;;
width: 400px;
height: 400px;
margin: 0 auto;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
table {
width: 100%;
height: 100%;
border-spacing: 1px;
}
table tr:nth-child(even) td{
border: 1px solid green;
}
table tr:nth-child(odd) td{
border: 1px solid red;
}
td {
width: 40px;
height: 40px;
max-width: 40px;
max-height: 40px;
font-size: 14px;
color: #444;
cursor: pointer;
outline: none;
text-align: center;
overflow: hidden;
background: #f4f4f4;
}
.myInput {
position: absolute;
display: none;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 300px;
height: 300px;
z-index: 3;
background: #fff;
padding: 10px;
align-items: center;
justify-content: center;
flex-direction: column;
}
#myInputData {
width: 90%;
margin: 10px auto;
min-height: 80px;
padding: 10px;
}
button {
display: inline-block;
margin: 5px;
padding: 10px;
text-align: center;
outline: none;
border-radius: 4px;
text-transform: uppercase;
border: 1px solid transparent;
cursor: pointer;
}
button#submit {
color: #fff;
background: #009688;
}
button#submit :hover {
border: 1px solid #009888;
}
button#cancel {
color: rgba(0, 0, 0, 0.8);
background: #f8f8f8;
}
button#cancel:hover {
border: 1px solid rgba(0, 0, 0, 0.8);
}
.myInputBg {
position: absolute;
display: none;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
left: 0;
top: 0;
z-index: 2;
}
<div class="myInputBg"></div>
<div id="myDynamicTable"></div>
<div class="myInput">
<textarea id="myInputData"></textarea>
<div>
<button id="submit">Submit</button>
<button id="cancel">Cancel</button>
</div>
</div>
I'm trying to figure out the best way of adding a time counter to my memory game. The game consists of a lot of squares and if the user has figured out and won the game, a modal pops up and tells the user that they have won and offers to reset the game to start over.
I want to add the time the user spent playing the game. Since they have opened the page it should count the time from 0 to x seconds, and later when the user finishes the game, it echo's the score on the modal, so the user can see their score. But if someone did not complete the quiz in x seconds, a function runs that opens modal, but this time echo's that person has run out of time and offers to start over.
I'm using a small remake of this game on codepen
HTML:
<div class="modal-overlay">
<div class="modal">
<h2 class="winner">You Rock!</h2>
<button class="restart">Play Again?</button>
<p class="message">Developed on CodePen by Nate Wiley</p>
<p class="share-text">Share it?</p>
<ul class="social">
<li><a target="_blank" class="twitter" href="http://twitter.com/share?url=http://codepen.io/natewiley/pen/HBrbL"><span class="brandico-twitter-bird"></span></a></li>
<li><a target="_blank" class="facebook" href="http://www.facebook.com/sharer.php?u=http://codepen.io/natewiley/pen/HBrbL"><span class="brandico-facebook"></span></a></li>
<li><a target="_blank" class="google" href="https://plus.google.com/share?url=http://codepen.io/natewiley/pen/HBrbL"><span class="brandico-googleplus-rect"></span></a></li>
</ul>
</div>
</div>
All logos are property of their respective owners, No Copyright infringement intended.
The CSS part:
#import url(http://weloveiconfonts.com/api/?family=brandico);
/* brandico */
[class*="brandico-"]:before {
font-family: 'brandico', sans-serif;
}
* {
box-sizing: border-box;
}
html, body {
height: 100%;
}
body {
background: black;
min-height: 100%;
font-family: "Arial", sans-serif;
}
.wrap {
position: relative;
height: 100%;
min-height: 500px;
padding-bottom: 20px;
}
.game {
transform-style: preserve-3d;
perspective: 500px;
min-height: 100%;
height: 100%;
}
#mixin width($max){
#media (max-width: $max){
#content;
}
}
#keyframes matchAnim {
0% {
background: #bcffcc;
}
100% {
background: white;
}
}
.card {
float: left;
width: 16.66666%;
height: 25%;
padding: 5px;
text-align: center;
display: block;
perspective: 500px;
position: relative;
cursor: pointer;
z-index: 50;
-webkit-tap-highlight-color: rgba(0,0,0,0);
#include width(800px){
width: 25%;
height: 16.666%;
}
.inside {
width: 100%;
height: 100%;
display: block;
transform-style: preserve-3d;
transition: .4s ease-in-out;
background: white;
&.picked, &.matched {
transform: rotateY(180deg);
}
&.matched {
animation: 1s matchAnim ease-in-out;
animation-delay: .4s;
}
}
.front, .back {
border: 1px solid black;
backface-visibility: hidden;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
padding: 20px;
img {
max-width: 100%;
display: block;
margin: 0 auto;
max-height: 100%;
}
}
.front {
transform: rotateY(-180deg);
#include width(800px){
padding: 5px;
}
}
.back{
#include width(800px){
padding: 10px;
}
}
}
.modal-overlay {
display: none;
background: rgba(0,0,0,.8);
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.modal {
display: none;
position: relative;
width: 500px;
height: 400px;
max-height: 90%;
max-width: 90%;
min-height: 380px;
margin: 0 auto;
background: white;
top: 50%;
transform: translateY(-50%);
padding: 30px 10px;
.winner {
font-size: 80px;
text-align: center;
font-family: "Anton", sans-serif;
color: #4d4d4d;
text-shadow: 0px 3px 0 black;
#include width(480px){
font-size: 60px;
}
}
.restart {
font-family: "Anton", sans-serif;
margin: 30px auto;
padding: 20px 30px;
display: block;
font-size: 30px;
border: none;
background: #4d4d4d;
background: linear-gradient(#4d4d4d, #222);
border: 1px solid #222;
border-radius: 5px;
color: white;
text-shadow: 0px 1px 0 black;
cursor: pointer;
&:hover {
background: linear-gradient(#222, black);
}
}
.message {
text-align: center;
a {
text-decoration: none;
color: #28afe6;
font-weight: bold;
&:hover {
$c: lighten(#28afe6, 10%);
color: $c;
border-bottom: 1px dotted $c;
}
}
}
.share-text {
text-align: center;
margin: 10px auto;
}
.social {
margin: 20px auto;
text-align: center;
li {
display: inline-block;
height: 50px;
width: 50px;
margin-right: 10px;
&:last-child {
margin-right: 0;
}
a {
display: block;
line-height: 50px;
font-size: 20px;
color: white;
text-decoration: none;
border-radius: 5px;
&.facebook {
background: #3b5998;
&:hover {
background: lighten(#3b5998, 10%);
}
}
&.google {
background: #D34836;
&:hover {
background: lighten(#D34836, 10%);
}
}
&.twitter {
background: #4099FF;
&:hover {
background: lighten(#4099FF, 10%);
}
}
}
}
}
}
footer {
height: 20px;
position: absolute;
bottom: 0;
width: 100%;
z-index: 0;
.disclaimer {
line-height: 20px;
font-size: 12px;
color: #727272;
text-align: center;
#include width(767px){
font-size: 8px;
}
}
}
And js:
(function(){
var Memory = {
init: function(cards){
this.$game = $(".game");
this.$modal = $(".modal");
this.$overlay = $(".modal-overlay");
this.$restartButton = $("button.restart");
this.cardsArray = $.merge(cards, cards);
this.shuffleCards(this.cardsArray);
this.setup();
},
shuffleCards: function(cardsArray){
this.$cards = $(this.shuffle(this.cardsArray));
},
setup: function(){
this.html = this.buildHTML();
this.$game.html(this.html);
this.$memoryCards = $(".card");
this.binding();
this.paused = false;
this.guess = null;
},
binding: function(){
this.$memoryCards.on("click", this.cardClicked);
this.$restartButton.on("click", $.proxy(this.reset, this));
},
// kinda messy but hey
cardClicked: function(){
var _ = Memory;
var $card = $(this);
if(!_.paused && !$card.find(".inside").hasClass("matched") && !$card.find(".inside").hasClass("picked")){
$card.find(".inside").addClass("picked");
if(!_.guess){
_.guess = $(this).attr("data-id");
} else if(_.guess == $(this).attr("data-id") && !$(this).hasClass("picked")){
$(".picked").addClass("matched");
_.guess = null;
} else {
_.guess = null;
_.paused = true;
setTimeout(function(){
$(".picked").removeClass("picked");
Memory.paused = false;
}, 600);
}
if($(".matched").length == $(".card").length){
_.win();
}
}
},
win: function(){
this.paused = true;
setTimeout(function(){
Memory.showModal();
Memory.$game.fadeOut();
}, 1000);
},
showModal: function(){
this.$overlay.show();
this.$modal.fadeIn("slow");
},
hideModal: function(){
this.$overlay.hide();
this.$modal.hide();
},
reset: function(){
this.hideModal();
this.shuffleCards(this.cardsArray);
this.setup();
this.$game.show("slow");
},
// Fisher--Yates Algorithm -- http://bost.ocks.org/mike/shuffle/
shuffle: function(array){
var counter = array.length, temp, index;
// While there are elements in the array
while (counter > 0) {
// Pick a random index
index = Math.floor(Math.random() * counter);
// Decrease counter by 1
counter--;
// And swap the last element with it
temp = array[counter];
array[counter] = array[index];
array[index] = temp;
}
return array;
},
buildHTML: function(){
var frag = '';
this.$cards.each(function(k, v){
frag += '<div class="card" data-id="'+ v.id +'"><div class="inside">\
<div class="front"><img src="'+ v.img +'"\
alt="'+ v.name +'" /></div>\
<div class="back"><img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/codepen-logo.png"\
alt="Codepen" /></div></div>\
</div>';
});
return frag;
}
};
var cards = [
{
name: "php",
img: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/php-logo_1.png",
id: 1,
},
{
name: "css3",
img: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/css3-logo.png",
id: 2
},
{
name: "html5",
img: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/html5-logo.png",
id: 3
},
{
name: "jquery",
img: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/jquery-logo.png",
id: 4
},
{
name: "javascript",
img: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/js-logo.png",
id: 5
},
{
name: "node",
img: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/nodejs-logo.png",
id: 6
},
{
name: "photoshop",
img: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/photoshop-logo.png",
id: 7
},
{
name: "python",
img: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/python-logo.png",
id: 8
},
{
name: "rails",
img: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/rails-logo.png",
id: 9
},
{
name: "sass",
img: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/sass-logo.png",
id: 10
},
{
name: "sublime",
img: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/sublime-logo.png",
id: 11
},
{
name: "wordpress",
img: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/wordpress-logo.png",
id: 12
},
];
Memory.init(cards);
})();
You can accomplish a timer functionality with using setTimeout recursively or setInterval like so:
(function () {
var timeContainer = document.getElementById("timer-value");
var startButton = document.getElementById("start-game");
var timer = 0;
var maxTime = 30;
var timeout = null;
function count () {
timeout = setTimeout(function () {
if (timer < maxTime) {
timer++;
timeContainer.innerText = timer;
count();
}
else {
alert("Time's up!");
startButton.style.display = "inline-block";
}
}, 1000);
}
function endGame () {
clearTimeout(timeout);
startButton.style.display = "inline-block";
alert("You completed the game in time!");
}
function startGame () {
if (timeout) { clearTimeout(timeout); }
timer = 0;
timeContainer.innerText = timer;
this.style.display = "none";
count();
}
document.getElementById("start-game").addEventListener("click", startGame);
document.getElementById("end-game").addEventListener("click", endGame);
})();
<h3>Timer: <span id="timer-value">0</span></h3>
<button id="start-game">Start Game</button> <button id="end-game">End Game</button>
How I can make the game restart from the beginning by clicking start button except reloading the whole page?
The problem occurs because when user clicks Start playGame function is called, but the a previous instance of playGame function is still running. I even thought about to kill a previous instance of function but in JS it can not be implemented except using webworker.terminate().
Here's the code:
document.addEventListener("DOMContentLoaded", function() {
'use strict';
var checkOn = document.querySelector("input[type=checkbox]");
var gameCount = document.getElementsByClassName("innerCount")[0];
var startButton = document.getElementById("innerStart");
var strictButton = document.getElementById("strictButton");
var strictInd = document.getElementById("strictIndicator");
var strictMode = false;
var soundArray = document.getElementsByTagName("audio");
var buttons = document.querySelectorAll(".bigButton");
var buttonArray = [].slice.call(buttons, 0);
checkOn.addEventListener("change", function() {
if (checkOn.checked) {
gameCount.innerHTML = "--";
} else {
gameCount.innerHTML = "";
}
});
strictButton.addEventListener("click", function() {
if (checkOn.checked) {
strictMode = !strictMode;
strictMode ? strictInd.style.backgroundColor = "#FF0000" :
strictInd.style.backgroundColor = "#850000";
}
});
function getRandArray() {
var array = [];
for (var i = 0; i < 22; i++) {
array[i] = Math.floor(Math.random() * 4);
}
return array;
}
startButton.addEventListener("click", function() {
if (checkOn.checked) {
var level = 0;
var randIndexArr = getRandArray();
sleep(700).then(function() {
playGame(randIndexArr, level);
});
}
});
function sleep(time) {
return new Promise(resolve => {
setTimeout(resolve, time)
})
}
function checkButton(randIndexArr, counter) {
var indexButton = 0;
var checker = function checker(e) {
var clickedButtonId = e.target.dataset.sound;
lightenButton(clickedButtonId);
if (+(clickedButtonId) === randIndexArr[indexButton]) {
if (indexButton === counter) {
counter++;
for (let i = 0; i < 4; i++) {
buttonArray[i].removeEventListener("click", checker, false)
}
sleep(2000).then(function() {
playGame(randIndexArr, counter);
});
}
indexButton++;
} else {
gameCount.innerHTML = "--";
if (strictMode) {
indexButton = 0;
counter = 0;
} else {
indexButton = 0;
}
for (let i = 0; i < 4; i++) {
buttonArray[i].removeEventListener("click", checker, false)
}
sleep(2000).then(function() {
playGame(randIndexArr, counter);
});
}
};
for (var i = 0; i < 4; i++) {
buttonArray[i].addEventListener("click", checker, false)
}
}
function playGame(randIndexArr, counter) {
if (counter === 22) {
return;
}
//Show the level of the Game
gameCount.innerHTML = counter + 1;
//Light and play user's input then check if input is correct
randIndexArr.slice(0, counter + 1).reduce(function(promise, div, index) {
return promise.then(function() {
lightenButton(div);
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve();
}, 1000);
})
})
}, Promise.resolve()).then(function() {
checkButton(randIndexArr, counter);
});
}
function lightenButton(id) {
var lightColorsArr = ["liteGreen", "liteRed", "liteYell", "liteBlue"];
soundArray[id].play();
buttonArray[id].classList.add(lightColorsArr[id]);
sleep(500).then(function() {
buttonArray[id].classList.remove(lightColorsArr[id])
});
}
});
#font-face {
font-family: myDirector;
src: url('https://raw.githubusercontent.com/Y-Taras/FreeCodeCamp/master/Simon/fonts/myDirector-Bold.otf');
}
body {
background-color: #5f5f5f;
}
#outerCircle {
display: flex;
flex-wrap: wrap;
margin: 0 auto;
width: 560px;
border: 2px dotted grey;
position: relative;
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
}
.bigButton {
height: 250px;
width: 250px;
border: solid #464646;
transition: all 1s;
-webkit-transition: all 1s;
-moz-transition: all 1s;
-o-transition: background-color 0.5s ease;
}
#greenButton {
background-color: rgb(9, 174, 37);
border-radius: 100% 0 0 0;
border-width: 20px 10px 10px 20px;
}
.liteGreen#greenButton {
background-color: #86f999;
}
#redButton {
background-color: rgb(174, 9, 15);
border-radius: 0 100% 0 0;
border-width: 20px 20px 10px 10px;
}
.liteRed#redButton {
background-color: #f9868a;
}
#yellowButton {
background-color: rgb(174, 174, 9);
border-radius: 0 0 0 100%;
border-width: 10px 10px 20px 20px;
}
.liteYell#yellowButton {
background-color: #f9f986;
}
#blueButton {
background-color: rgb(9, 37, 174);
border-radius: 0 0 100% 0;
border-width: 10px 20px 20px 10px;
}
.liteBlue#blueButton {
background-color: #8699f9;
}
div#innerCircle {
border: 15px solid #464646;
border-radius: 50%;
position: absolute;
top: 25%;
right: 25%;
background-color: #c4c7ce;
}
div.additionalBorder {
margin: 4px;
border-radius: 50%;
height: 242px;
width: 242px;
overflow: hidden;
}
p#tradeMark {
margin: auto;
height: 104px;
text-align: center;
font-size: 68px;
font-family: myDirector;
color: #c4c7ce;
background-color: black;
border-color: antiquewhite;
line-height: 162px;
}
span#reg {
font-size: 12px;
}
.partition {
height: 6px;
}
.buttons {
height: 128px;
border-radius: 0 0 128px 128px;
border: 2px solid black;
}
/* Start and Strict buttons*/
table {
margin-left: 5px;
}
td {
text-align: center;
width: auto;
padding: 2px 10px;
vertical-align: bottom;
}
div.innerCount {
width: 54px;
height: 40px;
background-color: #34000e;
color: crimson;
border-radius: 11px;
font-size: 28px;
line-height: 42px;
text-align: center;
font-family: 'Segment7Standard', italic;
}
button#innerStart {
width: 27px;
height: 27px;
border: 4px solid #404241;
border-radius: 50%;
background: #a50005;
box-shadow: 0 0 3px gray;
cursor: pointer;
}
div.strict {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
button#strictButton {
width: 27px;
height: 27px;
border: 4px solid #404241;
border-radius: 50%;
background: yellow;
box-shadow: 0 0 3px gray;
cursor: pointer;
}
div#strictIndicator {
width: 6px;
height: 6px;
margin-bottom: 2px;
background-color: #850000;
border-radius: 50%;
border: 1px solid #5f5f5f;
}
#switcher {
display: flex;
justify-content: center;
align-items: center;
}
.labels {
font-family: 'Roboto', sans-serif;
margin: 4px;
}
/* toggle switch */
.checkbox > input[type=checkbox] {
visibility: hidden;
}
.checkbox {
display: inline-block;
position: relative;
width: 60px;
height: 30px;
border: 2px solid #424242;
}
.checkbox > label {
position: absolute;
width: 30px;
height: 26px;
top: 2px;
right: 2px;
background-color: #a50005;
cursor: pointer;
}
.checkbox > input[type=checkbox]:checked + label {
right: 28px;
}
<div id="outerCircle">
<div class="bigButton" id="greenButton" data-sound="0">
<audio src="https://s3.amazonaws.com/freecodecamp/simonSound1.mp3"></audio>
</div>
<div class="bigButton" id="redButton" data-sound="1">
<audio src="https://s3.amazonaws.com/freecodecamp/simonSound2.mp3"></audio>
</div>
<div class="bigButton" id="yellowButton" data-sound="2">
<audio src="https://s3.amazonaws.com/freecodecamp/simonSound3.mp3"></audio>
</div>
<div class="bigButton" id="blueButton" data-sound="3">
<audio src="https://s3.amazonaws.com/freecodecamp/simonSound4.mp3"></audio>
</div>
<div id="innerCircle">
<div class="additionalBorder">
<p id="tradeMark">simon<span id="reg">®</span>
</p>
<div class="partition"></div>
<div class="buttons">
<table>
<tr class="firstRow">
<td>
<div class="innerCount"></div>
</td>
<td>
<button type="button" id="innerStart"></button>
</td>
<td>
<div class="strict">
<div id="strictIndicator"></div>
<button type="button" id="strictButton"></button>
</div>
</td>
</tr>
<tr class="labels">
<td>
<div id="countLabel">COUNT</div>
</td>
<td>
<div id="startLabel">START</div>
</td>
<td>
<div id="strictLabel">STRICT</div>
</td>
</tr>
</table>
<div id="switcher">
<span class="labels">ON</span>
<div class="checkbox">
<input id="checkMe" type="checkbox">
<label for="checkMe"></label>
</div>
<span class="labels">OFF</span>
</div>
</div>
</div>
</div>
</div>
I didn't dig super deep into your code, but it looks like the crux of it is you're using setTimeout(), and that timeout may still be running when you restart.
What you need to do is store the return value of setTimeout() which is actually an id you can then pass to clearTimeout(), which will stop that timeout.
So, on your sleep() function, store the id:
function sleep(time) {
return new Promise(resolve => {
this.timeoutId = setTimeout(resolve, time)
});
}
And when you go to restart your game:
// ...
if (this.timeoutId) {
clearTimeout(this.timeoutId);
this.timeoutId = null;
}
//...
And then also just make sure you don't have any other code that will get more than two timeouts running at the same time (or you'll lose one of the ids).