Swiperjs in web component return duplicated items - javascript

I'm creating a custom carousel web component with using Swiperjs as part of the element.
The quantity of items is dynamic, it is depend on the value that get from the web component attribute sliderBgImage and sliderCopyImage.
Then, it will loop thru the array and implementing the items with Swiper.js carousel library.
Hope someone can advise on this, thank you!
index.html
<div style="width:320px;height:480px;">
<verticalsliderBgImage="banner1.png,banner2.png" sliderCopyImage="copy1.png,copy2.png" sliderlanding1="http://www.google.com" sliderlanding2="http://www.test.com" sliderlanding3="http://www.test.com" sliderlanding4="" sliderlanding5="" autoplay="true" speed="2000" pagingColor="3eff8a"></vertical>
</div>
<script src="vertical.js" type="module"></script>
<script src="https://s0.2mdn.net/creatives/assets/4671853/swiper.min.js"></script>
</body>
Vertical.js
template.innerHTML = `
<link rel="stylesheet" href="./swiper.min.css"/><link rel="stylesheet" href="./vertical.css"/><div id="mainContainer"><div class="swiper-container"><div class="swiper-wrapper"></div><div class="swiper-pagination"></div></div></div>`;
class Vertical extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: "open" });
this.shadowRoot.appendChild(template.content.cloneNode(true));
}
handleSliderClick = () => {
let slides = this.shadowRoot.querySelectorAll(".swiper-slide");
for (let i = 0; i < slides.length; i++) {
const slide = slides[i];
slide.addEventListener("click", () => {
const getslide = slide.getAttribute("data");
const landing = getslide.slice(-1);
Enabler.exitOverride(getslide, this.getAttribute(landing));
});
}
};
setPaginateColor = () => {
const activeBullet = this.shadowRoot.querySelector(
".swiper-pagination-bullet-active"
);
if (activeBullet) {
activeBullet.style.backgroundColor = `#${this.getAttribute(
"pagingColor"
)}`;
}
};
darkNav = () => {
var slides = this.shadowRoot.querySelectorAll(".swiper-slide");
var copies = this.shadowRoot.querySelectorAll(".copies");
for (let i = 0, c = 0; i < slides.length, c < copies.length; i++, c++) {
const slide = slides[i];
const copy = copies[c];
if (
slide.classList.contains("swiper-slide-active") ||
slide.classList.contains("swiper-slide-duplicate-active")
) {
slide.classList.add("bubbleslide");
this.setPaginateColor();
setTimeout(() => {
copy.classList.add("slide-top");
}, 200);
} else {
slide.classList.remove("bubbleslide");
copy.classList.remove("slide-top");
}
}
};
setSwiper = (ele) => {
const setAutoplay = this.getAttribute("autoplay");
const setSpeed = this.getAttribute("speed");
var swiper = new Swiper(ele, {
autoplay: {
delay: setSpeed,
},
direction: "vertical",
pagination: {
el: this.shadowRoot.querySelector(".swiper-pagination"),
clickable: true,
},
loop: true,
on: {
slideChangeTransitionStart: this.darkNav,
},
});
this.setPaginateColor();
if (setAutoplay === "true" || setAutoplay === "TRUE") {
swiper.autoplay.start();
} else {
swiper.autoplay.stop();
}
this.handleSliderClick();
setTimeout(() => {
swiper.autoplay.stop();
}, 30000);
};
static get observedAttributes() {
return [
"brandLogo",
"sliderBgImage",
"sliderCopyImage",
"pagingColor",
"autoplay",
"speed",
];
}
attributeChangedCallback(name, oldValue, newValue) {
var bgImgs = this.getAttribute("sliderBgImage").split(",");
var copyImgs = this.getAttribute("sliderCopyImage").split(",");
var items = [];
// clean up each item
for (let i = 0; i < bgImgs.length; i++) {
let val1 = bgImgs[i];
let val2 = copyImgs[i];
let obj = {
bg: val1,
tag: val2,
};
items.push(obj);
}
}
connectedCallback() {
var bgImgs = this.getAttribute("sliderBgImage").split(",");
var copyImgs = this.getAttribute("sliderCopyImage").split(",");
var items = [];
// clean up each item
for (let i = 0; i < bgImgs.length; i++) {
let val1 = bgImgs[i];
let val2 = copyImgs[i];
let obj = {
bg: val1,
tag: val2,
};
items.push(obj);
}
let checkItem = null;
const swiperContainer = this.shadowRoot.querySelector(".swiper-wrapper");
const swiperMain = this.shadowRoot.querySelector(".swiper-container");
items.map((v, k) => {
let html = `<div class="swiper-slide slide${k + 1}" data="slide${
k + 1
}" style="background-image:url('${v.bg}')"><div class="copies copy${
k + 1
}" style="background-image:url('${v.tag}')"></div></div>`;
checkItem++;
return (swiperContainer.innerHTML += html);
});
let check = setInterval(() => {
if (checkItem === items.length) {
this.setSwiper(swiperMain);
clearInterval(check);
}
}, 500);
}
}
export default Vertical;

Related

Javascript Cannot access class before initialization

