Java Script Sorting algorithm visualizer - javascript

k = []
len = 100;
time = true
cont = document.getElementsByClassName("cont")[0];
cont.innerHTML = "";
for (let i = 0; i < len; i++) {
t = Math.round(Math.random() * 800 ) + 5
k.push(t);
cont.innerHTML += "<div class='block' style = 'height:" + t + "px'></div>"
}
function reset(){
k = []
cont.innerHTML = "";
for (let i = 0; i < len; i++) {
t = Math.round(Math.random() * 800 ) + 5
k.push(t);
cont.innerHTML += "<div class='block' style = 'height:" + t + "px'> </div>"
}
}
function bubble(){
function iloop(i){
if(i < len){
setTimeout(function(){
function jloop(j){
if(j < len){
setTimeout(function(){
if (k[j] > k[j + 1]) {
let tmp = k[j];
k[j] = k[j + 1];
k[j + 1] = tmp;
}
cont.innerHTML = "";
for (let p = 0; p < len; p++) {
cont.innerHTML += "<div class='block' style = 'height:" + k[p] + "px'></div>"
}
j++;
jloop(j);
}, 100);
}
}
jloop(0);
i++;
iloop(i);
}, 100);
}
}
iloop(0);
}
.cont {
width: 100%;
height: 900px;
display: block;
background-color: pink;
padding: 0px;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
-ms-flex-line-pack: center;
align-content: center; }
.cont .block {
display: inline-block;
width: 10px;
margin: auto 1px;
background-color: red;
font-size: 5px;
bottom: 0px; }
<button class="reset" onclick="reset()">Reset Array
</button>
<button class="bubble" onclick="bubble()">Bubble Sort
</button>
<div class="cont">
</div>
I am using this simple code to make a javascript visualizer for sorting algorithm but the problem is that it is very choppy and skips multiples frames while running even at a delay of 100ms. I have an i7 7700hq and gtx 1060 so I know that the problem is mostly not my laptop but my approach to it so what approach should I take
Here is a code pen version if your snippets are not working
https://codepen.io/varunagarwal/pen/gOaQqbG
Edit: someone told me to make it a runnable snippet so there you go

