A function defined in setTimeOut only runs once in certain situations
I set up 3 inputs in ComboBox by which the user could set the grid size in the game. Selecting a value changes the rows and cols variables respectively, and then reboots the game by calling the init function.
The program starts normally, when I choose a different size the timer does not run the game more than once. When I change the size again it does work. If for example I change the sizes 6 times the game will only work in 3 of the times.
/* Game Of Life Application */
/* ------------------------ */
// initialize global variables
var rows = 55;
var cols = 140;
//initialize 2dim arrays
var arr;// current generation array
var nextArr; // next generation array
var mode = 0; //game current mode
var timeInMS = 40;
var timer;
//buttons selectors
var randomBtn = document.getElementById("randomBtnId");
var startBtn = document.getElementById("startBtnId");
var clearBtn = document.getElementById("clearBtnId");
var gridSize = document.getElementById("sizeId");
var container = document.getElementById("container");
function remove() {
let tb = document.querySelector("table");
tb.outerHTML = "";
}
gridSize.addEventListener("change",function(e) {
remove();
if (this.value === "Small") {
cols = 80;
rows = 20;
}
else if (this.value === "Medium") {
cols = 126;
rows = 34;
}
else {
cols = 140;
rows = 55;
}
timer = 0;
init();
});
//update the visual grid according to the states of the cell - live or dead.
function update() {
for (var i = 0; i < rows; i++) {
for (var j = 0; j < cols; j++) {
var cell = document.getElementById(i + "_" + j);
if (arr[i][j] === 0) {
cell.setAttribute("class", "dead");
} else {
cell.setAttribute("class", "live");
}
}
}
}
//copy generation 0 array to generation 1. current arr gets the values of next arr
function copyAndResetGrid() {
console.log("in the reset grid");
for (var i = 0; i < rows; i++) {
for (var j = 0; j < cols; j++) {
arr[i][j] = nextArr[i][j];
nextArr[i][j] = 0;
}
}
}
//count number of neighbors for every cell - inputs are r - rows , c - columns
function countNeighbors(r, c) {
let rstart = 0, cstart = 0, rend = rows - 1, cend = cols - 1;
let count = 0;
if (r - 1 > 0)
rstart = r - 1;
if (c - 1 > 0)
cstart = c - 1;
if (r + 1 <= rend)
rend = r + 1;
if (c + 1 <= cend)
cend = c + 1;
for (let i = rstart; i <= rend; i++) {
for (let j = cstart; j <= cend; j++) {
if (arr[i][j] === 1)
count++;
}
}
count -= arr[r][c];
if (count < 0)
count = 0;
// console.log("number of live neighbors at : " + r + "," + c + " is : " + count);
return count;
}
// calculate next 2dim array (generation 1) according to gameOfLife rules
function calculateNext() {
let numOfLivesArr = 0;
for (let i = 0; i < rows; i++) {
for (let j = 0; j < cols; j++) {
let currentMode = arr[i][j];
if (currentMode === 1)
numOfLivesArr++;
let count = countNeighbors(i, j);
if (currentMode === 0 && count === 3) {
nextArr[i][j] = 1;
}
else if (currentMode === 1 && (count < 2 || count > 3)) {
nextArr[i][j] = 0;
}
else {
nextArr[i][j] = currentMode;
}
}
}
// console.log("num of lives next: " + numOfLivesArr);
copyAndResetGrid();
//update();
}
//run game
function run() {
calculateNext();
update();
timer = setTimeout(run, 1000);
}
//populate the array with random values 0/1
function populateArr() {
for (let i = 0; i < rows; i++) {
for (let j = 0; j < cols; j++) {
arr[i][j] = Math.floor(Math.random() * 2);
if (arr[i][j] === 1) {
let cell = document.getElementById(i + "_" + j);
cell.setAttribute("class", "live");
}
else {
let cell = document.getElementById(i + "_" + j);
cell.setAttribute("class", "dead");
}
}
}
}
function deleteArr() {
}
//clear array - set 0 values for current and next generations arrays
function clear() {
for (let i = 0; i < rows; i++) {
for (let j = 0; j < cols; j++) {
arr[i][j] = 0;
nextArr[i][j] = 0;
}
}
//mode = 0;
}
function buttonsControl() {
randomBtn.addEventListener("click", function () {
clear();
populateArr();
});
startBtn.addEventListener("click", function () {
if (mode == 1) {
mode = 0;
startBtn.textContent = "Continue";
clearTimeout(timer);
}
else {
mode = 1;
startBtn.textContent = "Pause";
run();
}
});
clearBtn.addEventListener("click", function () {
startBtn.textContent = "Start";
clear();
update();
})
}
//draw table grid in the web page
function drawGrid() {
let grid = document.getElementById("container");
let table = document.createElement("table");
table.setAttribute("class", "center");
for (let i = 0; i < rows; i++) {
let tr = document.createElement("tr");
for (let j = 0; j < cols; j++) {
let cell = document.createElement("td");
cell.setAttribute("id", i + "_" + j);
cell.setAttribute("class", "dead");
tr.appendChild(cell);
cell.addEventListener("click", function () {
if (cell.classList.contains("live")) {
cell.setAttribute("class", "dead");
arr[i][j] = 0;
}
else
cell.setAttribute("class", "live");
arr[i][j] = 1;
});
}
table.appendChild(tr);
}
grid.appendChild(table);
}
//create 2 dim arrays - current and next generations.
function make2DArr() {
console.log("befire create arr !! ");
for (let i = 0; i < rows; i++) {
arr[i] = new Array(cols);
nextArr[i] = new Array(cols);
}
for (let i = 0; i < rows; i++) {
for (let j = 0; j < cols; j++) {
arr[i][j] = 0;
nextArr[i][j] = 0;
}
}
}
//initial game
function init() {
arr = new Array(rows);
nextArr = new Array(rows);
make2DArr();
drawGrid();
buttonsControl();
}
//load init function
window.onload = init();
body {
background-color: rgba(76, 77, 62, 0.514);
}
.center {
margin: auto;
width: 90%;
padding: 0.5rem;
position: relative;
}
#container {
margin: 0;
position: relative;
overflow: auto;
display: flex;
}
table {
border:1px rgb(241, 241, 241) solid;
border-spacing: 0;
position: absolute;
flex:1;
}
.live {
background-color:rgba(0, 0, 0, 0.685);
}
.dead {
background-color:rgba(228, 228, 241, 0.829);
}
td {
border:1px rgb(29, 182, 29) solid;
/* border-radius: 61px;*/
width: 10px;
height: 10px;
}
/* button {
margin-left: 0.5rem;
} */
button {
background-color: #4CAF50; /* Green */
border: none;
color: white;
padding: 1rem 2rem;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
margin: 0.5rem 0.5rem;
transition-duration: 0.4s;
cursor: pointer;
}
button:hover {
background-color: rgba(144, 180, 145, 0.596);
color: rgb(54, 59, 54)
}
<body>
<div class="center">
<div id="container">
</div>
<button id="startBtnId"><span>Start</span></button>
<button id="clearBtnId"><span>Clear</span></button>
<button id="randomBtnId"><span>Random</span></button>
<select id="sizeId">
<option value="Big">Big</option>
<option value="Medium">Medium</option>
<option value="Small">Small</option>
</select>
</div>
<script type="text/javascript" src="game.js"></script>
</body>
the timer is work only in even number of mode selection and does not work in odd number of mode selection.
for example , if i changed the mode 4 times : work -> not -> word -> not
Once an option has been selected from the list, init function was called. Within the function I called for 3 functions that build the arrays, initialize the board and create the button events listeners. The solution is to build the arrays and initialize the board without create the buttons event listeners again. so i just calld make2darray and drawgrid functions.
Related
I've been trying to make a little neural network. It plays a copy of the No-Wifi Google dinosaure game. It is not the most efficient (and probably has a tons of leaks), but it works after a hundred generations more or less. But I've notived something: the best elements of the previous generations do not copy to the next or don't act as before, shown by how the best before always jumped and then the next generation doesn't jump at all. Where is the problem? Btw it is a large code but I don't know how to attach files, can you comment on how?
HTML file:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" name="viewport" content="width=device-width,initial-scale=1">
<title>Dino</title>
<script type="text/javascript" src="genetic.js" defer></script>
<script type="text/javascript" src="dino.js" defer></script>
<script type="text/javascript" src="population_dino.js" defer></script>
<link rel="stylesheet" href="dino.css">
</head>
<body>
<div id="score">Score: 0</div>
<div id="line"></div>
<div id="test"></div>
</body>
</html>
genetic.js:
function sigmoid(t) {
return 1/(1+Math.pow(Math.E, -t));
}
function weightedRand(spec) {
var i, j, table=[];
for (i in spec) {
for (j=0; j<spec[i]*10; j++) {
table.push(i);
}
}
//console.log(table,spec)
return table[Math.floor(Math.random() * table.length)];
}
function randomfromminustoone(){
return Math.random()*2-1;
}
class Connection {
constructor(weight) {
if (weight==null){
this.weight=randomfromminustoone();
} else{
this.weight = weight;
}
}
get_weight() {
return this.weight;
}
set_weight(weight) {
return this.weight = weight;
}
}
class Neuron {
constructor(numOuputs = 0, neuronIndex, weights = []) {
this.outputWeights = [];
this.outputVal = null;
this.neuronIndex = neuronIndex;
for (let c = 0; c < numOuputs; c++) {
var weight = weights[c] || null;
this.outputWeights.push(new Connection(weight));
}
}
setOutputVal(outputVal) {
this.outputVal = outputVal;
}
getOutputVal(inputs,inputWeights) {
this.outputVal=0;
for (var i = inputs.length - 1; i >= 0; i--) {
this.outputVal+=inputs[i]*inputWeights[i];
}
this.outputVal=sigmoid(this.outputVal);
return this.outputVal;
}
getNeuronIndex() {
return this.neuronIndex;
}
getOuputWeight(i){
return this.outputWeights[i].get_weight();
}
updateInputWeights(prevLayer) {
for (let n = 0; n < prevLayer.length-1; n++) {
let neuron = prevLayer[n];
// neuron.outputWeights[this.neuronIndex].weight += ;
}
}
}
class Net {
constructor(topology = [],model=false) {
if (model){
this.layers=model.layers;
//console.log("flag");
} else {
this.topology = topology;
// number of layers
this.numLayers = topology.length;
this.layers = [];
for (let layerNum = 0; layerNum < this.numLayers; layerNum++) {
var layer = [];
var numOuputs = topology[layerNum + 1] ? topology[layerNum + 1] : 0;
for (let neuronNum = 0; neuronNum < topology[layerNum]; neuronNum++) {
let neuron = new Neuron(numOuputs, neuronNum);
layer.push(neuron);
}
this.layers.push(layer);
}
}
}
/*
getResults() {
this.resultVals = [];
var lastLayer = this.layers[this.layers.length-1];
for (let n = 0; n < lastLayer.length-1; n++) {
this.resultVals.push(lastLayer[n].getOutputVal());
}
return this.resultVals;
}*/
feedLayer(inputs,layerNum){
let layer=this.layers[layerNum];
let outputs=[];
for (let i = 0; i < layer.length; i++) {
let neuron = layer[i];
let neuronWeights=[];
for (let a = 0; a < this.layers[layerNum-1].length; a++) {
neuronWeights.push(this.layers[layerNum-1][a].getOuputWeight(i));
}
outputs.push(neuron.getOutputVal(inputs,neuronWeights));
}
return outputs;
}
feedAll(inputs){
let prevOutputs=inputs;
for (let i = 1; i < this.layers.length; i++) {
prevOutputs=this.feedLayer(prevOutputs,i);
}
return prevOutputs;
}
};
class Population{
constructor (PopNumber,topology,bestResults=[]){
if (bestResults.length>1){
this.population=[];
for (var i = 0; i < bestResults.length; i++) {
this.population.push(bestResults[i][1]);
console.log(bestResults[i][1].feedAll([0.05,0]))
}
for (var i = 0; i < PopNumber-bestResults.length; i++) {
let addNet= this.choose(bestResults);
/*if (randomfromminustoone()>0.5){
addNet=this.mix_elements(addNet,this.choose(bestResults));
}*/
addNet=this.mutate_values(addNet);
this.population.push(addNet);
}
} else {
this.population=[];
for (var i = 0; i < PopNumber; i++) {
let addNet= new Net(topology);
this.population.push(addNet);
}
}
}
choose(results){
let dic={};
for (var i = 0; i < results.length; i++) {
dic[i]=results[i][0];
}
return results[weightedRand(dic)][1];
}
mix_elements(elem1,elem2){
var output= new Net();
output.topology = elem2.topology;
output.numLayers = elem2.numLayers;
output.layers = elem2.layers;
for (var i = 0; i < elem1.layers.length; i++) {
for (var a = 0; a < elem1.layers[i].length; a++) {
if (randomfromminustoone()>0.5){
output.layers[i][a]=elem1.layers[i][a];
}
}
}
return output;
}
mutate_values(model){
if (model==null){
return [];
}
var output= new Net();
output.topology = model.topology;
output.numLayers = model.numLayers;
output.layers = model.layers;
for (var layer = 0; layer < output.layers.length; layer++) {
for (var neuron = 0; neuron < output.layers[layer].length; neuron++) {
for (var i = 0; i < output.layers[layer][neuron].outputWeights.length; i++) {
if (randomfromminustoone()>0.75){
output.layers[layer][neuron].outputWeights[i].weight =output.layers[layer][neuron].outputWeights[i].get_weight()+randomfromminustoone()/5;
}
}
}
}
return output;
}
};
dino.js:
function getRndInteger(min, max) {
return Math.floor(Math.random() * (max - min + 1) ) + min;
};
function Dino(brain,pop){
this.cooldown=0;
this.score=0;
this.alive=true;
this.y=300;
this.before_jump=this.y;
this.inertia=0;
this.population=pop;
this.div = document.createElement('div');
this.div.className="Dino";
this.div.main=this;
this.div.style.top=this.y+"px";
document.body.appendChild(this.div);
this.brain = brain;
};
Dino.prototype.jump = function(dino){
if (dino.y==dino.before_jump){
//dino.y=dino.before_jump;
dino.inertia=3;
dino.jumpInterval=setInterval( jump => {
dino.y-=dino.inertia
dino.div.style.top=dino.y+"px";
dino.inertia -= 0.05;
if (dino.y>dino.before_jump){
dino.inertia=0;
clearInterval(dino.jumpInterval);
dino.y=dino.before_jump;}
});
}
};
Dino.prototype.down = function(dino){
if (dino.y<dino.before_jump && dino.inertia>-5){
dino.inertia-=1.5;
}
};
Dino.prototype.lost =function(dino){
dino.alive=false;
dino.div.remove();
dino.score=dino.population.score;
dino.population.still_alive-=1;
dino.population.records.push(dino.brain);
delete dino;
};
Dino.prototype.check_collision = function(dino) {
cactuses=document.getElementsByClassName("cactus");
for (var i = cactuses.length - 1; i >= 0; i--) {
if (cactuses[i].main.x<=280 && dino.y>=280 && dino.alive){
dino.lost(dino);
}
};
};
Dino.prototype.smart = function(dino){
var cactuses=Array.from(document.getElementsByClassName("cactus"));
cactuses.sort(function(cactus){
return cactus.main.x;
});
if (cactuses.length>0){
var distance_nearest_cactus=(cactuses[0].main.x-240)/660;
} else{
var distance_nearest_cactus=1;
};
let results = dino.brain.feedAll([distance_nearest_cactus,(300-dino.y)/100*(-dino.inertia/3)]);
let high_score=results.indexOf(Math.max.apply(Math,results))
if (high_score==0){
dino.jump(dino);
} else if (high_score==1){
dino.down(dino);
};
};
function Dot(height){
this.y=height
this.x=900
//-- cree le DIV
this.div = document.createElement('div');
this.div.className = 'dot';
this.div.style.top=this.y+"px";
this.div.style.left=this.x+"px";
this.div.main=this;
document.body.appendChild(this.div);
};
Dot.prototype.delete =function(leave_div)
{
if (leave_div == false){
this.div.remove();
}
delete this;
};
function CreateDot(){
new Dot(getRndInteger(346,350));
};
function Cactus(){
this.x=900
this.div = document.createElement('div');
this.div.style.left=this.x+"px";
this.div.className="cactus";
this.div.main=this;
document.body.appendChild(this.div);
};
Cactus.prototype.delete =function(leave_div)
{
if (leave_div == false){
this.div.remove();}
delete this;
}
function CreateCactus(){
new Cactus();
}
function move_object(object){
object.main.x -= 1;
object.main.div.style.left = object.main.x+'px';
if (object.main.x<230)
{
object.main.delete(false);
}
};
population_dino.js:
class Dino_population{
constructor(nbPopulation,id,prevPop=[]){
this.population=[];
this.id=id;
document.getElementById("test").innerHTML=this.id;
this.brains=new Population(nbPopulation,[2,5,5,3],prevPop);
for (var i = 0; i < nbPopulation; i++) {
this.population.push(new Dino(this.brains.population[i],this));
}
this.number=nbPopulation;
this.still_alive=nbPopulation;
this.score=0;
this.speed=4.5;
var self= this;
this.move_interval=setInterval(function(){self.move_all(self)},self.speed);
this.last_cactus=1000;
this.records=[];
}
move_all(self){
if (getRndInteger(0,60)<10){
CreateDot();
}
self.last_cactus-=getRndInteger(1,10);
if (self.last_cactus<10){
self.last_cactus=getRndInteger(500,5000);
CreateCactus();
}
var cactuses=document.getElementsByClassName("cactus");
for (var i = cactuses.length - 1; i >= 0; i--) {
move_object(cactuses[i]);
};
var dots=document.getElementsByClassName("dot");
for (var v = dots.length - 1; v >= 0; v--) {
move_object(dots[v]);
}
self.score+=1;
document.getElementById("score").innerHTML = "Score: "+Math.floor(self.score/10).toString();
for (var i = self.population.length - 1; i >= 0; i--) {
let dino=self.population[i];
dino.cooldown-=1;
if (dino.cooldown<=0){
dino.cooldown=10;
dino.smart(dino);
}
dino.check_collision(dino);
}
if (self.still_alive==0){
clearInterval(self.move_interval);
var cactuses=document.getElementsByClassName("cactus");
while (cactuses.length>0){
delete cactuses[0].main;
cactuses[0].remove();
};
var dots=document.getElementsByClassName("dot");
while (dots.length>0){
delete dots[0].main;
dots[0].remove();
};
self.new_pop(self);
}
}
new_pop(old){
let old_bests= old.records.slice(Math.max(old.records.length - 10, 0));
let old_best_weighted=[]
for(let i=0;i<old_bests.length;i++){
console.log([old_bests[i].feedAll([0.05,0])])
old_best_weighted.push([10-i,old_bests[i]]);
}
let old_nb=old.number;
let old_id=old.id;
globalThis.pop= new Dino_population(old_nb,old_id+1,old_best_weighted);
}
}
var pop=new Dino_population(200,1);
dino.css:
.Dino{
background-image: url("Dinausaure.png");
background-repeat: no-repeat;
background-size: contain;
width: 60px;
height: 45px;
left: 240px;
position: absolute;
z-index: 3;
}
#ground{
width: 1000px;
height: 200px;
top:270px;
left: 240px;
position: absolute;
}
#line{
width: 670px;
height: 0px;
top:345px;
left:230px;
border-top: 2px solid black;
position: absolute;
z-index: 1;
}
.dot{
border-top: 2px solid black;
width: 2px;
position: absolute;
z-index: 1;
}
.cactus{
background-image: url("cactus.png");
background-repeat: no-repeat;
background-size: contain;
width: 60px;
height: 40px;
top:310px;
position: absolute;
z-index: 0;
}
#score{
top:180px;
left:230px;
position: absolute;
}
Here are the images:
Do no doubht in telling me any optimisation, mistake or bug. I am new at neural networks and do not have much experience with Js.
Any question ask me on comments.
I am making a Conway Game of Life that it starts with a predefined board, I made that board through an two dimensional Array, have implemented the rules and they seem to be working, the problem is that the different generations just appear next to previous one without replacing it (see image [1])
image:
[![enter image description here][1]][1]
So, what i needed is to make the following generations replace the previous ones on the HTML display.
var gfg=new Array(10);
for (var COL=0; COL<gfg.length;COL++){
gfg[COL]=new Array(10);
}
run();
function run(){
board1();
rules();
}
function rules(){
for (var i=0; i<10;i++){
for(var j=0; j<10; j++){
const cell = gfg[i][j];
let numNeighbours = 0;
for (let x = -1; x < 2; x++) {
for (let y = -1; y < 2; y++) {
if (x === 0 && y === 0) {
continue;
}
var x_cell = i + x;
var y_cell = j + y;
if (x_cell > 0 && y_cell > 0 && x_cell <0 && y_cell <0) {
const currentNeighbour = 1;
numNeighbours=numNeighbours+currentNeighbour;
}
}
}
if (cell === 1 && numNeighbours < 2) {
gfg[i][j] = 0;
} else if (cell === 1 && numNeighbours > 3) {
gfg[i][j] = 0;
} else if (cell === 0 && numNeighbours === 3) {
gfg[i][j] = 1;
}
}
draw();
}
}
function draw(){
for (var i=0; i<10;i++){
for(var j=0; j<10; j++){
//Writes in HTML according to the coordinate value
if(gfg[i][j]===0){
document.write("◻");
}else if(gfg[i][j]===1){
document.write("◼");
}
}
document.write("<br>");
}
}
//predefined board
function board1() {
gfg[0][0] = 1;
gfg[0][1] = 0;
gfg[0][2] = 1;
gfg[0][3] = 0;
gfg[0][4] = 0;
gfg[0][5] = 1;
gfg[0][6] = 0;
gfg[0][7] = 0;
gfg[0][8] = 0;
gfg[0][9] = 1;
gfg[1][0] = 0;
gfg[1][1] = 0;
gfg[1][2] = 0;
gfg[1][3] = 0;
gfg[1][4] = 0;
gfg[1][5] = 0;
gfg[1][6] = 0;
gfg[1][7] = 1;
gfg[1][8] = 0;
gfg[1][9] = 0;
gfg[2][0] = 0;
gfg[2][1] = 0;
gfg[2][2] = 0;
gfg[2][3] = 1;
gfg[2][4] = 0;
gfg[2][5] = 1;
gfg[2][6] = 1;
gfg[2][7] = 0;
gfg[2][8] = 0;
gfg[2][9] = 0;
gfg[3][0] = 0;
gfg[3][1] = 0;
gfg[3][2] = 1;
gfg[3][3] = 0;
gfg[3][4] = 1;
gfg[3][5] = 0;
gfg[3][6] = 0;
gfg[3][7] = 0;
gfg[3][8] = 0;
gfg[3][9] = 1;
gfg[4][0] = 0;
gfg[4][1] = 0;
gfg[4][2] = 0;
gfg[4][3] = 0;
gfg[4][4] = 1;
gfg[4][5] = 0;
gfg[4][6] = 0;
gfg[4][7] = 0;
gfg[4][8] = 0;
gfg[4][9] = 0;
gfg[5][0] = 0;
gfg[5][1] = 1;
gfg[5][2] = 0;
gfg[5][3] = 0;
gfg[5][4] = 0;
gfg[5][5] = 0;
gfg[5][6] = 0;
gfg[5][7] = 0;
gfg[5][8] = 0;
gfg[5][9] = 0;
gfg[6][0] = 0;
gfg[6][1] = 0;
gfg[6][2] = 0;
gfg[6][3] = 0;
gfg[6][4] = 1;
gfg[6][5] = 0;
gfg[6][6] = 1;
gfg[6][7] = 0;
gfg[6][8] = 1;
gfg[6][9] = 0;
gfg[7][0] = 1;
gfg[7][1] = 0;
gfg[7][2] = 0;
gfg[7][3] = 1;
gfg[7][4] = 0;
gfg[7][5] = 0;
gfg[7][6] = 0;
gfg[7][7] = 1;
gfg[7][8] = 0;
gfg[7][9] = 0;
gfg[8][0] = 0;
gfg[8][1] = 0;
gfg[8][2] = 1;
gfg[8][3] = 0;
gfg[8][4] = 1;
gfg[8][5] = 0;
gfg[8][6] = 0;
gfg[8][7] = 0;
gfg[8][8] = 0;
gfg[8][9] = 0;
gfg[9][0] = 0;
gfg[9][1] = 1;
gfg[9][2] = 0;
gfg[9][3] = 0;
gfg[9][4] = 0;
gfg[9][5] = 0;
gfg[9][6] = 0;
gfg[9][7] = 0;
gfg[9][8] = 1;
gfg[9][9] = 0;
}
[1]: https://i.stack.imgur.com/ZKrFj.png
this is if you are using document.write()
All you will have to do is clear the container of the previous write with document.body.innerHTML = ""; this will clear your previous writes and then you can go through the loop again.
If you need to use html and javascript only canvas will still work as it is a built in html tag. However if you still wish to proceed with using html to display your cells then create a new div tag and give it an id e.g: id="life_container" then grab a reference to this tag when the page finishes loading and on every frame empty the div then go through your loop and fill it with your elements.
var container;
document.onload = function() {
container = document.getElementById('life_container');
}
// clear container
container.innerHtml = "";
// fill container
container.innerHtml += gfg[i][j] === 0 ? "◻" : "◼";
However I might suggest some improvements by using canvas instead of html to display your game of life. You can use P5 library, to run your loop and display it. You will need to add in extra calculation to figure out "where" to draw your cell on canvas but not much more.
Could anyone help me make a multiplication table, 0-10 in an 11x11 table?
I need to use createElement/appendchild. When I use document write, it almost look complete, just miss the placement of the blue columns/rows.
It should look something like this (Only need the numbers, no fancy outline):
This is what I've got so far:
for(var i = 0; i < 1; i++){
var tabell1 = document.createElement("table");
tabell.appendChild(tabell1);
//document.write("<table>");
for(var j = 0; j<11; j++){
var rad = document.createElement("tr");
tabell.appendChild("tr");
//document.write("<tr>");
for(var k = 1; k<=11; k++){
var kolonne = document.createElement("td");
tabell.appendChild(kolonne);
kolonne.innerHTML = k*(j+1);
//document.write("<td>"+ k*(j+1) +"</td>");
}
//document.write("</tr>");
}
//document.write("</table>")
}
<div id="tabell"></div>
You can generate the table using two loops.
You iterate twice from 0 to 10 included.
Use use the value 0 to represent your top row and first column, which hold the numbers to be multiplied. Since the iterator starts at 0, the value will be 0 and you can use that to detect when to add the header class and set the value to your non-zero iterator, either i or j:
const table = document.createElement('table');
for (let i = 0; i <= 10; i++){
const row = document.createElement('tr');
for (let j = 0; j <= 10; j++){
const col = document.createElement('td');
let val = i * j;
if (val === 0) {
val = i || j;
val = val ? val : '';
col.classList.add('header');
}
col.innerHTML = val;
row.appendChild(col);
}
table.appendChild(row);
}
document.body.appendChild(table);
table {
border-collapse: collapse;
}
td {
border: 1px solid black;
padding: 3px;
text-align: center;
}
.header {
background: #ccf;
}
The blue border can be obtained by css. See my code. I only changed four lines of loop
function createTables(maxNum,limit){
const table = document.createElement('table');
for(let i = 0;i<maxNum + 1;i++){
const row = document.createElement('tr');
for(let j = 0;j<limit + 1;j++){
const td = document.createElement('td');
//Below four lines are new
if(i === 0 && j === 0) td.innerHTML = '';
else if(i === 0) td.innerHTML = j;
else if(j === 0) td.innerHTML = i;
else td.innerHTML = i*j;
row.appendChild(td);
}
table.appendChild(row)
}
document.body.appendChild(table)
}
createTables(10,15);
table{
border-collapse:collapse;
}
td{
padding:20px;
font-size:25px;
background-color:gray;
border:2px solid black;
text-align:center;
vertical-align:center;
color:white;
}
tr > td:nth-child(1),tr:nth-child(1) > td{
background:blue;
}
I think best to use inserRow and insertCell
Cheers!
for (var i = 0; i < 1; i++) {
var tabell1 = document.createElement("table");
tabell.appendChild(tabell1);
for (var j = 0; j < 11; j++) {
var row = tabell1.insertRow(j);
for (var k = 0; k <= 10; k++) {
var cell = row.insertCell(k);
if (j == 0 && k == 0) {
//if first row and first column do nothing
} else if (j == 0) {
//else if first row
cell.innerHTML = k * (j + 1);
} else if (k == 0) {
//else if first column
cell.innerHTML = j;
} else {
//else multiply
cell.innerHTML = k * (j);
}
}
}
}
<div id="tabell"></div>
I'm writing simple Sudoku solver as an exercise and I've thought that I will show entire process by displaying it step by step on 9x9 grid.
I'm using HTML table with nested divs for displaying game board (probably not relevant) and recursive function for solving preseeded Sudoku. My solution is in "spaghetti" state right now, so below I give you pseudocode for what I have:
function fillBoard() {
for(let i = 0; i < 9; i++) {
for(let j = 0; j < 9; j++) {
const cell = document.querySelector(`div[data-row="${i}"][data-column="${j}"]`);
cell.innerHTML = gameState.solution[i][j];
}
}
}
function solve(row, column) {
/* simplified, it's working :) */
for(let guess = 1; guess < 10; guess++) {
this.solution[row][column] = guess; <--- SHOW THIS STEP TO USER
let conflicts = checkConflicts(row, column)
if(!conflicts) {
let emptyCell = this.findNextEmptyCell(row, column);
if(emptyCell) {
let result = this.solve(emptyCell.i, emptyCell.j);
if(!result) continue;
else return true;
}
return true;
}
else continue;
}
}
I've tried putting fillBoard() function call inside solve() but that obviously didn't worked as I'm getting only the final result in form of solved grid. I've also tried using setInterval(fillBoard, 100) but solve() function is executing too fast.
How can I achieve "incremental" displaying of entire solving process by updating HTML after each solve() call?
I'm trying to get something like this: https://www.youtube.com/watch?v=ChjQRIhH414 but I'm filling the board left-right, top-bottom
Current solution: Codepen
You'll want to slow down your html rendering process, which suggests animation to me. You can look into this approach, and maybe throttle it if you want. Without having a complete example I can't run it to be sure. I'll give you some pseudo code in response for the way I would test it to start with, and hopefully that'll point you in the right direction:
function fillBoard(i=0, j=0) {
const cell = document.querySelector(`div[data-row="${i}"][data-column="${j}"]`);
cell.innerHTML = gameState.solution[i][j];
j++;
if( j >= 9) { j = 0; i++; }
if(i < 9)
requestAnimationFrame(function() { fillBoard(i,j) });
}
If you want, you can replace requestAnimationFrame with a setTimeout on a given delay. You can set it to 1-2 seconds or something to start with to see if it gives you the results you're looking for.
Unoptimized, but working example, by following #loctrice advice from comments above. Still looking for other solutions to this problem.
console.clear();
let states = [];
let x = 0;
document.addEventListener("DOMContentLoaded", function() {
const start = document.getElementById("start");
const check = document.getElementById("check");
const sudoku = document.getElementById("sudoku");
function fillBoard(index) {
console.log(`Displaying ${index}/${states.length}`);
for (let i = 0; i < 9; i++) {
for (let j = 0; j < 9; j++) {
const cell = document.querySelector(
`div[data-row="${i}"][data-column="${j}"]`
);
cell.innerHTML =
states[index][i][j] == 0 ? "" : states[index][i][j];
}
}
}
class Sudoku {
checkRow(row) {
for (let i = 0; i < 9; i++) {
let number = this.solution[row][i];
if (!number) continue;
for (let j = 0; j < 9; j++) {
if (i == j) continue;
const challenge = this.solution[row][j];
if (number == challenge) return number;
}
}
return false;
}
checkColumn(column) {
for (let i = 0; i < 9; i++) {
let number = this.solution[i][column];
if (!number) continue;
for (let j = 0; j < 9; j++) {
if (i == j) continue;
const challenge = this.solution[j][column];
if (number == challenge) return number;
}
}
return false;
}
checkBox(box) {
const rowModifier = Math.floor(box / 3) * 3;
const colModifier = (box % 3) * 3;
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
let number = this.solution[i + rowModifier][j + colModifier];
if (!number) continue;
for (let x = 0; x < 3; x++) {
for (let y = 0; y < 3; y++) {
if (x == i && y == j) continue;
const challenge = this.solution[x + rowModifier][y + colModifier];
if (number == challenge) return number;
}
}
}
}
return false;
}
solve(row, column, array) {
for (let guess = 1; guess < 11; guess++) {
if (guess == 10) {
this.solution[row][column] = 0;
return false;
}
this.solution[row][column] = guess; // <=== SHOW ENTIRE BOARD HERE
let state = this.solution.map(a => [...a]);
array.push(state);
const rowError = this.checkRow(row);
const columnError = this.checkColumn(column);
const boxError = this.checkBox(
3 * Math.floor(row / 3) + Math.floor(column / 3)
);
if (!rowError && !columnError && !boxError) {
// find next empty cell
let emptyCell = this.findNextEmptyCell(row, column);
if (emptyCell) {
let result = this.solve(emptyCell.i, emptyCell.j, array);
if (!result) continue;
else return true;
}
return true;
} else continue;
}
}
findNextEmptyCell(row, column) {
for (let i = row; i < 9; i++) {
if (column == 8) column = 0;
for (let j = column; j < 9; j++) {
if (this.solution[i][j]) continue;
else return { i: i, j: j };
}
column = 0;
}
return false;
}
generateBox(boxNumber) {
let numbers = [];
for (let i = 0; i < 9; i++) {
numbers.push(i);
}
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
const length = numbers.length;
const index = Math.floor(Math.random() * length);
const number = numbers.splice(index, 1);
const row = i + boxNumber * 3 / 4;
const col = j + boxNumber * 3 / 4;
this.solution[row][col] = parseInt(number) + 1;
}
}
}
initialize() {
this.solution = [];
for (let i = 0; i < 9; i++) {
this.solution.push([]);
for (let j = 0; j < 9; j++) {
this.solution[i][j] = 0;
}
}
for (let i = 0; i < 3; i++) {
this.generateBox(i * 4);
}
}
}
let gameState = new Sudoku();
gameState.initialize();
start.onclick = function() {
gameState.solve(0, 3, states);
window.setInterval(function() {if(x < states.length) fillBoard(x++);}, 15);
};
check.onclick = function() {
for (let i = 0; i < 9; i++) {
let error = gameState.checkRow(i);
if (error) {
for (let j = 0; j < 9; j++) {
const cell = document.querySelector(
`div[data-row="${i}"][data-column="${j}"]`
);
cell.classList.add("incorrect-area");
if (cell.innerHTML == error) cell.classList.add("incorrect");
}
return;
}
error = gameState.checkColumn(i);
if (error) {
const cells = document.querySelectorAll(`div[data-column="${i}"]`);
cells.forEach(c => {
c.classList.add("incorrect-area");
if (c.innerHTML == error) c.classList.add("incorrect");
});
return;
}
error = gameState.checkBox(i);
if (error) {
const cells = document.querySelectorAll(`div[data-box="${i}"]`);
cells.forEach(c => {
c.classList.add("incorrect-area");
if (c.innerHTML == error) c.classList.add("incorrect");
});
return;
}
}
};
for (let i = 0; i < 9; i++) {
const row = document.createElement("tr");
row.classList.add("row");
for (let j = 0; j < 9; j++) {
let td = document.createElement("td");
let cell = document.createElement("div");
cell.classList.add("cell");
cell.dataset.row = i;
cell.dataset.column = j;
let a = Math.floor(i / 3);
let b = Math.floor(j / 3);
cell.dataset.box = 3 * a + b;
cell.innerHTML =
gameState.solution[i][j] == "0" ? "" : gameState.solution[i][j];
if (!cell.innerHTML)
cell.onclick = function(e) {
const row = e.target.dataset.row;
const col = e.target.dataset.column;
gameState.solution[row][col] =
++gameState.solution[row][col] > 9
? 0
: gameState.solution[row][col];
cell.innerHTML =
gameState.solution[row][col] == "0"
? ""
: gameState.solution[row][col];
document
.querySelectorAll("div.cell")
.forEach(c =>
c.classList.remove("correct", "incorrect", "incorrect-area")
);
};
td.appendChild(cell);
row.appendChild(td);
}
sudoku.appendChild(row);
}
});
.incorrect-area {
background-color: #A55 !important;
border-color: #F00 !important;
}
.correct {
background-color: #1A1 !important;
border-color: #0F0 !important;
}
.incorrect {
background-color: #A11 !important;
border-color: #F00 !important;
}
div[data-box='1'],
div[data-box='3'],
div[data-box='5'],
div[data-box='7'] {
background-color: #444;
}
.button {
display: inline-block;
min-height: 30px;
width: 120px;
background-color: white;
font-size: 32px;
cursor: pointer;
}
#buttons {
text-align: center;
}
div {
padding: 0px;
}
body {
background-color: #000;
}
#game {
width: 500px;
margin-left: auto;
margin-right: auto;
margin-top: 0px;
}
#sudoku {
width: 500px;
height: 500px;
margin-left: auto;
margin-right: auto;
background-color: #111;
border: dashed 1px white;
color: white;
}
.row {
border: 1px solid yellow;
}
.cell {
cursor: default;
height: 40px;
width: 40px;
padding: 0;
margin: 0;
border: solid white 1px;
text-align: center;
font-weight: bold;
display: table-cell;
vertical-align: middle;
user-select: none;
}
.cell:hover {
background-color: #765;
transform: scale(1.3);
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>SudoQ</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" type="text/css" media="screen" href="main.css" />
<script src="main.js"></script>
</head>
<body>
<div id="game">
<div id="buttons">
<div id="start" class="button">SOLVE</div>
<div id="check" class="button">CHECK</div>
</div>
<table id="sudoku">
</table>
</div>
</body>
</html>
I need a rainbow function in plain javascript.
Solution 1: Javascript + HSL
this should be working in any browser
window.addEventListener("load", function() {
var elements = document.getElementsByClassName("rainbowText");
for (let i = 0; i < elements.length; i++) {
generateRainbowText(elements[i]);
}
});
function generateRainbowText(element) {
var text = element.innerText;
element.innerHTML = "";
for (let i = 0; i < text.length; i++) {
let charElem = document.createElement("span");
charElem.style.color = "hsl(" + (360 * i / text.length) + ",80%,50%)";
charElem.innerHTML = text[i];
element.appendChild(charElem);
}
}
p {
font-size: 2em;
font-weight: 500;
}
<p class="rainbowText">This is an awesome text</p>
Solution 2: background-clip + linear-gradient
This is a webkit only answer (Chrome) but should be more efficient.
Update (01/2021): This is supported by modern browsers.
For compatibility list, check background-clip: text (including those with only -webkit- prefix support) here (MDN)
let nb_stops = 10; // 10 color stops should be enough
let dir = "left"; // left, right, top, bottom
function SetupRainbow() {
var rainbowStr = GetRainbowString(nb_stops, 80, 50);
var oppositeDir = (dir==="left"?"right":(dir==="right"?"left":(dir==="top"?"bottom":"top")));
var css = '.rainbowText {\
background-clip: text;\
color: transparent;\
-webkit-background-clip: text;\
-webkit-text-fill-color: transparent;\
background-image: -webkit-linear-gradient(' + dir + ',' + rainbowStr + '); \
background-image: linear-gradient(to ' + oppositeDir + ',' + rainbowStr + ') \
}'
var style = document.createElement("style");
style.type = 'text/css';
if (style.styleSheet) {
style.styleSheet.cssText = css;
} else {
style.appendChild(document.createTextNode(css));
}
document.head.appendChild(style);
}
// function that generate the rainbow string
function GetRainbowString(nbStops, saturation, luminosity) {
let gap = 360 / nbStops,
colors = [];
for (let i = 0; i < nbStops; i++) {
colors.push("hsl(" + (i * gap) + "," + saturation + "%," + luminosity + "%)");
}
return colors.join();
}
window.addEventListener("load", function() {
SetupRainbow();
});
span {
font-size: 2em;
font-weight: 500;
}
<span class="rainbowText">This is an awesome text</span>
It depends on what kind of rainbow text do you want. Some rainbow styles are really elegant.
Here are few :-
A pen from codepen.
Another pen here.
animated rainbow text at jsfiddle.
<div class="rainbow-text">Words and things</div>
#keyframes rainbow-text {
0% {
color: #e87d7d;
}
2% {
color: #e88a7d;
}
4% {
color: #e8977d;
}
6% {
color: #e8a47d;
}
8% {
color: #e8b07d;
}
10% {
color: #e8bd7d;
}
12% {
color: #e8ca7d;
}
14% {
color: #e8d77d;
}
16% {
color: #e8e47d;
}
18% {
color: #dfe87d;
}
20% {
color: #d3e87d;
}
22% {
color: #c6e87d;
}
24% {
color: #b9e87d;
}
26% {
color: #ace87d;
}
28% {
color: #9fe87d;
}
30% {
color: #92e87d;
}
32% {
color: #86e87d;
}
34% {
color: #7de881;
}
36% {
color: #7de88e;
}
38% {
color: #7de89b;
}
40% {
color: #7de8a8;
}
42% {
color: #7de8b5;
}
44% {
color: #7de8c1;
}
46% {
color: #7de8ce;
}
48% {
color: #7de8db;
}
50% {
color: #7de8e8;
}
52% {
color: #7ddbe8;
}
54% {
color: #7dcee8;
}
56% {
color: #7dc1e8;
}
58% {
color: #7db5e8;
}
60% {
color: #7da8e8;
}
62% {
color: #7d9be8;
}
64% {
color: #7d8ee8;
}
66% {
color: #7d81e8;
}
68% {
color: #867de8;
}
70% {
color: #927de8;
}
72% {
color: #9f7de8;
}
74% {
color: #ac7de8;
}
76% {
color: #b97de8;
}
78% {
color: #c67de8;
}
80% {
color: #d37de8;
}
82% {
color: #df7de8;
}
84% {
color: #e87de4;
}
86% {
color: #e87dd7;
}
88% {
color: #e87dca;
}
90% {
color: #e87dbd;
}
92% {
color: #e87db0;
}
94% {
color: #e87da4;
}
96% {
color: #e87d97;
}
98% {
color: #e87d8a;
}
100% {
color: #e87d7d;
}
}
.rainbow-text {
animation: rainbow-text 1s infinite;
}
I researched the same thing and came up with this:
var css = 'body {animation-name:test; animation-duration:4s; animation-iteration-count:infinite; } #keyframes test{ 0%{color:#ff0000} 20%{color:#00ff00} 40%{color:#ffff00} 60%{color:#0000ff} 80%{color:#00ffff} 100%{color:#ff0000}', head = document.head || document.getElementsByTagName('head')[0], style = document.createElement('style'); style.type = 'text/css'; if (style.styleSheet){ style.styleSheet.cssText = css; } else { style.appendChild(document.createTextNode(css)); } head.appendChild(style);
<p>WOO!<p>
This also works as a bookmarklet, with great results!
const colors = ['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet'];
var temp=document.getElementsByTagName('span');
for(let i=0;i<colors.length;i++){
temp[i].style.color=colors[i];
}
<!DOCTYPE html>
<head>
<title>Rainbow</title>
<script src="node_modules/babel-polyfill/dist/polyfill.js" type="text/javascript"> </script>
<script src="https://unpkg.com/#babel/standalone/babel.min.js"></script>
</head>
<body>
<h1>
<span>R</span>
<span>A</span>
<span>I</span>
<span>N</span>
<span>B</span>
<span>O</span>
<span>W</span>
</h1>
</body>
</html>
function rainbow(str) {
var multiplier = 5;
var result = "";
for (var i = 0; i < str.length; i++) {
result += "<font style='color: hsl(" + i * multiplier % 360 + ", 100%, 70%)'>";
result += str.substr(i, 1);
result += "</font>";
}
return result;
}
And it does the same in less time.
Pretty old question, but if anyone's still looking for a plain javascript ONLY answer to animated rainbow text (of which you can control direction and speed if you look hard enough through my spaghetti), here's my version: https://github.com/FeedbackFox/RGB-CSS-script
Working JSfiddle: https://jsfiddle.net/FeedbackFox/68ekobav/1/
//Script made by FeedbackFox. Please refrain from distributing this outside of github, but feel free to use it in whatever you want, whether commercial or non-commercial.
//https://github.com/FeedbackFox/RGB-CSS-script
(function(){
let textspeed = 1;
let backgroundspeed = 0.1;
let hoverspeed = 10;
let hoverbackgroundspeed = -1;
let classestoberainbowed = document.getElementsByClassName('foxrainbow');
let backgroundtoberainbowed = document.getElementsByClassName('foxrainbowbg');
let spanstoberainbowed = spanArrayContents(classestoberainbowed);
textcolorchange(spanstoberainbowed, textspeed);
//Actually do the rainbow effect. Backgrounds only.
let backgroundcounter = 0;
setInterval(() => {
for(let i = 0; i < backgroundtoberainbowed.length; i++) {
backgroundtoberainbowed[i].style.backgroundColor = 'hsl(' + (backgroundcounter + Math.floor(i * 1)) + ', 100%, 70%';
}
backgroundcounter = backgroundcounter + backgroundspeed;
}, 15);
//Turn the rainbow effect on only when the mouse is over the element. Use foxrainbowhover to use.
let rainbowhover = document.getElementsByClassName('foxrainbowhover');
let invertedhover = document.getElementsByClassName('foxrainbowhoverinv');
let rainbowelements = [];
let rainbowinvelements = [];
let hoverinterval = [];
let hoverinvinterval = [];
let hovercounters = [];
let invcounters = [];
let originalcolors = [];
let originalinvcolors = [];
for(let i = 0; i < rainbowhover.length; i++) {
rainbowelements[i] = spanElementContents(rainbowhover[i]);
}
for(let i = 0; i < invertedhover.length; i++) {
rainbowinvelements[i] = spanElementContents(invertedhover[i]);
}
//Set up the wavey effect with counters.
for(let id = 0; id < rainbowelements.length; id++) {
hovercounters[id] = [];
for(let i = 0; i < rainbowelements[id].length; i++) {
hovercounters[id].push(i);
}
}
for(let id = 0; id < rainbowinvelements.length; id++) {
invcounters[id] = [];
for(let i = 0; i < rainbowinvelements[id].length; i++) {
invcounters[id].push(i);
}
}
//Save the original color to easily return to it later.
for(let i = 0; i < rainbowhover.length; i++) {
originalcolors[i] = rainbowhover[i].style.color;
}
// Add event listeners for every item classed foxrainbowhover. If it has a data tag called foxrainbowhover with an id inside it instead uses that to start the hover effect.
for(let id = 0; id < rainbowhover.length; id++) {
//Checks if the passed along id exists or not. If it doesn't, execute regularly. If it does, execute with hover on a different element.
if(rainbowhover[id].dataset.foxrainbowhover) {
let hoverelement = document.getElementById(rainbowhover[id].dataset.foxrainbowhover);
hoverelement.addEventListener("mouseenter", function startanimation() {
hoverinterval[id] = setInterval(() => {
for(let i = 0; i < rainbowelements[id].length; i++) {
rainbowelements[id][i].style.color = 'hsl(' + (hovercounters[id][i] + Math.floor(i * hoverspeed)) + ', 100%, 70%';
hovercounters[id][i]++;
}
}, 7);
}, false);
hoverelement.addEventListener("mouseleave", function stopanimation() {
console.log("gay1");
clearInterval(hoverinterval[id]);
for(let i = 0; i < rainbowelements[id].length; i++) {
rainbowelements[id][i].style.color = originalcolors[id];
}
}, false);
}
else {
rainbowhover[id].addEventListener("mouseenter", function startanimation() {
hoverinterval[id] = setInterval(() => {
for(let i = 0; i < rainbowelements[id].length; i++) {
rainbowelements[id][i].style.color = 'hsl(' + (hovercounters[id][i] + Math.floor(i * hoverspeed)) + ', 100%, 70%';
hovercounters[id][i]++;
}
}, 7);
}, false);
rainbowhover[id].addEventListener("mouseleave", function stopanimation() {
clearInterval(hoverinterval[id]);
for(let i = 0; i < rainbowelements[id].length; i++) {
console.log("gay1");
rainbowelements[id][i].style.color = originalcolors[id];
}
}, false);
}
}
//Same code as before. Will make it way DRY-er later, but for now, this'll have to do.
for(let i = 0; i < invertedhover.length; i++) {
originalinvcolors[i] = invertedhover[i].style.color;
}
let startinterval = [];
// Add event listeners for every item classed foxrainbowhoverinv.
for(let id = 0; id < invertedhover.length; id++) {
startinterval[id] = setInterval(() => {
for(let i = 0; i < rainbowinvelements[id].length; i++) {
rainbowinvelements[id][i].style.color = 'hsl(' + (invcounters[id][i] + Math.floor(i * hoverspeed)) + ', 100%, 70%';
invcounters[id][i]++;
}
}, 7);
//Checks if the passed along id exists or not. If it doesn't, execute regularly. If it does, execute with hover on a different element.
if(invertedhover[id].dataset.foxrainbowhover) {
let hoverelement = document.getElementById(invertedhover[id].dataset.foxrainbowhover);
hoverelement.addEventListener("mouseenter", function stopanimation() {
clearInterval(startinterval[id]);
clearInterval(hoverinvinterval[id]);
for(let i = 0; i < rainbowinvelements[id].length; i++) {
rainbowinvelements[id][i].style.color = originalinvcolors[id];
}
}, false);
hoverelement.addEventListener("mouseleave", function startanimation() {
hoverinvinterval[id] = setInterval(() => {
for(let i = 0; i < rainbowinvelements[id].length; i++) {
rainbowinvelements[id][i].style.color = 'hsl(' + (invcounters[id][i] + Math.floor(i * hoverspeed)) + ', 100%, 70%';
invcounters[id][i]++;
}
}, 7);
}, false);
}
else {
invertedhover[id].addEventListener("mouseenter", function stopanimation() {
clearInterval(startinterval[id]);
clearInterval(hoverinterval[id]);
for(let i = 0; i < rainbowinvelements[id].length; i++) {
rainbowinvelements[id][i].style.color = originalinvcolors[id];
}
}, false);
invertedhover[id].addEventListener("mouseleave", function startanimation() {
hoverinterval[id] = setInterval(() => {
for(let i = 0; i < rainbowinvelements[id].length; i++) {
rainbowinvelements[id][i].style.color = 'hsl(' + (invcounters[id][i] + Math.floor(i * hoverspeed)) + ', 100%, 70%';
invcounters[id][i]++;
}
}, 7);
}, false);
}
}
//Hover but for backgrounds.
let rainbowhoverbg = document.getElementsByClassName('foxrainbowhoverbg');
let hoverbginterval = [];
let hoverbgcounter = 0;
let originalbgcolors = [];
//Save the original color to easily return to it later, but for backgrounds.
for(let i = 0; i < rainbowhoverbg.length; i++) {
originalbgcolors[i] = rainbowhoverbg[i].style.backgroundColor;
}
for(let id = 0; id < rainbowhoverbg.length; id++) {
rainbowhoverbg[id].addEventListener("mouseenter", function startbganimation() {
hoverbginterval[id] = setInterval(() => {
rainbowhoverbg[id].style.backgroundColor = 'hsl(' + (hoverbgcounter + Math.floor(id * hoverbackgroundspeed)) + ', 100%, 70%';
hoverbgcounter++;
}, 15);
}, false);
rainbowhoverbg[id].addEventListener("mouseleave", function stopbganimation() {
clearInterval(hoverbginterval[id]);
rainbowhoverbg[id].style.backgroundColor = originalbgcolors[id];
}, false);
}
})()
//Actually do the rainbow effect. Text only.
function textcolorchange(rainbowarray, rainbowspeed) {
let counterarray = [];
for(let i = 0; i < rainbowarray.length; i++) {
counterarray[i] = 0 + i;
}
setInterval(() => {
for(let i = 0; i < rainbowarray.length; i++) {
rainbowarray[i].style.color = 'hsl(' + (counterarray[i] + Math.floor(i * rainbowspeed)) + ', 100%, 70%';
if(counterarray[i] == 360)
{
counterarray[i] = 0;
}
else {
counterarray[i]++;
}
}
}, 7);
}
//Prepare text for having its color changed by splicing it up into individual bits
//and taking it out of the HTMLcollection.
function spanArrayContents(classes) {
let spans = [];
let chars = [];
for(let i = 0; i < classes.length; i++) {
chars.push(classes[i].innerText.split(""));
classes[i].innerHTML = chars[i].map(function(char) {
return '<span>' + char + "</span>";
}).join('');
}
for(let i = 0; i < classes.length; i++) {
let temphtmlcollection = [].slice.call(classes[i].children)
for(let j = 0; j < temphtmlcollection.length; j++) {
spans.push(temphtmlcollection[j]);
}
}
return spans;
}
//Same as above except for single elements instead of an array of elements.
//Helps split them up and give them an ID before they're taken to the slaughterhouse.
function spanElementContents(element) {
let spans = [];
let chars = [];
chars.push(element.innerText.split(""));
for(let i = 0; i < chars.length; i++){
element.innerHTML = chars[i].map(function(char) {
return '<span>' + char + "</span>";
}).join('');
}
let temphtmlcollection = [].slice.call(element.children)
for(let j = 0; j < temphtmlcollection.length; j++) {
spans.push(temphtmlcollection[j]);
}
return spans;
}