I have the following bit of code whereby I'm using class expressions
const ShapeOverlays = class {
constructor(elm) {
this.elm = elm;
this.path = elm.querySelectorAll('path');
this.numPoints = 4;
this.duration = 1000;
this.delayPointsArray = [];
this.delayPointsMax = 0;
this.delayPerPath = 60;
this.timeStart = Date.now();
this.isOpened = false;
this.isAnimating = false;
}
}
(function() {
const elmHamburger = document.querySelector('.hamburger');
const gNavItems = document.querySelectorAll('.global-menu__item');
const elmOverlay = document.querySelector('.shape-overlays');
const overlay = new ShapeOverlays(elmOverlay);
elmHamburger.addEventListener('click', () => {
if (overlay.isAnimating) {
return false;
}
overlay.toggle();
if (overlay.isOpened === true) {
elmHamburger.classList.add('is-opened-navi');
for (var i = 0; i < gNavItems.length; i++) {
gNavItems[i].classList.add('is-opened');
}
} else {
elmHamburger.classList.remove('is-opened-navi');
for (var i = 0; i < gNavItems.length; i++) {
gNavItems[i].classList.remove('is-opened');
}
}
});
}());
But I'm getting the error
Uncaught ReferenceError: Cannot access 'ShapeOverlays' before initialization
on the line const overlay = new ShapeOverlays(elmOverlay); which is weird because the class has been initialized above. What I'm I doing wrong? Thanks.
Your class expression is missing a semicolon.
const ShapeOverlays = class {
constructor(elm) {
// ...
}
}; // <-- this one
Working:
const Test = class {
constructor() {
this.isOpened = false;
this.isAnimating = false;
}
};
(function() {
const overlay = new Test(4);
console.log(overlay)
}());
Not working:
const Test = class {
constructor() {
this.isOpened = false;
this.isAnimating = false;
}
}
(function() {
const overlay = new Test(4);
console.log(overlay)
}());
you have one too may (...). So, if you moved it to the following I think it should work fine.
const ShapeOverlays = class {
constructor(elm) {
this.elm = elm;
this.path = elm.querySelectorAll("path");
this.numPoints = 4;
this.duration = 1000;
this.delayPointsArray = [];
this.delayPointsMax = 0;
this.delayPerPath = 60;
this.timeStart = Date.now();
this.isOpened = false;
this.isAnimating = false;
}
};
(function () {
const elmHamburger = document.querySelector(".hamburger");
const gNavItems = document.querySelectorAll(".global-menu__item");
const elmOverlay = document.querySelector(".shape-overlays");
const overlay = new ShapeOverlays(elmOverlay);
elmHamburger.addEventListener("click", () => {
if (overlay.isAnimating) {
return false;
}
overlay.toggle();
if (overlay.isOpened === true) {
elmHamburger.classList.add("is-opened-navi");
for (var i = 0; i < gNavItems.length; i++) {
gNavItems[i].classList.add("is-opened");
}
} else {
elmHamburger.classList.remove("is-opened-navi");
for (var i = 0; i < gNavItems.length; i++) {
gNavItems[i].classList.remove("is-opened");
}
}
});
})();
Let me know if it does NOT help. Thanks.
It works if you declare the class inside the function, its happening because you are instantly declaring and invoking the function so the scope is lexical in terms to the globally declared class. See this for reference:https://developer.mozilla.org/en-US/docs/Glossary/IIFE
(function() {
const ShapeOverlays = class {
constructor(elm) {
this.elm = elm;
this.path = elm.querySelectorAll('path');
this.numPoints = 4;
this.duration = 1000;
this.delayPointsArray = [];
this.delayPointsMax = 0;
this.delayPerPath = 60;
this.timeStart = Date.now();
this.isOpened = false;
this.isAnimating = false;
}
}
const elmHamburger = document.querySelector('.hamburger');
const gNavItems = document.querySelectorAll('.global-menu__item');
const elmOverlay = document.querySelector('.shape-overlays');
const overlay = new ShapeOverlays(elmOverlay);
elmHamburger.addEventListener('click', () => {
if (overlay.isAnimating) {
return false;
}
overlay.toggle();
if (overlay.isOpened === true) {
elmHamburger.classList.add('is-opened-navi');
for (var i = 0; i < gNavItems.length; i++) {
gNavItems[i].classList.add('is-opened');
}
} else {
elmHamburger.classList.remove('is-opened-navi');
for (var i = 0; i < gNavItems.length; i++) {
gNavItems[i].classList.remove('is-opened');
}
}
});
}());

Click event listener is being repeatedly triggered without any clicks?

I am developing a "Battleship" game with two grids made up of divs and am currently attempting to add a click event listener to all of the divs.
The issue that I am having is that the event listener is being repeatedly triggered (until every single div has been clicked) when I refresh my page and I can't understand why...
Here's the event listener in question:
let aiGridCells = document.querySelectorAll(".ai-grid__game-cell");
aiGridCells.forEach(cell => {
cell.addEventListener("click", humanPlayer.humanAttack(cell.getAttribute('data-ai'),aiPlayer))
});
Where humanPlayer is an object that has been generated by a factory function:
const humanPlayer = playerFactory('human');
import gameboardFactory from './gameboardFactory';
const playerFactory = (name) => {
const playerBoard = gameboardFactory();
const humanAttack = (cell, player) => { // Where player is opponent
if (player.playerBoard.gameBoard[cell].id !== 'miss') {
player.playerBoard.receiveAttack(cell);
};
};
const aiAttack = (player) => { // Where player is opponent
const availableCells = [];
for (let i = 0; i < player.playerBoard.gameBoard.length; i++) {
if (player.playerBoard.gameBoard[i].id === null) {
availableCells.push(i);
};
};
const attackCell = Math.floor(Math.random() * availableCells.length);
player.playerBoard.receiveAttack(attackCell);
};
return {
name,
playerBoard,
humanAttack,
aiAttack
}
};
export default playerFactory;
and gameboardFactory is:
import shipFactory from './shipFactory';
const gameboardFactory = () => {
const gameBoard = [];
const shipYard = [];
const init = () => {
for (let i = 0; i<100; i++) {
gameBoard.push({id: null})
};
};
const checkValidCoordinates = (direction, start, end) => {
if (direction === 'horizontal') {
if ((start <= 9) && (end <= 9)) {
return true;
} else {
let newStart = (start/10).toString(10);
let newEnd = (end/10).toString(10);
if ((newStart.charAt(0)) === (newEnd.charAt(0))) {
return true;
};
};
} else {
if ((start <= 9) && (end <= 9)) {
return false
} else if (start <= 9) {
let newStart = start.toString(10);
let newEnd = end.toString(10);
if ((newStart.charAt(0)) === (newEnd.charAt(1))) {
return true;
};
} else {
let newStart = start.toString(10);
let newEnd = end.toString(10);
if ((newStart.charAt(1)) === (newEnd.charAt(1))) {
return true;
};
};
};
return false
};
const checkIfShipPresent = (direction, start, end) => {
if (direction === 'horizontal') {
for (let i = start; i <= end; i++) {
if (gameBoard[i].id !== null) {
return true;
}
};
return false;
} else {
for (let i = start; i <= end; i += 10) {
if (gameBoard[i].id !== null) {
return true;
}
};
return false;
};
};
const placeShip = (id, direction, start, end) => {
if (!checkValidCoordinates(direction, start, end)) {
return;
};
if (checkIfShipPresent(direction, start, end)) {
return;
};
const newShip = [];
if (direction === 'horizontal') {
for (let i = start; i <= end; i++) {
gameBoard[i].id = id;
newShip.push(i);
};
} else {
for (let i = start; i <= end; i += 10) {
gameBoard[i].id = id;
newShip.push(i);
};
};
shipYard.push(shipFactory(id, newShip));
};
const receiveAttack = (cell) => {
console.log(cell)
if (gameBoard[cell].id !== null) {
const attackedShip = shipYard.filter((ship) => {
return ship.id === gameBoard[cell].id;
})[0];
if (!attackedShip.hits.includes(cell)) {
attackedShip.hits.push(cell);
};
} else {
gameBoard[cell].id = 'miss';
};
};
const checkIfAllShipsSunk = () => {
let allShipsSunk = true;
shipYard.forEach((ship) => {
if (ship.isSunk() === false) {
allShipsSunk = false;
};
});
return allShipsSunk;
};
if (gameBoard.length === 0) {
init();
};
return {
gameBoard,
placeShip,
receiveAttack,
shipYard,
checkIfAllShipsSunk
};
};
export default gameboardFactory;
I'm completely lost to what the issue could be and have tried countless things to rectify it. Any suggestions would be hugely appreciated.
Thank you!
You trying to add actual function call as listener here:
let aiGridCells = document.querySelectorAll(".ai-grid__game-cell");
aiGridCells.forEach(cell => {
cell.addEventListener("click", humanPlayer.humanAttack(cell.getAttribute('data-ai'),aiPlayer))
});
So on your event listener initialization you actually call your function instead of passing it as a listener.
You can pass it like this instead:
aiGridCells.forEach(cell => {
cell.addEventListener("click", () => humanPlayer.humanAttack(cell.getAttribute('data-ai'),aiPlayer))
});