You have overlapping setTimeout timers, and a lot of them being scheduled. You only want to yield back to the browser when there's a change to show, and you only want to show a given change once.
Since you're using ES2015+, I'd probably use a generator function to do the sort, yielding when something changes:
function *sortGen() {
for (let i = 0; i < len; ++i) {
for (let j = 0; j < len; ++j) {
if (k[j] > k[j + 1]) {
let tmp = k[j];
k[j] = k[j + 1];
k[j + 1] = tmp;
yield j; // *** Yield to caller, saying what changed
}
}
}
}
Then the code calling it would call its next method, do the update, and then schedule callback for just before the next animation frame via requestAnimationFrame (unless you want to artificially slow it down, in which case setTimeout is fine). Animation frames happen 60 times/second (roughly every 16.666667ms) provided the browser isn't busy doing something else. Here's bubble using the generator from the sortGen function above:
function bubble() {
const gen = sortGen();
tick();
function tick() {
const result = gen.next();
if (!result.done) {
// *** No need to recreate all the elements, just reorder the ones that got swapped
const el = cont.children[result.value];
const next = el.nextElementSibling;
el.parentElement.insertBefore(next, el);
requestAnimationFrame(tick);
}
}
}
(You could make it an async generator and use a for-await-of loop, but I don't think it really buys you much.)
Here's a live example; I've also included some comments in the code making other suggestions:
"use strict"; // *** Use strict mode
// *** Declare your variables (the old code relied on The Horror of Implicit Globals, which
// strict mode fixes)
let k = []; // *** Consistently use semicolons (or consistently rely on ASI)
let len = 100;
let time = true;
const cont = document.getElementsByClassName("cont")[0];
// *** Don't duplicate code, just use `reset`
reset();
function reset(){
k = [];
// *** Never use += on `innerHTML`
let html = "";
for (let i = 0; i < len; i++) {
// *** Declare your variables
const t = Math.round(Math.random() * 800 ) + 5;
k.push(t);
html += makeBlock(t);
}
cont.innerHTML = html;
}
function makeBlock(value) {
return "<div class='block' style = 'height:" + value + "px'></div>";
}
function *sortGen() {
for (let i = 0; i < len; ++i) {
for (let j = 0; j < len; ++j) {
if (k[j] > k[j + 1]) {
let tmp = k[j];
k[j] = k[j + 1];
k[j + 1] = tmp;
yield j; // *** Yield to caller, saying what changed
}
}
}
}
function bubble() {
const gen = sortGen();
tick();
function tick() {
const result = gen.next();
if (!result.done) {
// *** No need to recreate all the elements, just reorder the ones that got swapped
const el = cont.children[result.value];
const next = el.nextElementSibling;
el.parentElement.insertBefore(next, el);
requestAnimationFrame(tick);
}
}
}
.cont {
width: 100%;
height: 900px;
display: block;
background-color: pink;
padding: 0px;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
-ms-flex-line-pack: center;
align-content: center;
} /* *** Don't hide closing } at the end of a line */
.cont .block {
display: inline-block;
width: 10px;
margin: auto 1px;
background-color: red;
font-size: 5px;
bottom: 0px;
} /* *** Don't hide closing } at the end of a line */
<button class="reset" onclick="reset()">Reset Array
</button>
<button class="bubble" onclick="bubble()">Bubble Sort
</button>
<div class="cont">
</div>
Also on CodePen.
For what it's worth, the async generator approach looks something like this:
const nextFrame = cb => new Promise(resolve => {
requestAnimationFrame(() => {
cb();
resolve();
});
});
function bubble() {
(async () => {
for await (const value of sortGen()) {
await nextFrame(() => {
const el = cont.children[value];
const next = el.nextElementSibling;
el.parentElement.insertBefore(next, el);
});
}
})()
.catch(error => {
// Handle/report error here...
console.error(error);
});
}
"use strict"; // *** Use strict mode
// *** Declare your variables (the old code relied on The Horror of Implicit Globals, which
// strict mode fixes)
let k = []; // *** Consistently use semicolons (or consistently rely on ASI)
let len = 100;
let time = true;
const cont = document.getElementsByClassName("cont")[0];
// *** Don't duplicate code, just use `reset`
reset();
function reset(){
k = [];
// *** Never use += on `innerHTML`
let html = "";
for (let i = 0; i < len; i++) {
// *** Declare your variables
const t = Math.round(Math.random() * 800 ) + 5;
k.push(t);
html += makeBlock(t);
}
cont.innerHTML = html;
}
function makeBlock(value) {
return "<div class='block' style = 'height:" + value + "px'></div>";
}
function *sortGen() {
for (let i = 0; i < len; ++i) {
for (let j = 0; j < len; ++j) {
if (k[j] > k[j + 1]) {
let tmp = k[j];
k[j] = k[j + 1];
k[j + 1] = tmp;
yield j; // *** Yield to caller, saying what changed
}
}
}
}
const nextFrame = cb => new Promise(resolve => {
requestAnimationFrame(() => {
cb();
resolve();
});
});
function bubble() {
(async () => {
for await (const value of sortGen()) {
await nextFrame(() => {
const el = cont.children[value];
const next = el.nextElementSibling;
el.parentElement.insertBefore(next, el);
});
}
})()
.catch(error => {
// Handle/report error here...
console.error(error);
});
}
.cont {
width: 100%;
height: 900px;
display: block;
background-color: pink;
padding: 0px;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
-ms-flex-line-pack: center;
align-content: center;
} /* *** Don't hide closing } at the end of a line */
.cont .block {
display: inline-block;
width: 10px;
margin: auto 1px;
background-color: red;
font-size: 5px;
bottom: 0px;
} /* *** Don't hide closing } at the end of a line */
<button class="reset" onclick="reset()">Reset Array
</button>
<button class="bubble" onclick="bubble()">Bubble Sort
</button>
<div class="cont">
</div>
Also on CodePen.

Related

Looping a lotto game from codepen

Recently I've been looking at different projects and trying to modify them to try and understand the JS code much better. Recently I came across this lotto game from code pen. So I thought to try to make it to a game where you had coins as lives, then you get some stars based on how many numbers you got right.
The thing that I am struggling at is trying to loop the code on the click of a button. Currently the code is restarting the game by recalling its own link, in this case I just used my index.html as replacement for the href just to work on it for now. I want to change this because it doesn't let me consume all my coins (lives) without refreshing the page.
I tried putting everything in a function instead of calling it through the DOM being loaded. I then called that function when the dom has loaded, then after each draw I tried calling it again by using another button but it doesn't work. Tried changing the href to the function but that doesn't work as well. I also tried a few other things but I cannot make a work around this. Any help is appreciated! I'm still learning Javascript, so please pardon my question.
The code is not owned by me, I am just playing around with it, here's the original codepen link. https://codepen.io/EwaTrojanowskaGrela/pen/KmJMWb
// Declaration of scores and lives
var stars = 0;
var coins = 5;
// End of comment
// For redeclaration in innerHTML
var starsEarned;
// End of comment
// For displaying current score
document.getElementById("star-count").innerHTML = stars;
document.getElementById("coin-count").innerHTML = coins;
// End of comment
document.addEventListener("DOMContentLoaded", function(e){
var body = document.querySelector("body");
var section = document.querySelector("section");
var articleLotto = document.querySelector(".lotto");
var articleBalls = document.querySelector(".balls");
var numbers = [];
var balls = document.getElementsByClassName("ball");
var drawnNums = [];
var chosenByMachine = [];
function createNumberBoard(number){
console.log("I work");
var board = document.createElement("div");
board.classList.add("board");
articleLotto.appendChild(board);
for( var i = 0; i<number; i ++){
var boardEl = document.createElement("button");
boardEl.classList.add("boardEl");
board.appendChild(boardEl);
}
var boardEls = document.getElementsByClassName("boardEl");
for( var i =0; i<boardEls.length; i++){
boardEls[i].setAttribute("data-number", i+1);
var dataNumber = boardEls[i].getAttribute("data-number");
var number = parseInt(dataNumber, 10);
numbers.push(number);
boardEls[i].textContent = number;
}
}
createNumberBoard(49);
var board = document.querySelector(".board");
var boardEls = document.querySelectorAll(".boardEl");
function drawNumbers(){
//boardEls.forEach(boardEl => boardEl.addEventListener("click", selectNums));
for (var i = 0; i<boardEls.length; i++){
boardEls[i].addEventListener("click", selectNums);
}
function selectNums(){
var number = parseInt(this.dataset.number, 10);
if(this.hasAttribute("data-number")){
drawnNums.push(number);
this.removeAttribute("data-number");
this.classList.add("crossedOut");
}
if(drawnNums.length=== 6){
//boardEls.forEach( boardEl => boardEl.removeAttribute("data-number"));
//boardEls.forEach(boardEl => boardEl.addEventListener("click", makeAlert));
for ( var i = 0; i<boardEls.length; i++){
boardEls[i].removeAttribute("data-number");
boardEls[i].addEventListener("click", makeAlert);
}
var startDraw = document.querySelector(".startDraw");
if(startDraw === null){ // you have to prevent creating the button if it is already there!
createButtonForMachineDraw();
} else {
return;
}
}
}
return drawnNums;
}
drawNumbers();
function makeAlert() {
var alertBox = document.createElement("div");
board.appendChild(alertBox);
alertBox.classList.add("alertBox");
alertBox.textContent = "You can only choose 6!";
setTimeout(function() {
alertBox.parentNode.removeChild(alertBox);
}, 1500);
}
function machineDraw(){
for( var i =0; i<6; i++){
var idx = Math.floor(Math.random() * numbers.length)
chosenByMachine.push(numbers[idx]);
/*a very important line of code which prevents machine from drawing the same number again
*/
numbers.splice(idx,1);
console.log(numbers)
/*this line of code allows to check if numbers are taken out*/
}
var btnToRemove = document.querySelector(".startDraw");
btnToRemove.classList.add("invisible");
/* why not remove it entirely? because it might then be accidentally created if for some reason you happen to try to click on board!!! and you may do that*/
return chosenByMachine;
}
//machineDraw();
function createButtonForMachineDraw(){
var startDraw = document.createElement("button");
startDraw.classList.add("startDraw");
section.appendChild(startDraw);
startDraw.textContent ="Release the balls";
startDraw.addEventListener("click", machineDraw);
startDraw.addEventListener("click", compareArrays);
}
function compareArrays(){
for( var i =0; i<balls.length; i++) {
balls[i].textContent = chosenByMachine[i];
(function() {
var j = i;
var f = function(){
balls[j].classList.remove("invisible");
balls[j].classList.add("ball-align");
}
setTimeout(f, 1000*(j+1));
})();
}
var common =[];
var arr1 = chosenByMachine;
var arr2 = drawnNums;
for(var i = 0; i<arr1.length; i++){
for(var j= 0; j<arr2.length; j++){
if(arr1[i]===arr2[j]){
common.push(arr1[i]);
}
}
}
console.log(arr1, arr2, common); /* you can monitor your arrays in console*/
function generateResult(){
// Deduction of coins once draw started
coins = coins - 1;
// End of comment
var resultsBoard = document.createElement("article");
section.appendChild(resultsBoard);
var paragraph = document.createElement("p");
resultsBoard.appendChild(paragraph);
resultsBoard.classList.add("resultsBoard");
resultsBoard.classList.add("invisible");
if(common.length === 0){
paragraph.textContent ="Oh no! You got " + common.length + " Star!";
starsEarned = stars + common.length;
return starsEarned;
} else if(common.length === 1){
paragraph.textContent ="You got " + common.length + " Star!";
starsEarned = stars + common.length;
return starsEarned;
} else if(common.length === 2){
paragraph.textContent ="You got " + common.length + " Stars!";
starsEarned = stars + common.length;
return starsEarned;
} else if(common.length === 3) {
paragraph.textContent ="You got " + common.length + " Stars!";
starsEarned = stars + common.length;
return starsEarned;
} else if(common.length === 4){
paragraph.textContent ="You got " + common.length + " Stars!";
starsEarned = stars + common.length;
return ststarsEarnedars;
} else if(common.length === 5){
paragraph.textContent ="You got " + common.length + " Stars!";
starsEarned = stars + common.length;
return starsEarned;
}
else if(common.length===6){
paragraph.textContent ="A true winner! You got " + common.length + " Stars!";
stars = stars + common.length;
return starsEarned;
}
// Returning of new coins
return coins;
// End of comment
}
setTimeout(function() {
makeComebackBtn();
document.querySelector(".resultsBoard").classList.remove("invisible"); //well, you cannot acces this outside the code
// Displaying of new scores
stars = stars + starsEarned;
document.getElementById("coin-count").innerHTML = coins;
document.getElementById("star-count").innerHTML = stars;
// End of comment
}, 8000);
generateResult();
}
function makeComebackBtn(){
var comebackBtn = document.createElement("a");
comebackBtn.classList.add("comebackBtn");
section.appendChild(comebackBtn);
comebackBtn.textContent ="Go again"
comebackBtn.setAttribute("href", "index.html");
}
})
body {
padding: 0 15%;
}
.game-container {
height: auto;
background-color:#424B54;
font-family: "Allerta", sans-serif;
margin: 0;
max-width: 425px;
height: 750px;
/* padding: 0 2%; */
box-sizing: border-box;
}
section {
margin: 0 auto;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: auto;
padding-bottom: 15px;
}
h1,
p {
width: 100%;
text-align: center;
color: #FF6663;
text-shadow: 3px 3px #A20202;
font-family: "Bungee", cursive;
}
h1 {
font-size: 35px;
margin: 0;
}
p {
font-size: 30px;
margin: 0;
}
h3 {
color: #FF6663;
text-align: center;
text-shadow: 2px 2px #A20202;
font-size: 25px;
margin-bottom: 5px;
}
article {
height: 90%;
width: 250px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin-bottom: 1rem;
}
.scores {
width: 100%;
}
.coins,
.stars{
display: flex;
align-items: center;
gap: .5rem;
}
.score-icons {
color: #F6BD60;
font-size: 3rem;
}
.scores span {
color: white;
}
#star-count,
#coin-count{
font-size: 1.5 rem;
}
.invisible {
display: none;
}
.ball-align {
display: flex;
justify-content: center;
align-items: center;
}
.board {
position: relative;
background-color: #FF6663;
width: 13.125rem;
height: 13.125rem;
border: 1px solid #FF6663;
display: flex;
flex-wrap: wrap;
justify-content: space-around;
align-items: center;
}
.boardEl {
background-color: #E8F7EE;
width: 28px;
height: 28px;
color: #000000;
text-align: center;
font-size: 15px;
border: none;
}
.crossedOut {
background-color: #424B54;
color: #F7EDE2;
}
.startDraw {
background: #FF6663;
border: none;
font-size: 1.3rem;
font-weight: bolder;
color: #ffffff;
padding: 0.5rem 1rem;
margin: 0 auto;
border-radius: .5rem;
padding: .5rem 1rem;
}
.ball {
width: 2rem;
height: 2rem;
border-radius: 50%;
line-height: 2;
color: #efefef;
font-weight: bold;
text-align: center;
}
.ball:nth-of-type(2n) {
align-self: flex-end;
}
.ball:nth-of-type(2n + 1) {
align-self: flex-start;
}
.ball:first-of-type {
background-color: gold;
border: 1px solid #ccac00;
}
.ball:nth-of-type(2) {
background-color: hotpink;
border: 1px solid #ff369b;
}
.ball:nth-of-type(3) {
background-color: teal;
border: 1px solid #004d4d;
}
.ball:nth-of-type(4) {
background-color: #009900;
border: 1px solid #006600;
}
.ball:nth-of-type(5) {
background-color: #339999;
border: 1px solid #267373;
}
.ball:last-of-type {
background-color: #ff6633;
border: 1px solid #ff4000;
}
#ballContainer {
background-color: inherit;
border-bottom: none;
display: flex;
align-items: center;
gap: 0.1rem;
}
.resultsBoard {
margin-top: .5rem;
text-align: center;
width: 100%;
}
.resultsBoard p {
color: #F6BD60;
font-size: 2rem;
font-family: "Allerta", sans-serif;
text-shadow: none;
}
.comebackBtn {
line-height: 2;
margin-top: 2rem;
font-size: 1.3rem;
text-align: center;
background-color: #FF6663;
text-decoration: none;
color: #ffffff;
padding: .3rem 1rem;
border-radius: .3rem;
text-transform: uppercase;
}
.alertBox {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 3;
color: #ffffff;
background-color: #FF6663;
text-align: center;
line-height: 210px;
}
<!DOCTYPE html>
<html lang="eng-ENG">
<head>
<meta charset="UTF-8">
<title>lotto</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" type="text/css" href="style.css">
<link href="https://fonts.googleapis.com/css?family=Allerta|Bungee" rel="stylesheet">
<link href='https://unpkg.com/boxicons#2.1.2/css/boxicons.min.css' rel='stylesheet'>
</head>
<body>
<main>
<div class="game-container">
<section>
<h1>Lottery</h1>
<div class="scores">
<div class="coins">
<i class='score-icons bx bxs-star'></i><span id="star-count"></span>
</div>
<div class="stars">
<i class='score-icons bx bx-coin'></i><span id="coin-count"></span>
</div>
</div>
<article class="lotto">
<h3>Pick 6 numbers:</h3>
</article>
<article class="balls">
<div id="ballContainer">
<div class="ball invisible"></div>
<div class="ball invisible"></div>
<div class="ball invisible"></div>
<div class="ball invisible"></div>
<div class="ball invisible"></div>
<div class="ball invisible"></div>
</div>
</article>
</section>
</div>
</main>
<script type="text/javascript" src="index.js"></script>
<!--<script type="text/javascript" src="js/app2.js"></script>-->
</body>
</html>
[UPDATED]
I've tried and modified the js script code and no need to save values in local storage. And in addition, no need to refresh page to reload game state. I implemented in JSFiddle here
var stars = 0;
var coins = 5;
var starsEarned = 0;
// For displaying current score
document.getElementById("star-count").innerHTML = stars;
document.getElementById("coin-count").innerHTML = coins;
document.addEventListener("DOMContentLoaded", function(e){
var body = document.querySelector("body");
var section = document.querySelector("section");
var articleLotto = document.querySelector(".lotto");
var articleBalls = document.querySelector(".balls");
var numbers = [];
var balls = document.getElementsByClassName("ball");
var drawnNums = [];
var chosenByMachine = [];
var board;
var boardEls;
function createNumberBoard(number){
console.log("I work");
numbers = [];
drawnNums = [];
chosenByMachine = [];
var board = document.createElement("div");
board.classList.add("board");
articleLotto.appendChild(board);
for( var i = 0; i<number; i ++){
var boardEl = document.createElement("button");
boardEl.classList.add("boardEl");
board.appendChild(boardEl);
}
var boardEls = document.getElementsByClassName("boardEl");
for( var i =0; i<boardEls.length; i++){
boardEls[i].setAttribute("data-number", i+1);
var dataNumber = boardEls[i].getAttribute("data-number");
var number = parseInt(dataNumber, 10);
numbers.push(number);
boardEls[i].textContent = number;
}
}
createNumberBoard(49);
board = document.querySelector(".board");
boardEls = document.querySelectorAll(".boardEl");
function drawNumbers(){
//boardEls.forEach(boardEl => boardEl.addEventListener("click", selectNums));
for (var i = 0; i<boardEls.length; i++){
boardEls[i].addEventListener("click", selectNums);
}
function selectNums(){
var number = parseInt(this.dataset.number, 10);
if(this.hasAttribute("data-number")){
drawnNums.push(number);
this.removeAttribute("data-number");
this.classList.add("crossedOut");
}
if(drawnNums.length=== 6){
//boardEls.forEach( boardEl => boardEl.removeAttribute("data-number"));
//boardEls.forEach(boardEl => boardEl.addEventListener("click", makeAlert));
for ( var i = 0; i<boardEls.length; i++){
boardEls[i].removeAttribute("data-number");
boardEls[i].addEventListener("click", makeAlert);
}
var startDraw = document.querySelector(".startDraw");
if(startDraw === null){ // you have to prevent creating the button if it is already there!
createButtonForMachineDraw();
} else {
return;
}
}
}
return drawnNums;
}
drawNumbers();
function makeAlert() {
var alertBox = document.createElement("div");
board.appendChild(alertBox);
alertBox.classList.add("alertBox");
alertBox.textContent = "You can only choose 6!";
setTimeout(function() {
alertBox.parentNode.removeChild(alertBox);
}, 1500);
}
function machineDraw(){
for( var i =0; i<6; i++){
var idx = Math.floor(Math.random() * numbers.length)
chosenByMachine.push(numbers[idx]);
/*a very important line of code which prevents machine from drawing the same number again
*/
numbers.splice(idx,1);
/* console.log(numbers) */
/*this line of code allows to check if numbers are taken out*/
}
var btnToRemove = document.querySelector(".startDraw");
btnToRemove.classList.add("invisible");
/* why not remove it entirely? because it might then be accidentally created if for some reason you happen to try to click on board!!! and you may do that*/
return chosenByMachine;
}
//machineDraw();
function createButtonForMachineDraw(){
var startDraw = document.createElement("button");
startDraw.classList.add("startDraw");
section.appendChild(startDraw);
startDraw.textContent ="Release the balls";
startDraw.addEventListener("click", machineDraw);
startDraw.addEventListener("click", compareArrays);
}
function compareArrays(){
for( var i =0; i<balls.length; i++) {
balls[i].textContent = chosenByMachine[i];
(function() {
var j = i;
var f = function(){
balls[j].classList.remove("invisible");
balls[j].classList.add("ball-align");
}
setTimeout(f, 1000*(j+1));
})();
}
var common =[];
var arr1 = chosenByMachine;
var arr2 = drawnNums;
for(var i = 0; i<arr1.length; i++){
for(var j= 0; j<arr2.length; j++){
if(arr1[i]===arr2[j]){
common.push(arr1[i]);
}
}
}
/* console.log(arr1, arr2, common) */; /* you can monitor your arrays in console*/
function generateResult(){
// Deduction of coins once draw started
coins = coins - 1;
// End of comment
var resultsBoard = document.createElement("article");
section.appendChild(resultsBoard);
var paragraph = document.createElement("p");
resultsBoard.appendChild(paragraph);
resultsBoard.classList.add("resultsBoard");
resultsBoard.classList.add("invisible");
if(common.length === 0){
paragraph.textContent ="Oh no! You got " + common.length + " Star!";
starsEarned = stars + common.length;
return starsEarned;
} else if(common.length === 1){
paragraph.textContent ="You got " + common.length + " Star!";
starsEarned = stars + common.length;
return starsEarned;
} else if(common.length === 2){
paragraph.textContent ="You got " + common.length + " Stars!";
starsEarned = stars + common.length;
return starsEarned;
} else if(common.length === 3) {
paragraph.textContent ="You got " + common.length + " Stars!";
starsEarned = stars + common.length;
return starsEarned;
} else if(common.length === 4){
paragraph.textContent ="You got " + common.length + " Stars!";
starsEarned = stars + common.length;
return ststarsEarnedars;
} else if(common.length === 5){
paragraph.textContent ="You got " + common.length + " Stars!";
starsEarned = stars + common.length;
return starsEarned;
}
else if(common.length===6){
paragraph.textContent ="A true winner! You got " + common.length + " Stars!";
stars = stars + common.length;
return starsEarned;
}
// Returning of new coins
return coins;
// End of comment
}
setTimeout(function() {
makeComebackBtn();
document.querySelector(".resultsBoard").classList.remove("invisible"); //well, you cannot acces this outside the code
// Displaying of new scores
stars = starsEarned;
document.getElementById("coin-count").innerHTML = coins;
document.getElementById("star-count").innerHTML = stars;
// End of comment
}, 8000);
generateResult();
}
function makeComebackBtn(){
var comebackBtn = document.createElement("a");
comebackBtn.classList.add("comebackBtn");
section.appendChild(comebackBtn);
comebackBtn.textContent ="Go again";
if (coins === 0) {
comebackBtn.addEventListener("click", function () {
alert("You ran out of coins");
});
} else {
comebackBtn.addEventListener("click", function () {
const board_ = document.querySelector(".board");
board_.parentNode.removeChild(board_);
const startDraw_ = document.querySelector(".startDraw");
startDraw_.parentNode.removeChild(startDraw_);
for( let i=0; i<balls.length; i++) {
balls[i].classList.add("invisible");
balls[i].classList.remove("ball-align");
}
const resultsBoard_ = document.querySelector(".resultsBoard");
resultsBoard_.parentNode.removeChild(resultsBoard_);
createNumberBoard(49);
board = document.querySelector(".board");
boardEls = document.querySelectorAll(".boardEl");
drawNumbers();
const comebackBtn_ = document.querySelector(".comebackBtn");
comebackBtn_.parentNode.removeChild(comebackBtn_);
});
}
}
})
You need to save the values of stars and coins to localStorage using localStorage.setItem("stars", stars) or sessionStorage and get the values again at beginning of each turn.
Also initialize value of variable starsEarned = 0 and check condition of number of coins when reload the page. If run out of coins, alert player.
You can see following code I modified from your
// Declaration of scores and lives
var stars = Number(localStorage.getItem("stars")) || 0;
var coins = Number(localStorage.getItem("coins")) || 5;
// End of comment
// For redeclaration in innerHTML
var starsEarned = 0;
// End of comment
// For displaying current score
document.getElementById("star-count").innerHTML = stars;
document.getElementById("coin-count").innerHTML = coins;
// End of comment
document.addEventListener("DOMContentLoaded", function(e){
var body = document.querySelector("body");
var section = document.querySelector("section");
var articleLotto = document.querySelector(".lotto");
var articleBalls = document.querySelector(".balls");
var numbers = [];
var balls = document.getElementsByClassName("ball");
var drawnNums = [];
var chosenByMachine = [];
function createNumberBoard(number){
console.log("I work");
var board = document.createElement("div");
board.classList.add("board");
articleLotto.appendChild(board);
for( var i = 0; i<number; i ++){
var boardEl = document.createElement("button");
boardEl.classList.add("boardEl");
board.appendChild(boardEl);
}
var boardEls = document.getElementsByClassName("boardEl");
for( var i =0; i<boardEls.length; i++){
boardEls[i].setAttribute("data-number", i+1);
var dataNumber = boardEls[i].getAttribute("data-number");
var number = parseInt(dataNumber, 10);
numbers.push(number);
boardEls[i].textContent = number;
}
}
createNumberBoard(49);
var board = document.querySelector(".board");
var boardEls = document.querySelectorAll(".boardEl");
function drawNumbers(){
//boardEls.forEach(boardEl => boardEl.addEventListener("click", selectNums));
for (var i = 0; i<boardEls.length; i++){
boardEls[i].addEventListener("click", selectNums);
}
function selectNums(){
var number = parseInt(this.dataset.number, 10);
if(this.hasAttribute("data-number")){
drawnNums.push(number);
this.removeAttribute("data-number");
this.classList.add("crossedOut");
}
if(drawnNums.length=== 6){
//boardEls.forEach( boardEl => boardEl.removeAttribute("data-number"));
//boardEls.forEach(boardEl => boardEl.addEventListener("click", makeAlert));
for ( var i = 0; i<boardEls.length; i++){
boardEls[i].removeAttribute("data-number");
boardEls[i].addEventListener("click", makeAlert);
}
var startDraw = document.querySelector(".startDraw");
if(startDraw === null){ // you have to prevent creating the button if it is already there!
createButtonForMachineDraw();
} else {
return;
}
}
}
return drawnNums;
}
drawNumbers();
function makeAlert() {
var alertBox = document.createElement("div");
board.appendChild(alertBox);
alertBox.classList.add("alertBox");
alertBox.textContent = "You can only choose 6!";
setTimeout(function() {
alertBox.parentNode.removeChild(alertBox);
}, 1500);
}
function machineDraw(){
for( var i =0; i<6; i++){
var idx = Math.floor(Math.random() * numbers.length)
chosenByMachine.push(numbers[idx]);
/*a very important line of code which prevents machine from drawing the same number again
*/
numbers.splice(idx,1);
/* console.log(numbers) */
/*this line of code allows to check if numbers are taken out*/
}
var btnToRemove = document.querySelector(".startDraw");
btnToRemove.classList.add("invisible");
/* why not remove it entirely? because it might then be accidentally created if for some reason you happen to try to click on board!!! and you may do that*/
return chosenByMachine;
}
//machineDraw();
function createButtonForMachineDraw(){
var startDraw = document.createElement("button");
startDraw.classList.add("startDraw");
section.appendChild(startDraw);
startDraw.textContent ="Release the balls";
startDraw.addEventListener("click", machineDraw);
startDraw.addEventListener("click", compareArrays);
}
function compareArrays(){
for( var i =0; i<balls.length; i++) {
balls[i].textContent = chosenByMachine[i];
(function() {
var j = i;
var f = function(){
balls[j].classList.remove("invisible");
balls[j].classList.add("ball-align");
}
setTimeout(f, 1000*(j+1));
})();
}
var common =[];
var arr1 = chosenByMachine;
var arr2 = drawnNums;
for(var i = 0; i<arr1.length; i++){
for(var j= 0; j<arr2.length; j++){
if(arr1[i]===arr2[j]){
common.push(arr1[i]);
}
}
}
/* console.log(arr1, arr2, common) */; /* you can monitor your arrays in console*/
function generateResult(){
// Deduction of coins once draw started
coins = coins - 1;
// End of comment
var resultsBoard = document.createElement("article");
section.appendChild(resultsBoard);
var paragraph = document.createElement("p");
resultsBoard.appendChild(paragraph);
resultsBoard.classList.add("resultsBoard");
resultsBoard.classList.add("invisible");
if(common.length === 0){
paragraph.textContent ="Oh no! You got " + common.length + " Star!";
starsEarned = stars + common.length;
return starsEarned;
} else if(common.length === 1){
paragraph.textContent ="You got " + common.length + " Star!";
starsEarned = stars + common.length;
return starsEarned;
} else if(common.length === 2){
paragraph.textContent ="You got " + common.length + " Stars!";
starsEarned = stars + common.length;
return starsEarned;
} else if(common.length === 3) {
paragraph.textContent ="You got " + common.length + " Stars!";
starsEarned = stars + common.length;
return starsEarned;
} else if(common.length === 4){
paragraph.textContent ="You got " + common.length + " Stars!";
starsEarned = stars + common.length;
return ststarsEarnedars;
} else if(common.length === 5){
paragraph.textContent ="You got " + common.length + " Stars!";
starsEarned = stars + common.length;
return starsEarned;
}
else if(common.length===6){
paragraph.textContent ="A true winner! You got " + common.length + " Stars!";
stars = stars + common.length;
return starsEarned;
}
// Returning of new coins
return coins;
// End of comment
}
setTimeout(function() {
makeComebackBtn();
document.querySelector(".resultsBoard").classList.remove("invisible"); //well, you cannot acces this outside the code
// Displaying of new scores
stars = starsEarned;
document.getElementById("coin-count").innerHTML = coins;
document.getElementById("star-count").innerHTML = stars;
localStorage.setItem("stars", stars);
localStorage.setItem("coins", coins);
// End of comment
}, 8000);
generateResult();
}
function makeComebackBtn(){
var comebackBtn = document.createElement("a");
comebackBtn.classList.add("comebackBtn");
section.appendChild(comebackBtn);
comebackBtn.textContent ="Go again";
if (coins === 0) {
comebackBtn.setAttribute("onClick", "alert('You ran out of coins')");
} else {
comebackBtn.setAttribute("onClick", "window.location.reload();");
}
}
})

How do I fix my canvas width and height changing every render when I zoom in or out?

I'm developing a sorting visualization website with my friend (https://tann1.github.io/SortingViz/). We both are fairly new to web development. We're currently facing this issue where if you were to zoom in and press play the canvas window shrinks every render or so. Similarly, if you zoom out it'll expand every render or so as well. You can test this bug out through the GitHub link I attached earlier. There's is definitely something that we're missing and we can't seem to identify it. Some help would be very appreciated.
// JS script goes here
let form = document.querySelector('#nBarsInput'); //# = reference to ID
let sortType = document.querySelector('#algoSelect');
let canvas = document.querySelector('.zdog-canvas');
let ctx = canvas.getContext('2d');
let nTextBox = document.getElementById('nTextBox');
let nSubmitButton = document.getElementById('nSubmit');
let replayButton = document.getElementById("replayButton");
let nVal = []; //stores the array that needs to be sorted
let nValUnsorted = [];
let sortSpeed = document.getElementById('sortSpeed');
let unsorted = '#fc90a9'//'#c9751a'
let sorted = '#c94260'
let sorting = '#83BCFF'
//let swapColor = 'red'
nSubmitButton.addEventListener('click', () => { // generate graph when submit button is clicked
generateNums(nTextBox.value);
startSort();
});
function enableInput() {
nSubmitButton.disabled = false;
replayButton.disabled = false;
nTextBox.disabled = false;
sortSpeed.disabled = false;
}
function disableInput() {
nSubmitButton.disabled = true;
replayButton.disabled = true;
nTextBox.disabled = true;
sortSpeed.disabled = true;
}
replayButton.addEventListener('click', () => {
nVal = [...nValUnsorted.map(arr => [...arr])];
//console.log(nValUnsorted);
startSort();
});
async function startSort() {
createCanvas(nTextBox.value);
disableInput();
switch (sortType.value) {
case 'Selection Sort': {
await selectionSort();
break;
}
case 'Insertion Sort': {
await insertionSort();
break;
}
default: {
alert("Invalid sort type");
break;
}
}
enableInput();
}
function generateNums(size) { // generates array of size ntextBox.value, populated with random numbers
nVal = []; // reset array
nValUnsorted = [];
for (let i = 0; i < size; ++i){
let randVal = Math.floor((Math.random() * 0.55 * canvas.height) + (0.40 * canvas.height));
nVal.push([randVal, unsorted]);
nValUnsorted.push([randVal, unsorted]);
}
//copy();
}
// do drawing stuff based on given array and N value
function createCanvas(size) {
let barWidth = (canvas.width/size);
let illo = new Zdog.Illustration({ //main parent object of
element: ".zdog-canvas",
centered: false
});
let rect = new Zdog.Rect({ // create first bar
addTo: illo,
width: barWidth * 0.95,
height: nVal[0][0],
color: nVal[0][1],
fill: true,
stroke: 4,
translate: {y: canvas.height - nVal[0][0] / 2, x : barWidth/2}});
illo.updateRenderGraph();
for(let i = 1; i < size; ++i){ // create rest of the bars by copying the first bar
rect.copy({
height: nVal[i][0],
color: nVal[i][1],
translate: {y: canvas.height - nVal[i][0]/ 2, x : (i * barWidth) + barWidth/2}
});
//nArr.push(rect);
illo.updateRenderGraph();
/* text setup*/
}
for (let i = 0; i < size; ++i) {
ctx.fillStyle = 'white';
ctx.textAlign = 'center';
ctx.font = (0.50 * barWidth) + 'px monospace';
ctx.fillText(nVal[i][0], (i * barWidth) + (barWidth/2), 490);
}
}
async function selectionSort() {
let curr_pos = 0; // find the position with the lowest current value
let temp = 0; // need it for the swap
for (let i = 0; i < nTextBox.value - 1; ++i) {
curr_pos = i;
nVal[i][1] = sorting;
await pause(sortSpeed.value);
createCanvas(nTextBox.value);
for (let j = curr_pos + 1; j < nTextBox.value; ++j) { // finds the lowest relative value in the array
if (nVal[j][0] < nVal[curr_pos][0])
curr_pos = j;
}
//swap
temp = nVal[i];
nVal[i] = nVal[curr_pos];
nVal[curr_pos] = temp;
nVal[curr_pos][1] = sorting;
await pause(sortSpeed.value);
createCanvas(nTextBox.value);
nVal[i][1] = sorted;
if (i != curr_pos) {
nVal[curr_pos][1] = unsorted;
}
await pause(sortSpeed.value);
createCanvas(nTextBox.value);
}
nVal[nTextBox.value - 1][1] = sorted;
createCanvas(nTextBox.value);
//console.log(nVal);
return new Promise((resolve, reject) => {resolve('');})
}
async function insertionSort() {
let val, j;
nVal[0][1] = sorted;
for (let i = 1; i < nTextBox.value; i++) {
val = nVal[i]; // current value to sort
val[1] = sorting; //nVal[i][1] = sorting;
await pause(sortSpeed.value);
createCanvas(nTextBox.value);
j = i; // only loop through "sorted" portion
while (j > 0 && val[0] < nVal[j-1][0]) { // keep going through sorted portion until correct position is found
nVal[j] = nVal[j-1]; // shift other values up
nVal[j][1] = sorted;
nVal[j-1] = val;
j--;
await pause(sortSpeed.value);
createCanvas(nTextBox.value);
}
nVal[j][1] = sorted;
await pause(sortSpeed.value);
createCanvas(nTextBox.value);
}
return new Promise((resolve) => {resolve('');})
}
function pause(ms) { // pause execution for a given duration in milliseconds
return new Promise(resolve => {
setTimeout(() => { resolve('') }, ms * 1000);
})
}
/* CSS goes here */
:root {
--pageColor: #FDB;
--buttonColor: #c94260;
--LabelColor: rgba(58, 44, 48, 0.959);
--textColor: #c94260;
--disabledColor: #fc90a9;
}
body {
background-color: var(--pageColor);
font-family: 'Patrick Hand', cursive;
font-size: 25px;
}
h1 {
font-family: 'Source Code Pro', monospace;
font-size: 60px;
color: white;
text-shadow: 2px 5px var(--textColor);
}
#nBarsInput {
display: flex;
flex-direction: column;
align-items: center;
border: 5px solid var(--buttonColor);
border-radius: 15px;
padding: 30px;
width: 1100px;
}
#textInput {
width: 980px;
display: flex;
justify-content: space-between;
}
.button {
background-color: var(--buttonColor);
border-radius: 3px;
border: none;
color: white;
padding: 5px 25px;
margin: 20px 5px;
font-family: 'Inter', sans-serif;
box-shadow: 0 5px #999;
}
.button:hover {
background-color: #83BCFF;
}
.button:active {
background-color: #83BCFF;
box-shadow: none;
transform: translateY(5px);
}
.button:disabled {
background-color: var(--disabledColor);
}
.inputTextBox {
padding: 5px 15px;
border: none;
border-radius: 25px;
}
.inputTextBox:focus {
outline: none;
}
.inputTextBox:disabled
{
}
label {
color: var(---LabelColor);
margin: 5px 15px;
}
main {
display: flex;
flex-direction: column;
align-items: center;
}
.zdog-canvas {
background: var(--pageColor); /*#843b62;*/
flex-basis: 100%;
}
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="./style.css">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Caveat:wght#600&family=Patrick+Hand&family=Source+Code+Pro&display=swap" rel="stylesheet">
</head>
<body>
<main>
<h1>SORTING ALGORITHM VISUALIZER</h1>
<div id="nBarsInput">
<div id="textInput">
<div>
<label>Enter a sorting speed (in seconds)</label>
<input type="text" name="sortSpeed" id='sortSpeed' value="0.25" class='inputTextBox'>
</div>
<div>
<label>Enter a Value for N</label>
<input type="text" name="nBars" id='nTextBox' value="5" class='inputTextBox' required>
</div>
</div>
<div id="nBarsButton">
<select id='algoSelect' class='button'>
<option value='Selection Sort'>Selection Sort</option>
<option value='Insertion Sort'>Insertion Sort</option>
</select>
<input type="submit" value="Go" id='nSubmit' class='button'>
<input type="submit" value="Replay" id='replayButton' class='button'>
</div>
</div>
<canvas class='zdog-canvas' width='1000' height='500'></canvas>
</main>
<script src="./script.js" type="module"></script>
<script src="https://unpkg.com/zdog#1/dist/zdog.dist.min.js"></script>
</body>
</html>