How to pause the entire code for 1 second in javascript

I am trying to code an evolutionary neural network for the game snake. I already coded the neural network part and now I'd like to output the game of the best individual of every generation. For that I'm using the drawing library p5.js (https://p5js.org/).
In my code I am running a loop in where a new generation based on the last generation is created. Each individual of the generation will have to play the game and that is how they are rated. Now I want the best individual to be outputted after I let every individual play once.
Between each outputted turn of the game of the best individual I want the code to wait 500 milliseconds. How do I achieve that?
Here is the code I've already tried but here it only outputed the board after the last turn of each generation:
async function start() {
for (let i = 0; i < 50; i++) {
population.createNewGeneration();
let bestGameTurns = population.bestIndividual.game.turns; //Array of boards
for (let turn = 0; turn < bestGameTurns.length; turn++) {
let board = bestGameTurns[turn];
drawBoard(board);
let p = new Promise(resolve => setTimeout(resolve, 500));
await p;
function drawBoard(board) {
//Draw the board using p5.js rect()'s
}
}
}
}
Another version but the waiting didn't work here:
let i = 0;
setInterval(async () => {
population.createNewGeneration();
console.log(i, population.avgFitness);
let bestGameTurns = population.bestIndividual.game.turns; //Array of boards
for (let turn = 0; turn < bestGameTurns.length; turn++) {
let board = bestGameTurns[turn];
drawBoard(board);
let p = new Promise(resolve => setTimeout(resolve, 500));
await p;
function drawBoard(board) {
//Draw the board using p5.js rect()'s
}
}
i++;
}, 1);
The code you provided should do what you asked for, I could only clean up some parts for you. Explain a bit better what is the problem you are facing.
// The function should be defined only once.
function drawBoard(board) { }
async function start() {
for (let i = 0; i < 50; i++) {
population.createNewGeneration();
const bestGameTurns = population.bestIndividual.game.turns; //Array of boards
for (let turn = 0; turn < bestGameTurns.length; turn++) {
// Don't wait on first iteration
await new Promise(resolve => setTimeout(resolve, 500 * (turn ? 0 : 1 )));
drawBoard(bestGameTurns[turn]);
}
}
}
Original idea (discarded)
You can create a short function like that:
function pause(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
Then in any async function you can call it like that:
async function () {}
// something happening
await pause(500);
// continue
}
The other idea
Now, the code in your question is not complete so this is kind of blind coding but.
So, first of all setInterval will be running the whole function every 1 millisecond (actually 4 ms, as this is the minimum in JS). Which means it will run those loops. I decided to focus on the part that was marked by you.
Instead of loop and trying to pause it, I ask that maybe rewriting the loop into function called each half second until condition is met would do?
Also, I move drawBoard outside
setInterval(async () => {
// ^^^^^^^^ <-- this should probably go
population.createNewGeneration();
console.log(i, population.avgFitness);
let bestGameTurns = population.bestIndividual.game.turns; //Array of boards
function tick(turn = 0) {
let board = bestGameTurns[turn];
function drawBoard(board) {
//Draw the board using p5.js rect()'s
}
drawBoard(board);
// here is "setTimeouted" loop
if (turn < bestGameTurns.length) {
setTimeout(tick, 500, turn + 1);
}
}
tick();
}, 1);
Thanks to everyone, your suggestions brought me to an idea. I found out that the problem was lying somewhere else. Because javascript only runs on the one thread (I think thats how its called), after we run over one generation, we have to stop that function to let another draw function, which runs every frame, draw the board. After it is drawn, the main function can continue. This is how it looks:
let isDrawn = false;
let currentBoard;
async function setup() {
for (let i = 0; i < 50; i++) {
population.createNewGeneration();
const bestGameTurns = population.bestIndividual.game.turns;
for (let turn = 0; turn < bestGameTurns.length; turn++) {
await step(bestGameTurns[turn], turn);
}
}
}
function step(board, turn) {
currentBoard = board;
isDrawn = false;
return new Promise(resolve => setTimeout(() => {
if (isDrawn) resolve();
}, 500));
}
setTimeout(() => {
if (currentBoard) {
drawBoard(currentBoard);
isDrawn = true;
currentBoard = undefined;
}
}, 1);
const nrOfCols = 10;
const nrOfRows = 10;
const fieldWidth = 20;
const nodeNrs = [24, 8, 8, 4];
const populationSize = 200;
const mutationRate = 0.01;
let population;
let game;
let isDrawn = false;
let currentBoard;
async function setup() {
createCanvas(500, 500);
population = new PopulationManager(populationSize);
for (let i = 0; i < 50; i++) {
population.createNewGeneration();
const bestGameTurns = population.bestIndividual.game.turns;
for (let turn = 0; turn < bestGameTurns.length; turn++) {
await step(bestGameTurns[turn]);
}
}
}
function step(board) {
currentBoard = board;
isDrawn = false;
return new Promise(resolve => setTimeout(() => {
if (isDrawn) resolve();
}, 500));
}
function draw() {
if (currentBoard) {
drawBoard(currentBoard);
isDrawn = true;
currentBoard = undefined;
}
}
function drawBoard(board) {
board.forEach((col, colNr) => {
col.forEach((field, rowNr) => {
fill(field.isSnake ? "green" : field.isFruit ? "red" : "black");
stroke(255);
rect(colNr*fieldWidth, rowNr*fieldWidth, fieldWidth, fieldWidth);
});
});
}
function play(game) {
setInterval(() => {
if (!game.lost) {
game.nextTurn();
drawBoard(game.board);
} else {
clearInterval(1);
}
}, 500);
}
class PopulationManager {
constructor(populationSize) {
this.population = createPopulation();
function createPopulation() {
let population = [];
for (let i = 0; i < populationSize; i++) {
let chromosomes = createRandomChromosomes();
let i = new Individual(chromosomes);
population.push(i);
}
return population;
function createRandomChromosomes() {
let arr = [];
let nrOfChromosomes = calcNrOfChromosomes();
for (let i = 0; i < nrOfChromosomes; i++)
arr.push(Math.random()*2-1);
return arr;
function calcNrOfChromosomes() {
let nr = 0;
for (let i = 0; i < nodeNrs.length - 1; i++)
nr += (nodeNrs[i] + 1)*nodeNrs[i + 1];
return nr;
}
}
};
}
createNewGeneration() {
let that = this;
getFitnessOfPop();
this.calcAvgFitness();
this.findBestIndividual();
let parents = selection();
breed(parents);
function getFitnessOfPop() {
that.population.forEach(iv => {
iv.fitness = iv.playGame();
});
that.population.sort((a, b) => a.fitness - b.fitness);
}
function selection() {
let totalFitness = that.population.map(iv => iv.fitness/* + 1 */).reduce((a,b) => a + b);
let allParents = [];
for (let i = 0; i < that.population.length/2; i++) {
allParents.push(selectRandomParents());
}
return allParents;
function selectRandomParents() {
let p1, p2;
do {
p1 = selectRandomParent();
p2 = selectRandomParent();
} while (p1 == p2);
return [p1, p2];
function selectRandomParent() {
let rdm = Math.random()*totalFitness;
return that.population.find((iv, i) => {
let sum = that.population.filter((iv2, i2) => i2 <= i).map(iv => iv.fitness /* + 1 */).reduce((a,b) => a + b);
return rdm <= sum;
});
}
}
}
function breed(allParents) {
that.population = [];
allParents.forEach(ps => {
let childChromosomes = crossOver(ps[0].chromosome, ps[1].chromosome);
childChromosomes = [mutate(childChromosomes[0]), mutate(childChromosomes[1])];
let child1 = new Individual(childChromosomes[0]);
let child2 = new Individual(childChromosomes[1]);
that.population.push(child1);
that.population.push(child2);
});
function crossOver(parent1Chromosome, parent2Chromosome) {
let crossingPoint = Math.round(Math.random()*parent1Chromosome.length);
let divided1 = divideChromosome(parent1Chromosome);
let divided2 = divideChromosome(parent2Chromosome);
let child1Chromosome = divided1[0].concat(divided2[1]);
let child2Chromosome = divided2[0].concat(divided1[1]);
return [child1Chromosome, child2Chromosome];
function divideChromosome(chromosome) {
let part1 = chromosome.filter((g, i) => i <= crossingPoint);
let part2 = chromosome.filter((g, i) => i > crossingPoint);
return [part1, part2];
}
}
function mutate(chromosome) {
chromosome = chromosome.map(g => {
if (Math.random() < mutationRate)
return Math.random()*2-1;
return g;
});
return chromosome;
}
}
}
calcAvgFitness() {
this.avgFitness = this.population.map(iv => iv.fitness).reduce((a, b) => a + b) / this.population.length;
}
findBestIndividual() {
let bestFitness = -1, bestIndividual;
this.population.forEach(i => {
if (i.fitness > bestFitness) {
bestFitness = i.fitness;
bestIndividual = i;
}
});
this.bestIndividual = bestIndividual;
}
}
class Individual {
constructor(chromosome) {
this.chromosome = chromosome;
this.fitness = 0;
this.game = createGame();
function createGame() {
let weights = convertChromosomeToWeights();
let game = new Game(weights);
return game;
function convertChromosomeToWeights() {
let weights = [];
for (let i = 0; i < nodeNrs.length - 1; i++) {
let lArr = [];
for (let j = 0; j < nodeNrs[i] + 1; j++) {
let nArr = [];
lArr.push(nArr);
}
weights.push(lArr);
}
chromosome.forEach((gene, geneIdx) => {
let lIdx = -1, minIdx, maxIdx = 0;
for (let i = 0; i < nodeNrs.length - 1; i++) {
let nr = 0;
for (let j = 0; j <= i; j++)
nr += (nodeNrs[j] + 1)*nodeNrs[j + 1];
if (geneIdx < nr) {
lIdx = i;
break;
}
maxIdx = nr;
minIdx = maxIdx;
}
minIdx = maxIdx;
let nIdx = -1;
for (let i = 0; i < nodeNrs[lIdx] + 1; i++) {
let nr = minIdx + nodeNrs[lIdx + 1];;
if (geneIdx < nr) {
nIdx = i;
break;
}
maxIdx = nr;
minIdx = maxIdx;
}
minIdx = maxIdx;
let wIdx = -1;
for (let i = 0; i < nodeNrs[lIdx + 1]; i++) {
let nr = minIdx + 1;
if (geneIdx < nr) {
wIdx = i;
break;
}
maxIdx = nr;
minIdx = maxIdx;
}
weights[lIdx][nIdx][wIdx] = gene;
});
return weights;
}
}
}
playGame() {
while (!this.game.lost) {
this.game.nextTurn();
}
return this.game.score;
}
}
class Game {
constructor(weights) {
let that = this;
this.chromosome = flattenArray(weights);
this.nn = new NeuralNetwork(weights);
this.turnNr = 0;
this.score = 0;
this.lost = false;
this.board = createBoard();
this.snake = new Snake();
setupSnake();
this.createFruit();
this.turns = [JSON.parse(JSON.stringify(this.board))];
function createBoard() {
let board = [];
for (let colNr = 0; colNr < nrOfCols; colNr++) {
board[colNr] = [];
for (let rowNr = 0; rowNr < nrOfRows; rowNr++) {
let field = new Field(colNr, rowNr);
board[colNr][rowNr] = field;
}
}
return board;
}
function setupSnake() {
for (let i = 0; i < 4; i++)
that.addToTail([floor(nrOfCols/2) - i, floor(nrOfRows/2)]);
that.length = that.snake.body.length;
}
function flattenArray(arr) {
let flattened = [];
flatten(arr);
return flattened;
function flatten(arr) {
arr.forEach(e => {
if (Array.isArray(e))
flatten(e);
else
flattened.push(e);
});
}
}
}
addToTail(pos) {
this.snake.body.push(pos);
this.board[pos[0]][pos[1]].setSnake(true);
}
nextTurn() {
let that = this;
let direction = findDirection();
this.moveSnake(direction);
this.turns.push(JSON.parse(JSON.stringify(this.board)));
this.turnNr++;
checkEat();
function findDirection() {
let inputValues = [];
for (let i = 0; i < 8; i++) {
let distances = that.snake.look(i, that.board);
inputValues.push(distances.distToFruit);
inputValues.push(distances.distToWall);
inputValues.push(distances.distToBody);
}
let output = that.nn.getOutput(inputValues);
let probability = -1;
let direction = -1;
output.forEach((v, vIdx) => {
if (v > probability) {
probability = v;
direction = vIdx;
}
});
return direction;
}
function checkEat() {
let head = that.snake.body[0];
let headField = that.board[head[0]][head[1]];
if (headField.isFruit) {
that.snake.eat();
that.score++;
headField.setFruit(false);
that.createFruit();
}
}
}
createFruit() {
let field;
do {
let colNr = floor(random()*nrOfCols);
let rowNr = floor(random()*nrOfRows);
field = this.board[colNr][rowNr];
} while(field.isSnake);
field.setFruit(true);
}
moveSnake(newDirection) {
let that = this;
let oldBody = JSON.parse(JSON.stringify(that.snake.body));
moveTail();
makeSnakeLonger();
moveHead();
function moveTail() {
for (let i = oldBody.length - 1; i > 0; i--)
that.snake.body[i] = oldBody[i - 1];
}
function moveHead() {
let newHeadPosition = findNewHeadPos();
if (
newHeadPosition[0] >= nrOfCols || newHeadPosition[0] < 0 ||
newHeadPosition[1] >= nrOfRows || newHeadPosition[1] < 0
) {
that.lose();
return;
}
let newHeadField = that.board[newHeadPosition[0]][newHeadPosition[1]];
if (newHeadField.isSnake) {
that.lose();
return;
}
addNewHeadPos(newHeadPosition);
}
function findNewHeadPos() {
if (newDirection == 0) { //up
return [oldBody[0][0], oldBody[0][1] - 1];
} else if (newDirection == 1) { //right
return [oldBody[0][0] + 1, oldBody[0][1]];
} else if (newDirection == 2) { //down
return [oldBody[0][0], oldBody[0][1] + 1];
} else if (newDirection == 3) { //left
return [oldBody[0][0] - 1, oldBody[0][1]];
}
}
function makeSnakeLonger() {
if (that.snake.length > that.snake.body.length) {
that.addToTail(oldBody[oldBody.length - 1]);
} else {
removeFromTail(oldBody[oldBody.length - 1]);
}
}
function removeFromTail(pos) {
that.board[pos[0]][pos[1]].setSnake(false);
}
function addNewHeadPos(pos) {
that.snake.body[0] = pos;
that.board[pos[0]][pos[1]].setSnake(true);
}
}
lose() {
this.lost = true;
}
}
class Field {
constructor(col, row) {
this.col = col;
this.row = row;
this.isFruit = false;
this.isSnake = false;
}
setFruit(bool) {
this.isFruit = bool;
}
setSnake(bool) {
this.isSnake = bool;
}
}
class Snake {
constructor() {
this.body = [];
this.length = 4;
}
eat() {
this.length++;
}
look(direction, board) {
let distances = {distToFruit: 0, distToWall: 0, distToBody: 0};
let xDiff = getXDiff(direction), yDiff = getYDiff(direction);
let pos = [this.body[0][0] + xDiff, this.body[0][1] + yDiff];
let dist = 1;
while (pos[0] > 0 && pos[0] < nrOfRows && pos[1] > 0 && pos[1] < nrOfCols) {
if (board[pos[0]][pos[1]].isFruit && distances.distToFruit == 0) distances.distToFruit = dist;
if (board[pos[0]][pos[1]].isSnake && distances.distToBody == 0) distances.distToBody = dist;
pos[0] += xDiff, pos[1] += yDiff;
dist++;
}
distances.distToWall = dist;
return distances;
function getXDiff(direction) {
if (direction == 5 || direction == 6 || direction == 7) return -1;
else if (direction == 1 || direction == 2 || direction == 3) return 1;
return 0;
}
function getYDiff(direction) {
if (direction == 7 || direction == 0 || direction == 1) return -1;
else if (direction == 3 || direction == 4 || direction == 5) return 1;
return 0;
}
}
}
class NeuralNetwork {
constructor(weights) {
this.layers = createLayers();
this.layers = addWeights(this.layers, weights);
function createLayers() {
let layers = [];
let nrOfNodesGlobal;
nodeNrs.forEach((nrOfNodes, lNr) => {
nrOfNodesGlobal = nrOfNodes;
layers[lNr] = [];
for (let i = 0; i < nrOfNodes; i++) {
let node = createNode(lNr);
layers[lNr][i] = node;
}
if (lNr != nodeNrs.length - 1)
layers[lNr].push(new Bias());
});
return layers;
function createNode(lNr) {
if (lNr == 0) return new InputLayerNode();
else if (lNr == nrOfNodesGlobal - 1) return new OutputLayerNode();
else return new HiddenLayerNode();
}
}
function addWeights(layers, weights) {
for (let lNr = 0; lNr < layers.length - 1; lNr++) {
let l = layers[lNr];
l.forEach((n1, nNr) => {
for (let n2Nr = 0; n2Nr < layers[lNr+1].length - 1; n2Nr++) { //not including bias of next layer
let n2 = layers[lNr+1][n2Nr];
let weight = weights[lNr][nNr][n2Nr];
let w = new Weight(n1, n2, weight);
n1.addWeight(w);
}
});
}
return layers;
}
}
getOutput(inputValues) {
let output = [];
this.layers[0].forEach((inputNeuron, nNr) => {
if (nNr != this.layers[0].length - 1)
inputNeuron.addToInput(inputValues[nNr]);
});
this.layers.forEach((l, lNr) => {
calcOutputs(l);
if (lNr != this.layers.length - 1) {
l.forEach(n => {
n.feedForward();
});
} else {
output = l.map(n => n.output);
}
});
return output;
function calcOutputs(layer) {
layer.forEach(n => n.output = n.activationFunction(n.summedInput, layer.map(N => N.summedInput)));
}
}
log() {
console.log(this.weights, this.nodes);
}
}
class Node {
constructor() {
this.weights = [];
this.summedInput = 0;
}
addWeight(w) {
this.weights.push(w);
}
addToInput(input) {
if (input == NaN)
console.log("A");
this.summedInput += input;
}
feedForward() {
this.weights.forEach((w, wNr) => {
let input = w.weight*this.output;
w.to.addToInput(input);
});
}
}
class Bias extends Node {
constructor() {
super();
this.output = 1;
}
activationFunction(x, allXs) {
return x;
}
}
class InputLayerNode extends Node {
constructor() {
super();
}
activationFunction(x, allXs) {
return x;
}
}
class HiddenLayerNode extends Node {
constructor() {
super();
}
activationFunction(x, allXs) {
return leakyReLU(x);
}
}
class OutputLayerNode extends Node {
constructor() {
super();
}
activationFunction(x, allXs) {
return softmax(x, allXs);
}
}
class Weight {
constructor(from, to, weight) {
this.from = from;
this.to = to;
this.weight = weight;
}
setWeight(newWeight) {
this.weight = weight;
}
}
function leakyReLU(x) {
if (x >= 0) return x;
else return 0.01*x;
}
function softmax(x, allXs) {
return Math.exp(x) / allXs.map(X => Math.exp(X)).reduce((a, b) => a+b);
}
<!DOCTYPE html>
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.8.0/p5.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.8.0/addons/p5.dom.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.8.0/addons/p5.sound.min.js"></script>
<link rel="stylesheet" type="text/css" href="style.css">
<meta charset="utf-8" />
</head>
<body>
<script src="sketch.js"></script>
</body>
</html>
It's not working that well but a few improvements should make it better...
If you have any suggestions for improvements of the code, please let me know!
I tried to fix it into steps as I said in comments,I hope I have no mistake :
let i = 0;
async function step(bestGameTurns, turn)
{
if (turn == bestGameTurns.length)
return;
let board = bestGameTurns[turn];
drawBoard(board);
let p = new Promise(resolve => setTimeout(() => step(bestGameTurns, turn+1), 500));
await p;
}
function drawBoard(board) {
//Draw the board using p5.js rect()'s
}
setInterval(async () => {
population.createNewGeneration();
console.log(i, population.avgFitness);
let bestGameTurns = population.bestIndividual.game.turns; //Array of boards
step(bestGameTurns, 0);
i++;
}, 1);

How I can set images and custom styles to operator header title If the JSON key matches only?

import { Component, OnInit, AfterViewInit, Input, Output, EventEmitter, OnChanges, SimpleChanges, OnDestroy } from '#angular/core';
import { interval } from 'rxjs/observable/interval';
import { IChartModel } from './model/flow-data';
import { HelperService } from '../../core/helper/helper.service';
import { NgFlowchartService } from './ng-flowchart.service';
declare let $;
#Component({
selector: 'app-ng-flowchart',
templateUrl: './ng-flowchart.component.html',
styleUrls: ['./ng-flowchart.component.css'],
})
export class NgFlowchartComponent implements OnInit, AfterViewInit, OnChanges, OnDestroy {
#Input('data') flowchartData: IChartModel;
#Input() event: any;
currentElement = 'dp_flowchart';
// operatorId = 2;
operatorId: string;
// flowchartData;
possibleZooms = [0.5, 0.75, 1, 2, 3];
currentZoom = 2;
subscribeInterval: any;
constructor(
private helperService: HelperService,
private ngFlowchartService: NgFlowchartService
) { }
private autoSaveWorkflow() {
const $flowchart = $('#' + this.currentElement);
const source = interval(1000);
this.subscribeInterval = source.subscribe(val => {
const flowchartData = $flowchart.flowchart('getData');
const currentData = JSON.stringify(flowchartData);
const previousData = localStorage.getItem('ui.flowchart');
if (previousData !== currentData) {
localStorage.setItem('ui.flowchart', currentData);
}
});
}
ngOnInit() {
this.ngFlowchartService.currentMessage.subscribe(message => {
if (message.action && message.action === 'ng-clear-editor') {
setTimeout(() => {
this.clearEditor();
}, 100);
}
});
}
setFlowchartData(data) {
const $flowchart = $('#' + this.currentElement);
$flowchart.flowchart('setData', data);
}
ngOnChanges(changes: SimpleChanges) {
// console.log(changes);
this.dataChanges(changes);
}
private initFlowchartData() {
if (localStorage.getItem('ui.flowchart')) {
return JSON.parse(localStorage.getItem('ui.flowchart'));
} else {
return this.flowchartData;
}
}
ngAfterViewInit() {
setTimeout(() => {
this.advanceOption();
this.autoSaveWorkflow();
}, 100);
}
private dataChanges(changes) {
if (changes.hasOwnProperty('event')) {
if (changes.event.currentValue) {
if (changes.event.previousValue) {
/** Have current and previous state */
const data = {
curIn: +(changes.event.currentValue.inputCount),
preIn: +(changes.event.previousValue.inputCount),
curOut: +(changes.event.currentValue.outputCount),
preOut: +(changes.event.previousValue.outputCount)
};
if (data.curIn !== data.preIn || data.curOut !== data.preOut) {
this.addEndPoint(changes.event.currentValue);
}
} else {
/** Have only current state */
const data = {
curIn: +(changes.event.currentValue.inputCount),
curOut: +(changes.event.currentValue.outputCount)
};
if (data.curIn !== 1 || data.curOut !== 1) {
this.addEndPoint(changes.event.currentValue);
}
}
}
}
}
private addEndPoint(endpoint) {
// console.log(endpoint);
/**
* 1. Find the component in the chart data (using unique identifier - type as 'Event')
* 2. If already +++++input & output endpoints there then create remaining only
* 3. If input or output count less the previous one and have a connection (link)
* associated with the endpoint then popup comfirmation dialog for removing the
* desired in/out.
*/
const $flowchart = $('#' + this.currentElement);
const flowchartData = $flowchart.flowchart('getData');
Object.keys(flowchartData['operators']).forEach((key) => {
if (endpoint.eventName === flowchartData.operators[key].type.eventName) {
let inLen = Object.keys(flowchartData.operators[key].properties.inputs).length;
let outLen = Object.keys(flowchartData.operators[key].properties.outputs).length;
const _inLen = +endpoint.inputCount - inLen;
const _outLen = +endpoint.outputCount - outLen;
let loopFlag = false;
if (_inLen !== 0) {
loopFlag = true;
if (_inLen < 0) {
inLen = inLen + _inLen;
for (let i = inLen; i > Math.abs(inLen + _inLen); i--) {
const _key = Object.keys(flowchartData.operators[key].properties.inputs)[i];
delete flowchartData.operators[key].properties.inputs[_key];
}
} else {
for (let i = (inLen - 1); i < (inLen + _inLen); i++) {
flowchartData.operators[key].properties.inputs['input_' + i] = {
label: 'IN_' + (i + 1)
};
}
}
}
if (_outLen !== 0) {
loopFlag = true;
if (_outLen < 0) {
outLen = outLen + _outLen;
for (let i = outLen; i > Math.abs(outLen + _outLen); i--) {
const _key = Object.keys(flowchartData.operators[key].properties.outputs)[i];
delete flowchartData.operators[key].properties.outputs[_key];
}
} else {
for (let i = (outLen - 1); i < (outLen + _outLen); i++) {
flowchartData.operators[key].properties.outputs['output_' + i] = {
label: 'OUT_' + (i + 1)
};
}
}
}
if (loopFlag) {
$flowchart.flowchart('setData', flowchartData);
}
}
});
}
private advanceOption() {
const self = this;
const $flowchart = $('#' + this.currentElement);
const $container = $flowchart.parent();
const cx = $flowchart.width() / 2;
const cy = $flowchart.height() / 2;
// Panzoom initialization...
$flowchart.panzoom();
// Centering panzoom
$flowchart.panzoom('pan', -cx + $container.width() / 2, -cy + $container.height() / 2);
// Panzoom zoom handling...
const possibleZooms = [0.5, 0.75, 1, 2, 3];
let currentZoom = 2;
$container.on('mousewheel.focal', function (e) {
e.preventDefault();
const delta = (e.delta || e.originalEvent.wheelDelta) || e.originalEvent.detail;
const zoomOut: any = delta ? delta < 0 : e.originalEvent.deltaY > 0;
currentZoom = Math.max(0, Math.min(possibleZooms.length - 1, (currentZoom + (zoomOut * 2 - 1))));
$flowchart.flowchart('setPositionRatio', possibleZooms[currentZoom]);
$flowchart.panzoom('zoom', possibleZooms[currentZoom], {
animate: false,
focal: e
});
});
// Apply the plugin on a standard, empty div...
$flowchart.flowchart({
data: this.initFlowchartData(),
onOperatorCreate: function (operatorId, operatorData, fullElement) {
// console.log(fullElement);
// console.log('New operator created. Operator ID: "' + operatorId + '", operator title: "' + operatorData.properties.title + '".');
return true;
},
onOperatorSelect: function (operatorId, data) {
self.componentSelect.emit({
id: operatorId,
title: $flowchart.flowchart('getOperatorTitle', operatorId),
data: $flowchart.flowchart('getOperatorData', operatorId)
});
return true;
},
// Operators Unselected
onOperatorUnselect: function () {
self.componentDeSelected.emit({
action: 'operator-deselected'
});
return true;
},
onOperatorDelete: function (operatorId) {
const operatorType = $flowchart.flowchart('getOperatorData', operatorId).type;
if (operatorType.eventName) {
/** Once remove event from editor need to enable it again in event panel */
self.ngFlowchartService.send({
action: 'ng-event-deleted',
data: {
eventName: operatorType.eventName,
operatorId: operatorId
}
});
}
return true;
},
onLinkDelete: function (linkId, forced) {
// delete self.flowchartData['links'][linkId];
// console.log('Link deleted. Link ID: "' + linkId + '", link color: "' + $flowchart.flowchart('getLinkMainColor', linkId) + '".');
return true;
},
onLinkCreate: function (linkId, linkData) {
if (self.rule && self.rule.isEnabled) {
const fromoprtype = $flowchart.flowchart('getOperatorData', linkData.fromOperator).type;
const tooprtype = $flowchart.flowchart('getOperatorData', linkData.toOperator).type;
const rule = self.rule.rules['rule'];
const firstRule = fromoprtype['type'] || 'Component';
const secondRule = tooprtype['type'] || 'Component';
return rule[firstRule + ':' + secondRule];
}
self.flowchartData['links'][linkId] = linkData;
delete self.flowchartData['links'][linkId]['internal'];
// console.log('New link created. Link ID: "' + linkId + '", link color: "' + linkData.color + '".');
return true;
},
onLinkSelect: function (linkId) {
const flowchartObject = $flowchart.flowchart('getData');
const linkObject = flowchartObject.links[linkId];
const fromOperator = flowchartObject.operators[linkObject.fromOperator];
const toOperator = flowchartObject.operators[linkObject.toOperator];
const fromType = fromOperator.type.hasOwnProperty('eventName') ? 'Event' : 'Component';
const toType = toOperator.type.hasOwnProperty('eventName') ? 'Event' : 'Component';
const title = `Link: ${fromOperator.properties.title} - ${toOperator.properties.title}`;
self.componentSelect.emit({
id: linkId,
title: title,
data: {
type: {
isLink: true,
data: linkObject,
linkData: {
fromOperator: fromOperator.properties.title,
fromType: fromType,
toOperator: toOperator.properties.title,
toType: toType
}
}
}
});
return true;
}
});
const $draggableOperators = $('.draggable_operator');
$draggableOperators.draggable({
cursor: 'move',
opacity: 0.7,
// helper: 'clone',
appendTo: 'body',
zIndex: 1000,
});
}
private addNewComponent(event: any) {
// console.log(event);
const $flowchart = $('#' + this.currentElement);
let componentTitle;
componentTitle = (event.dragData.eventName) ? `${event.dragData.displayName}` : `${event.dragData.name}`;
this.operatorId = this.helperService.getUniqueId();
if (event.dragData.eventName) {
/** Component is an 'event' type */
const flowchartObject = $flowchart.flowchart('getData');
const operators = flowchartObject.operators;
for (let i = 0; i < Object.keys(operators).length; i++) {
const component = operators[Object.keys(operators)[i]];
if (event.dragData.eventName === component.type.eventName) {
return;
}
}
/** 1. Send operator details back to event panel for disabling the dropped event
* 2. Once remove event from editor need to enable it again in event panel
* 2. After rerender pipeline we need to set it again (pending)
*/
this.ngFlowchartService.send({
action: 'ng-event-added',
data: {
eventName: event.dragData.eventName,
operatorId: this.operatorId
}
});
}
const inputObject = {};
const outputObject = {};
if (event.dragData.hasOwnProperty('componentUIMetaData') && event.dragData.componentUIMetaData.hasOwnProperty('connector')) {
const iCount = event.dragData.componentUIMetaData.connector.endpoint.in || 0;
const oCount = event.dragData.componentUIMetaData.connector.endpoint.out || 0;
for (let i = 0; i < iCount; i++) {
inputObject['input_' + i] = {
label: 'IN_' + (i + 1)
};
}
for (let i = 0; i < oCount; i++) {
outputObject['output_' + i] = {
label: 'OUT_' + (i + 1)
};
}
}
const operatorData = {
top: event.nativeEvent.offsetY - 20,
left: event.nativeEvent.offsetX - 20,
type: event.dragData,
properties: {
title: componentTitle,
inputs: inputObject,
outputs: outputObject,
}
};
// this.operatorId++;
$flowchart.flowchart('createOperator', this.operatorId, operatorData);
if (event.dragData.hasOwnProperty('type') && (event.dragData.type === 'Event')) {
const cssClass = 'flowchart-operator-title ui-draggable-handle titleClass';
event.nativeEvent.currentTarget.childNodes[1].lastChild.children[0].classList.value = cssClass;
}
}
onComponentDrop(event: any) {
// console.log(event);
this.addNewComponent(event);
}
deleteSelected() {
const $flowchart = $('#' + this.currentElement);
$flowchart.flowchart('deleteSelected');
}
getFlowchartData() {
const $flowchart = $('#' + this.currentElement);
return $flowchart.flowchart('getData');
}
ngOnDestroy() {
if (this.subscribeInterval) {
this.subscribeInterval.unsubscribe();
}
}
}
<div class="flowchart-example">
<div id="chart_container">
<div class="zoom-panel-container">
</div>
<div class="flowchart-example-container" id="dp_flowchart" droppable [dropScope]="'component'" (onDrop)="onComponentDrop($event)"></div>
</div>
</div>
https://github.com/sdrdis/jquery.flowchart enter link description here
I am trying to add custom styles on operator header title to change the color it using jquery/JS but the issue is styles is coming to every operator while dragging.
I have to add custom styles on this class: "flowchart-operator-title ui-draggable-handle"
My requirement:
Add Custom styles if the JSON key matches only as well as add images only if condition matches. Can you please help?
Here inside code i am using function addNewComponent which i am used for drag and drop