How to sort elements on DOM by its inner Text

I have a graph that is rendering its values as a div inside the body element with a class according to their number values. This is working fine. But next I need to sort the divs according to their number values or background color. BUT, it needs to start on the lower left corner of the page and fan out upwards to towards the right as the numbers increase. Basically just like a line graph.
I'd like to stay away from libraries if at all possible.
How would I approach this? Thank you all.
let interval = setInterval(makeDivs, 5);
function makeDivs(){
let cont = checkHeight();
if(cont){
let div = document.createElement('div');
let randNum = Math.random() * 100;
if(randNum < 20) { div.classList.add('blue') }
if(randNum >= 20 && randNum < 40) { div.classList.add('green') }
if(randNum >= 40 && randNum < 60) { div.classList.add('yellow') }
if(randNum >= 60 && randNum < 80) { div.classList.add('orange') }
if(randNum >= 80 && randNum < 101) { div.classList.add('red') }
div.textContent = randNum.toFixed(2);
document.querySelector('body').appendChild(div);
} else {
alert('done');
clearInterval(interval);
sortDivs(); // Begin sorting divs
}
}
function checkHeight(){
let w = window.innerHeight;
let b = document.querySelector('body').offsetHeight;
if(b < w) {
return true;
} else {
return false;
}
}
function sortDivs(){
document.querySelector("body div:last-child").remove();
alert('sorting now...')
}
* { box-sizing: border-box;}
body { width: 100vw; margin: 0; padding: 0; display: flex; flex-wrap: wrap; align-items: end;}
body div { width: calc(10% + 1px); text-align: center; border: 1px solid #ddd; margin: -1px 0 0 -1px; padding: 10px;}
body div.blue { background: aqua; }
body div.green { background: green; }
body div.yellow { background: yellow; }
body div.orange { background: orange; }
body div.red { background: red; }
UPDATE!!!
So I have this so far based on the feed back down below. The problem now is the sorting is only happening laterally and not on an angle (spreading right and to the top).
let interval = setInterval(makeDivs, 10);
function makeDivs(){
let cont = checkHeight();
if(cont){
let div = document.createElement('div');
let randNum = Math.random() * 100;
if(randNum < 20) { div.classList.add('blue') }
if(randNum >= 20 && randNum < 40) { div.classList.add('green') }
if(randNum >= 40 && randNum < 60) { div.classList.add('yellow') }
if(randNum >= 60 && randNum < 80) { div.classList.add('orange') }
if(randNum >= 80 && randNum < 101) { div.classList.add('red') }
div.textContent = randNum.toFixed(2);
document.querySelector('.outPut').appendChild(div);
} else {
clearInterval(interval);
document.querySelector(".outPut div:last-child").remove();
compileArrays(); // Begin sorting divs
}
}
function checkHeight(){
let w = window.innerHeight;
let b = document.querySelector('.outPut').offsetHeight;
if(b < w) {
return true;
} else {
return false;
}
}
function compileArrays(){
let divs = document.querySelectorAll('.outPut div');
let bArr = [], gArr = [], yArr = [], oArr = [], rArr = [];
divs.forEach( (d) => {
if( d.classList.contains('blue') ){ bArr.push(d) }
if( d.classList.contains('green') ){ gArr.push(d) }
if( d.classList.contains('yellow') ){ yArr.push(d) }
if( d.classList.contains('orange') ){ oArr.push(d) }
if( d.classList.contains('red') ){ rArr.push(d) }
});
let finalArr = sortArray(bArr).concat(sortArray(gArr)).concat(sortArray(yArr)).concat(sortArray(oArr)).concat(sortArray(rArr));
newDom(finalArr);
}
function sortArray(arr){
let newArr = arr;
newArr.sort( (a, b) => {
return a.innerText - b.innerText;
});
return newArr;
}
function newDom(arr){
let b = document.querySelector('.outPut');
b.innerHTML = '';
arr.reverse();
arr.forEach((a) => {
b.appendChild(a);
});
}
* { box-sizing: border-box;}
body { width: 100vw; height: 100vh; margin: 0; padding: 0; display: flex; align-items: flex-end;}
body .outPut { flex: 1; display: flex; flex-wrap: wrap; flex-direction:row-reverse; }
body .outPut div { width: calc(10% + 1px); text-align: center; border: 1px solid #ddd; margin: -1px 0 0 -1px; padding: 10px;}
body .outPut div.blue { background: aqua; }
body .outPut div.green { background: #44df15; }
body .outPut div.yellow { background: yellow; }
body .outPut div.orange { background: orange; }
body .outPut div.red { background: red; }
<div class="outPut"></div>
Supposed you already have a mechanism to organise such DIVs in a grid as shown, the following should give you what you are looking for:
var items = divList.filter((div) => div.nodeType == 1); // get rid of the whitespace text nodes
items.sort(function(a, b) {
return a.innerHTML == b.innerHTML
? 0
: (a.innerHTML > b.innerHTML ? 1 : -1);
});
Then, place them back in the DOM as needed, example:
for (i = 0; i < items.length; ++i) {
divList.appendChild(items[i]);
}
This worked with the first code example!!!
try this sortDivs function:
function sortDivs() {
document.querySelector("body div:last-child").remove();
alert('sorting now...')
let toSort = document.getElementsByTagName("div")
toSort = Array.prototype.slice.call(toSort, 0)
toSort.sort((a, b) => {
let aord = parseFloat(a.textContent);
let bord = parseFloat(b.textContent);
return bord - aord;
})
document.body.innerHTML = ""
for(var i = 0, l = toSort.length; i < l; i++) {
document.querySelector('body').appendChild(toSort[i]);
}
}
and in the css file set flex-wrap to wrap-reverse. Hope I could help :)
PS: please, implement some else if instead of doing only if
Here is a small fiddle with my sample code demonstrating a simple solution in pure JavaScript and absolute CSS positioning for what you are trying to achieve. Link
As some pointed out already, there might be a library, that already provides a better and complete solution for this - I did not research if it is so.
Code:
file.js
var container = document.getElementById("container")
var results = [1,2,3,4,5,6,7,8]
//you can pre-calculate the order of the distances
//here already orderdered array [distanec][X-axis][Y-axis]
var distances =[[0,0,0],
[1,1,0],
[1,0,1],
[1.414, 1,1],
[2,0,2],
[2,2,0],
[2.234, 2,1],
[2.234, 1,2]]
for (i = 0; i < results.length; i++){
var newDiv = document.createElement("div")
newDiv.className = "result"
newDiv.innerHTML = results[i]
newDiv.style.left = distances[i][1]*20 + "px"
newDiv.style.bottom = distances[i][2]*20 + "px"
container.appendChild(newDiv)
}
function setColor(element){
// set class based on value - you already have this part
}
style.css
#container {
border: 4px;
border-color: red;
border-style: solid;
height: 200px;
width: 200px;
position: relative;
}
.result{
border: 2px;
width: 20px;
height: 20px;
position: absolute;
border-color: blue;
border-style: solid;
text-align: center;
}
site.html
<div id="container">
</div>
Output:

JavaScript Boolean is not working and looping goes wrong

The demo created is to illustrate the working of the logic described below -
I have created 4x4 tiles using JavaScript instead of hard coding into html
Code highlight the columns one by one in a infinite loop which is achieved by setInterval(colScan,1000)
When user press the mouse on html body, it changes the column scan --> row scan in the selected column which is also achieved by setInterval(rowScan,1000)
When user clicks again on the html body, it changes the row scan --> col scan
Problem:
No matter what, colScan is always activated which you can see in the console log that the column is always increasing.
When user clicks the second time it doesn't reset to column scan.
part of the code where I think the problem is occurring
createtiles();
var k = 0,
m = 0,
selected_col = "",
mousePressed = false,
col_scan = true,
row_scan = false;
scanSelector();
function scanSelector() {
if (col_scan) {
setInterval(colScan, 1000);
} else if (row_scan) {
setInterval(rowScan, 1000);
}
}
document.body.onmousedown = function() {
mousePressed = true;
}
function colScan() {
if (k > 2) k = 0;
else k++;
console.log("col " + k);
var col = ".j_" + k;
$(".tiles").removeClass('highlighter');
$(col).addClass('highlighter');
if (mousePressed) {
mousePressed = false;
col_scan = false;
row_scan = true;
selected_col = col;
scanSelector();
}
}
function rowScan() {
if (m > 2) m = 0;
else m++;
console.log("row " + m);
var row = selected_col + (".i_" + m);
$(".tiles").removeClass('highlighter');
$(row).addClass('highlighter');
if (mousePressed) {
mousePressed = false;
col_scan = true;
row_scan = false;
selected_col = "";
scanSelector();
}
}
function createtiles() {
for (var i = 0; i < 4; i++) {
for (var j = 0; j < 4; j++) {
var divTile = $('<div>', {
class: 'tiles ' + ("j_" + j) + " " + ("i_" + i)
});
divTile.appendTo('.comtile');
}
}
}
DEMO -> https://codepen.io/xblack/pen/BdGzYx
createtiles();
var k = 0,
m = 0,
selected_col = "",
mousePressed = false,
col_scan = true,
row_scan = false;
scanSelector();
function scanSelector() {
if (col_scan) {
setInterval(colScan, 1000);
} else if (row_scan) {
setInterval(rowScan, 1000);
}
}
document.body.onmousedown = function() {
mousePressed = true;
}
function colScan() {
if (k > 2) k = 0;
else k++;
console.log("col " + k);
var col = ".j_" + k;
$(".tiles").removeClass('highlighter');
$(col).addClass('highlighter');
if (mousePressed) {
mousePressed = false;
col_scan = false;
row_scan = true;
selected_col = col;
scanSelector();
}
}
function rowScan() {
if (m > 2) m = 0;
else m++;
console.log("row " + m);
var row = selected_col + (".i_" + m);
$(".tiles").removeClass('highlighter');
$(row).addClass('highlighter');
if (mousePressed) {
mousePressed = false;
col_scan = true;
row_scan = false;
selected_col = "";
scanSelector();
}
}
function createtiles() {
for (var i = 0; i < 4; i++) {
for (var j = 0; j < 4; j++) {
var divTile = $('<div>', {
class: 'tiles ' + ("j_" + j) + " " + ("i_" + i)
});
divTile.appendTo('.comtile');
}
}
}
html,
body {
margin: 0px;
padding: 0px;
height: 100%;
min-height: 100%;
overflow: hidden;
font-family: 'Roboto', sans-serif;
background: white;
}
* {
box-sizing: border-box!important;
}
.conatiner {
display: grid;
grid-template-columns: 1fr;
grid-template-rows: 1fr;
grid-template-area: "menu" "comContent";
}
.menu {
grid-area: menu;
height: 5vh;
padding: 2vh;
}
.comtile {
grid-area: comContent;
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-template-rows: auto;
grid-gap: 0.5vh;
height: 95vh;
padding: 2vh;
}
.tiles {
background: #F7F7F7;
border-radius: 0.4vh;
border: 1px solid #EEEBEB;
}
.highlighter {
box-shadow: 0 0 4px rgba(0, 0, 0, 0.15);
transition: box-shadow 0.3s cubic-bezier(0.38, -0.76, 0, 1.69);
border: 1px solid silver;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<div class="container">
<div class="menu">MAIN MENU</div>
<div class="comtile"></div>
</div>
You need to make the following changes:
Replace setInterval with setTimeout for reasons stated by #ASDFGerte.
function scanSelector() {
if (col_scan) {
// Replace setInterval with setTimeout
setTimeout(colScan, 1000);
} else i f (row_scan) {
// Replace setInterval with setTimeout
setTimeout(rowScan, 1000);
}
}
Move the scanSelector() lines in rowScan and colScan. The change is the same for both methods, I will only show the change in rowScan.
function rowScan() {
if (m > 2) m = 0;
else m++;
console.log("row " + m);
var row = selected_col + (".i_" + m);
$(".tiles").removeClass('highlighter');
$(row).addClass('highlighter');
if (mousePressed) {
mousePressed = false;
col_scan = true;
row_scan = false;
selected_col = "";
// Remove this line
// scanSelector();
}
// Because you're no longer using setInterval you need to call
// this method after each timeout.
scanSelector();
}
Every time you were calling scanSelector() it would create another interval. The initial interval will highlight the columns, after the first click you have two intervals running side-by-side: the original interval and an interval to highlight rows. After each click you're only adding intervals.
You could store the interval ID, the result of setInterval and clear this interval when you change from column to row highlight and vice versa. The easier solution is moving from setInterval to setTimeout as outlined in the changed I've shown you above.

javascript settimeout takes too much time than expected to excute the code

I have tried to make the code works fine in stackoverflow but when I added the javascript code, it has stopped.any way
I have this code:
showCharacters("hey, how are you");
function showCharacters(text) {
var charactersArray = new String(text).split('');
var i;
for (i = 0; i < charactersArray.length; i++) {
setCharacter(i, charactersArray);
}
deleteAll(i - 1);
}
function setCharacter(i, charactersArray) {
setTimeout(function() {
document.getElementById("characters").innerHTML = document.getElementById("characters").innerHTML + charactersArray[i];
}, 1000 * i);
}
function deleteAll(i) {
setTimeout(function() {
var text = document.getElementById("characters").innerHTML;
var charactersArray = new String(text).split('');
var charactersArrayLength = charactersArray.length - 1;
for (var j = charactersArrayLength; j >= 0; j--) {
charactersArray.splice(j, 1);
deleteCharacter(charactersArray.join(""), i + charactersArrayLength - j + 1);
}
}, (i + 1) * 1000);
}
function deleteCharacter(text, i) {
setTimeout(function() {
document.getElementById("characters").innerHTML = text;
}, 1000 * i);
}
#divContainer {
display: flex;
align-items: center;
float: right;
}
#characters {
font-weight: 700;
color: rgb(0, 0, 0);
font-size: 40px;
padding-top: 2px;
white-space: nowrap;
}
<div id="divContainer">
<h1 id="characters">
</h1>
</div>
I made the code to write one character every second and after complete the text it will delete one character every second,the problem is that after complete the text it will wait for a few seconds before starting deleting the characters.I did not do that.
I have followed the time value of settimeout method and everything is fine.
I want it to start deleting characters directly after complete the text.
any help please.
Dont line up app the timeouts in a row do them one after the other.
Here is how you should do it. I speed the time up a bit, but that matters not.
var text = "Add a new timeout as needed."
var div = document.createElement("div");
document.body.appendChild(div);
var textPos = 0;
function textAdd(){
div.textContent += text[textPos++];
if(textPos >= text.length){
setTimeout(clearText,1000);
}else{
setTimeout(textAdd,200);
}
}
function clearText(){
textPos --;
div.textContent = text.substr(0,textPos);
if(textPos === 0){
setTimeout(textAdd,1000);
}else{
setTimeout(clearText,200);
}
}
textAdd();
I stayed close to your code, the fix was actually quite simple, I marked it with THE FIX below.
// Replace spaces with non-breaking spaces so it does not stutter, and use global var
var text = "hey, how are you".replace(/ /g, String.fromCharCode(160));
showCharacters();
function showCharacters() {
var chars = new String(text).split('');
var i;
for (i = 0; i < chars.length; i++) {
setCharacter(i, chars);
}
deleteAll(i - 1);
}
function setCharacter(i, chars) {
setTimeout(function() {
document.getElementById("characters").innerHTML = document.getElementById("characters").innerHTML + chars[i];
}, 200 * i);
}
function deleteAll(i) {
setTimeout(function() {
var chars = new String(text).split('');
var len = chars.length - 1;
for (var j = len; j >= 0; j--) {
chars.splice(j, 1);
deleteCharacter(chars.join(""), i - j + 1); // THE FIX: don't add length here
}
}, (i + 1) * 200);
}
function deleteCharacter(text, i) {
setTimeout(function() {
document.getElementById("characters").innerHTML = text;
}, 200 * i);
}
#divContainer {
display: flex;
align-items: center;
float: right;
}
#characters {
font-weight: 700;
color: rgb(0, 0, 0);
font-size: 40px;
padding-top: 2px;
white-space: nowrap;
}
<div id="divContainer">
<h1 id="characters">
</h1>
</div>

Categories

Resources