How to remove a class from grandfather div

I have a code like this. It is here only to show how It works. Problem appears when I want to compare cards. When I click on the first and second card and they are not matched their grandfather div should remove a class which flips a tile. Second thing, I click on the same card twice it will return "win". How to fix this and make this code look clean?
{
let guesses = [];
let tries = 0;
const doubleArrVal = arr => arr.concat(arr);
const addFlipEffect = (e) => {
let target = e.currentTarget;
if (!target.classList.contains("tile--static")) {
target.classList.add("tile--active");
}
return target;
};
const addManyListeners = (collection, e, fn) => {
for (let i = 0; i < collection.length; i++) {
collection[i].addEventListener(e, fn, false);
}
};
const randomize = (arr) => {
for (let i = 0; i < arr.length; i++) {
const j = Math.floor(Math.random() * (i + 1));
const tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
return arr;
};
const prepareArray = (ammount) => {
let imgNames = ["angular", "bootstrap", "css", "foundation", "github", "grunt", "html", "ruby", "jquery", "less", "nodejs", "sass"];
imgNames = imgNames.slice(0, ammount);
const doubled = doubleArrVal(imgNames);
return randomize(doubled);
};
const createMarkUp = (id) => {
const markUp = `<div class="tile tile--game">
<div class="tile__side tile__side--front">
</div>
<div class="tile__side tile__side--back">
<img src="img/${id}.svg" alt="${id}" class="tile__img" data-name="${id}">
</div>
</div>`;
return markUp;
};
const createCards = (ammount) => {
const container = document.getElementById("gameContainer");
const preparedCards = prepareArray(ammount);
preparedCards.map(card => {
const cardElement = createMarkUp(card);
container.innerHTML += cardElement;
});
return container;
};
// Problem is here
const compare = (e) => {
const userPick = e.currentTarget;
let image = userPick.querySelector("[data-name]");
guesses.push(image);
tries++;
if (tries === 2) {
if (guesses[0].dataset.name === guesses[1].dataset.name) {
console.log("win");
} else {
setTimeout(() => {
guesses[0].parentNode.parentNode.classList.remove("tile--active");
guesses[1].parentNode.parentNode.classList.remove("tile--active");
}, 500);
}
guesses = [];
tries = 0;
}
}
const startGame = (level) => {
const gameCards = createCards(4);
addManyListeners(gameCards.children, "click", addFlipEffect);
addManyListeners(gameCards.children, "click", compare);
};
startGame();
}
<div id ="gameContainer"></div>
I would use a Set for guesses, to facilitate the unique selection:
let guesses = new Set;
//...
const compare = (e) => {
const userPick = e.currentTarget;
let image = userPick.querySelector("[data-name]");
guesses.add(image);
if (guesses.size === 2) { // guaranteed to be 2 different images
if (new Set(Array.from(guesses, guess => guess.dataset.name)).size == 1) {
console.log("win");
guesses = new Set;
} else {
setTimeout(() => {
for (let guess of guesses) {
guess.parentNode.parentNode.classList.remove("tile--active");
}
guesses = new Set; // only clear here
}, 500);
}
}
}
If your template would put the data-name="${id}" on the grandfather/root div, it would all become a bit simpler: then you only have to work with the div, not the img:
const createMarkUp = (id) => {
const markUp = `<div class="tile tile--game" data-name="${id}">
<div class="tile__side tile__side--front">
</div>
<div class="tile__side tile__side--back">
<img src="img/${id}.svg" alt="${id}" class="tile__img">
</div>
</div>`;
return markUp;
};
//...
const compare = (e) => {
guesses.add(e.currentTarget);
if (guesses.size !== 2) return;
if (new Set(Array.from(guesses, guess => guess.dataset.name)).size == 1) {
console.log("win");
guesses = new Set;
return;
}
setTimeout(() => {
for (let guess of guesses) {
guess.classList.remove("tile--active");
}
guesses = new Set;
}, 500);
}
This is an error of scope. Your timeout uses the variable guesses but it is executed in the global scope, where the variable is undefined. So I have used bind, to bind it to the function.
To make sure you have 2 different elements in guesses, simply test them before testing their value.
{
let guesses = [];
let tries = 0;
const doubleArrVal = arr => arr.concat(arr);
const addFlipEffect = (e) => {
let target = e.currentTarget;
if (!target.classList.contains("tile--static")) {
target.classList.add("tile--active");
}
return target;
};
const addManyListeners = (collection, e, fn) => {
for (let i = 0; i < collection.length; i++) {
collection[i].addEventListener(e, fn, false);
}
};
const randomize = (arr) => {
for (let i = 0; i < arr.length; i++) {
const j = Math.floor(Math.random() * (i + 1));
const tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
return arr;
};
const prepareArray = (ammount) => {
let imgNames = ["angular", "bootstrap", "css", "foundation", "github", "grunt", "html", "ruby", "jquery", "less", "nodejs", "sass"];
imgNames = imgNames.slice(0, ammount);
const doubled = doubleArrVal(imgNames);
return randomize(doubled);
};
const createMarkUp = (id) => {
const markUp = `<div class="tile tile--game">
<div class="tile__side tile__side--front">
</div>
<div class="tile__side tile__side--back">
<img src="img/${id}.svg" alt="${id}" class="tile__img" data-name="${id}">
</div>
</div>`;
return markUp;
};
const createCards = (ammount) => {
const container = document.getElementById("gameContainer");
const preparedCards = prepareArray(ammount);
preparedCards.map(card => {
const cardElement = createMarkUp(card);
container.innerHTML += cardElement;
});
return container;
};
const compare = (e) => {
const userPick = e.currentTarget;
let image = userPick.querySelector("[data-name]");
guesses.push(image);
tries++;
if (tries === 2) {
if (guesses[0] !== guesses[1] && guesses[0].dataset.name === guesses[1].dataset.name) {
console.log("win");
} else {
setTimeout(((guesses) => {
guesses[0].parentNode.parentNode.classList.remove("tile--active");
guesses[1].parentNode.parentNode.classList.remove("tile--active");
}).bind(null, guesses), 500);
}
guesses = [];
tries = 0;
}
}
const startGame = (level) => {
const gameCards = createCards(4);
addManyListeners(gameCards.children, "click", addFlipEffect);
addManyListeners(gameCards.children, "click", compare);
};
startGame();
}
<div id="gameContainer"></div>

Categories

Resources