Card Game: My function won't end after X amount of clicks - javascript

I've been researching how to fix my issue for the past week but can't. I am creating a "click a card and it adds to score" card game but I am having trouble getting the clicks to stop after x amount of cards have already been clicked. I have created a clickCounter function that tracks the clicks and executes with every click. I have also added an isGameOver boolean to stop the function once it hits a certain amount of clicks. However, the boolean doesn't execute as I'd hoped in the clickCounter function.
Long story short: I just want the game to end once x amount of cards have been clicked.
Here is the JavaScript:
let isGameOver = false;
let counter = 0;
const clickCounter = function () {
switch (counter) {
case 0:
case 1:
counter += 1
break;
case 2:
isGameOver; //this is where I want the game to end//
break;
}
//this keeps track of the counts in console//
console.log(`counter equals: ${counter}`);
}
//this is my function that runs everything//
const cardSelection = function () {
randomNumber() //just a function to generate random #//
//beginning of if statement//
if(!isGameOver) {
//CARD ONE//
document.querySelector('#cardOne').addEventListener('click', function (e) {
cardOneFront(); //this changes card appearance, ignore please//
clickCounter(); //clickCounter is being called here//
if (randomNums[0]) {
scoreDisplay.innerText = `SCORE: ${scoreCount += randomNums[0]}`;
console.log(`score just added: ${randomNums[0]}`);
}
this.style.pointerEvents = 'none'
});
//CARD TWO//
document.querySelector('#cardTwo').addEventListener('click', function () {
cardTwoFront(); //this changes card appearance, ignore please//
cardCounter(); //clickCounter is being called here//
if (randomNums[1]) {
scoreDisplay.innerText = `SCORE: ${scoreCount += randomNums[1]}`;
console.log(`score just added: ${randomNums[1]}`);
}
this.style.pointerEvents = 'none'
});
} //if statement ends here//
} //cardSelection function ends here//
cardSelection()
I have visualization of it too. See Here:
what appears in console
I have 10+ cards but I only included two here to keep the code from being super long but the cards all have the similar code and their own individual click events. So, according to the image above, I want card three to not be clickable after counter = 2. But, as you see in the image, even though counter has equaled 2, card 3 is clickable anyways.
I hope this explanation was thorough and I appreciate any help as I am newer to coding. Also, I am coding all of this using vanilla JavaScript.
Thank you!

i think you need to add 'true' to case 2
break;
case 2:
isGameOver = true; //this is where I want the game to end//
break;
}
I guess you already have tried, but i dont see what else would be wrong with the code,
you can also try to add an 'else' to the code. i can only see and If(!isGameOver)

Do the check inside click handlers.
Add card class on each card (this will help to find all cards in one go, so that we can disable each at one place/function).
break;
case 2:
isGameOver = true; //this is where I want the game to end//
disableCards();
break;
}
function disableCards() {
// before doing this add `card` class on each card
let cardsEl = document.querySelector('.card'); // document.getElementsByClassName('card')
for(let i = 0; i< cardsEl.length; i++) {
cardsEl[i].classList.add('disableCard');
}
}
Add CSS into stylesheet
.disableCard {
pointer-events: none;
opacity: 0.7;
}
//CARD ONE//
document.querySelector('#cardOne').addEventListener('click', function (e) {
if(!isGameOver) {
cardOneFront(); //this changes card appearance, ignore please//
clickCounter(); //clickCounter is being called here//
if (randomNums[0]) {
scoreDisplay.innerText = `SCORE: ${scoreCount += randomNums[0]}`;
console.log(`score just added: ${randomNums[0]}`);
}
this.style.pointerEvents = 'none'
}
});
And if click handlers have quite similar code you can register them through for loop. like(it is just suggestion otherwise if the logic is not same for each card you can attach for eachone separatoly)
//add handlers//
for (let I = 0; I< cards.length; i++) {
document.querySelector('#card-'+i).addEventListener('click', function (e) {
if(!isGameOver) {
cardFront(i); //this changes card appearance, ignore please//
clickCounter(); //clickCounter is being called here//
if (randomNums[i]) {
scoreDisplay.innerText = `SCORE: ${scoreCount += randomNums[i]}`;
console.log(`score just added: ${randomNums[i]}`);
}
this.style.pointerEvents = 'none'
}
});
}

You are not setting isGameOver = true
try isgameOver = true in
isGameOver; //this is where I want the game to end// line

Related

How to detect each "this" on click javascript

I want to basically detect click event on each card and save this card in a variable but secondGuess is shadowed by firstGuess. What's the solution?
for (let i = 0; i < cards.length; i++) {
//ADDING FLIP EFFECT TO EACH CARD
toggleFlipEffect(cards[i]);
cards[i].addEventListener("click", compareCards, false);
}
function compareCards() {
let points = 0;
let tries = 0;
const firstGuess = this.getElementsByTagName("img")[0].alt;
const secondGuess = this.getElementsByTagName("img")[0].alt;
console.log(firstGuess);
console.log(secondGuess);
if (firstGuess === secondGuess) {
points += 1;
console.log(points);
} else {
firstGuess.classList.remove("card--flip");
secondGuess.classList.remove("card--flip");
}
}
}
Without the HTML and code for toggleFlipEffect it is not possible to know how you have implemented the card flipping. In essence you need to act differently to a click on the first card than on the second card. On the first, you really only need to note which card it is. Only when it is the second time, you need to perform the comparison.
Note that when a guess is wrong, you would need to have a delay before concealing those cards again, or else the user would not have the time to see the second card.
Here is how you could do it:
function toggleFlipEffect() {} // mock-up.
var cards = document.querySelectorAll('div');
for (let i = 0; i < cards.length; i++) {
//ADDING FLIP EFFECT TO EACH CARD
toggleFlipEffect(cards[i]);
// *** add data attribute for quicker access
cards[i].dataset.value = cards[i].getElementsByTagName("img")[0].alt;
cards[i].addEventListener("click", compareCards, false);
}
// Your variables should not be local to the click event handler:
let points = 0;
let tries = 0;
let revealed = [];
function compareCards() {
// do not allow more than 2 cards to be revealed in one turn
if (revealed.length >= 2) return;
// reveal clicked card
this.classList.add("card--flip");
// add it to array with the previous one (if there was one)
revealed.push(this);
// Need another card?
if (revealed.length < 2) return; // allow for second choice
// We have two revealed cards. Check if they are the same
if (revealed[0].dataset.value === revealed[1].dataset.value) {
points += 1; // Yes, keep score
revealed = []; // initialise for next turn
score.textContent = points;
} else {
// Different: conceal these two cards again, but after a delay
setTimeout(_ => {
revealed.forEach(elem => elem.classList.remove("card--flip"))
revealed = []; // initialise for next turn
}, 1000);
}
}
.card--flip {
background-color: white;
}
div {
background-color: black;
margin: 5px;
width: 50px;
height: 50px;
display: inline-block;
}
<div><img src="https://image.flaticon.com/icons/svg/418/418278.svg" alt=1></div>
<div><img src="https://image.flaticon.com/icons/svg/418/418268.svg" alt=2></div>
<div><img src="https://image.flaticon.com/icons/svg/418/418275.svg" alt=3></div><br>
<div><img src="https://image.flaticon.com/icons/svg/418/418268.svg" alt=2></div>
<div><img src="https://image.flaticon.com/icons/svg/418/418278.svg" alt=1></div>
<div><img src="https://image.flaticon.com/icons/svg/418/418275.svg" alt=3></div><br>
score: <span id="score"></span>
The question isn't very clear, but I think you need to distinguish between the first and second click on cards. For that, I suggest you to use a global bool variable, initially set to false.
var first_click = false;
Then, modify your function like this:
function compareCards() {
first_click = !first_click;
if (first_click) {
//code you need after first click
} else {
//code you need after second click
}
//code you need to do anyway independently if first or second click.
}

onclick event in a for loop certain amount of times

I thought that this onClick event in a For loop would help me but when I tried it, it still didn't work.
I am making a simple Battleship game, and while I'm trying to have the user click on only 4 squares to place on ship, the loop keeps going and doesn't stop after 4 tries. I have my onclick even handler in a for loop, but after 4 tries it doesn't stop. I've tried adding a count variable after the end, and even tried adding a break statement but can't get it to work.
Here's my code:
function placeShips() {
var playerTable = document.getElementById("mainPlayer");
var playerCells = playerTable.getElementsByTagName("td");
var count = 1;
alert("Please place the first ship. Click on 4 squares.");
while (count <= 4) {
for (i = 0; i < playerCells.length; i++) {
playerCells[i].onclick = placeBattleship;
}
count++;
}
}
The placeBattleship function contains the code to change the grid square to a background color of red to mark it. My problem is that once the user clicks 4 squares, you can keep going and click on more and more. I can't get the above for loop that calls the placeBattleship function to stop after the user clicks on 4 squares. I've tried putting it in a while loop, and even the solution in the above link, as well as moving the assignment of count, but can't get it to stop after x amount of times (in this case, 4).
Any ideas on what I'm doing wrong, or a better way to do it?
Wouldn't you consider to use jQuery?
Look your function much shorter:
function placeShips() {
$("td:lt(4)").click(placeBattleship);
}
You can testify on the code below:
<table>
<tr>
<td>1.1</td><td>2.1</td>
</tr>
<tr>
<td>1.2</td><td>2.2</td>
</tr>
<tr>
<td>1.3</td><td>2.3</td>
</tr>
</table>
<div id="console"></div>
<script>
$("td:lt(4)").each(function(){
$("#console").append("Content of "+ $(this).html() + "<br/>");
});
$("td:lt(4)").click(function(){
$("#console").append("Clicking "+ $(this).html() + "<br/>");
});
</script>
...or on my Plunker: https://plnkr.co/edit/yNZw6ZhkNfA9E0NdQg7V
So, now we have a solution that stop for 4th click on the squares:
function placeBattleship() {
var $shipDisplay = $("#shipDisplay");
var counter = $shipDisplay.data("counter");
if(counter++ < 4) {
$(this).css("background-color", "red");
$shipDisplay.data("counter", counter);
}
}
function placeShips() {
$("td").click(placeBattleship);
}
$(document).ready(function(){
placeShips();
});
I use a div with id shipDisplay to store a data-attribute for count the clicks.
Look at the plunker: http://plnkr.co/edit/PEba15PSLv2LK6qjY7AD?p=preview
You should separate priorities in your logic and removeEventListener when counter hits 4 , hopefully this helps you :
//defined outside the function
var counter = 0;
playerCells.addEventListener("click" , placeShips );
Then
function placeShips() {
if(counter <= 4){
//Move ship
placeBattleship();
//add to counter
counter++
}else{
//Remove click event if counter reaches 4 .
playerCells.removeEventListener("click" , doSomethingElse)
}
}
You question needs a bit clarification. To my current understanding, you need to move the checking of count to placeBattleship.
What you are doing is binding click to same tds 4 times, not limiting the number of event triggering to 4 times.
// pseudo code
var count = 4; // this is global
var currentCount = 0;
initFunc() {
// bind click events ONCE
}
startPlacing() {
// accept user click and place ship
// set currentCount to zero
}
placeShip() {
// the callback of user `click`
// check for currentCount == count then move on (no more placement)
// increase currentCount by 1
// place ship
}
Note that after an event is triggered, the listener will not be removed. Until you removeEventListener() from it, it will always be listening.

Event listener fails to attach or remove in some contexts

I've created a script that attaches an event listener to a collection of pictures by default. When the elements are clicked, the listener swaps out for another event that changes the image source and pushes the id of the element to an array, and that reverses if you click on the swapped image (the source changes back and the last element in the array is removed). There is a button to "clear" all of the images by setting the default source and resetting the event listener, but it doesn't fire reliably and sometimes fires with a delay, causing only the last element in a series to be collected.
TL;DR: An event fires very unreliably for no discernible reason, and I'd love to know why this is happening and how I should fix it. The JSFiddle and published version are available below.
I've uploaded the current version here, and you can trip the error by selecting multiple tables, pressing "Cancel", and selecting those buttons again. Normally the error starts on the second or third pass.
I've also got a fiddle.
The layout will be a bit wacky on desktops and laptops since it was designed for phone screens, but you'll be able to see the issue and inspect the code so that shouldn't be a problem.
Code blocks:
Unset all the selected tables:
function tableClear() {
//alert(document.getElementsByClassName('eatPlace')[tableResEnum].src);
//numResTables = document.getElementsByClassName('eatPlace').src.length;
tableArrayLength = tableArray.length - 1;
for (tableResEnum = 0; tableResEnum <= tableArrayLength; tableResEnum += 1) {
tableSrces = tableArray[tableResEnum].src;
//alert(tableSrcTapped);
if (tableSrces === tableSrcTapped) {
tableArray[tableResEnum].removeEventListener('click', tableUntap);
tableArray[tableResEnum].addEventListener('click', tableTap);
tableArray[tableResEnum].src = window.location + 'resources/tableBase.svg';
} /*else if () {
}*/
}
resTableArray.splice(0, resTableArray.length);
}
Set/Unset a particular table:
tableUntap = function () {
$(this).unbind('click', tableUntap);
$(this).bind('click', tableTap);
this.setAttribute('src', 'resources/tableBase.svg');
resTableArray.shift(this);
};
tableTap = function () {
$(this).unbind('click', tableTap);
$(this).bind('click', tableUntap);
this.setAttribute('src', 'resources/tableTapped.svg');
resTableArray.push($(this).attr('id'));
};
Convert the elements within the 'eatPlace' class to an array:
$('.eatPlace').bind('click', tableTap);
tableList = document.getElementsByClassName('eatPlace');
tableArray = Array.prototype.slice.call(tableList);
Table instantiation:
for (tableEnum = 1; tableEnum <= tableNum; tableEnum += 1) {
tableImg = document.createElement('IMG');
tableImg.setAttribute('src', 'resources/tableBase.svg');
tableImg.setAttribute('id', 'table' + tableEnum);
tableImg.setAttribute('class', 'eatPlace');
tableImg.setAttribute('width', '15%');
tableImg.setAttribute('height', '15%');
$('#tableBox').append(tableImg, tableEnum);
if (tableEnum % 4 === 0) {
$('#tableBox').append("\n");
}
if (tableEnum === tableNum) {
$('#tableBox').append("<div id='subbles' class='ajaxButton'>Next</div>");
$('#tableBox').append("<div id='cazzles' class='ajaxButton'>Cancel</div>");
}
}
First mistake is in tapping and untapping tables.
When you push a Table to your array, your pushing its ID.
resTableArray.push($(this).attr('id'));
It will add id's of elements, depending on the order of user clicking the tables.
While untapping its always removing the first table.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift
resTableArray.shift(this);
So, when user clicks tables 1, 2, 3. And unclicks 3, the shift will remove table 1.
Lets fix this by removing untapped table
tableUntap = function () {
$(this).unbind('click', tableUntap);
$(this).bind('click', tableTap);
this.setAttribute('src', 'http://imgur.com/a7J8OJ5.png');
var elementID = $(this).attr('id');
var elementIndex = resTableArray.indexOf(elementID);
resTableArray.splice(elementIndex, 1);
};
So you were missing some tables after untapping.
Well lets fix tableClear,
You have a array with tapped tables, but you are searching in main array.
function tableClear() {
tableLen = resTableArray.length;
for (var i = 0; i < tableLen; i++) {
var idString = "#" + resTableArray[i];
var $element = $(idString);
$element.unbind('click', tableUntap);
$element.bind('click', tableTap);
$element.attr("src", 'http://imgur.com/a7J8OJ5.png');
}
resTableArray = [];
}
Im searching only tapped tables, and then just untap them and remove handlers.
fiddle: http://jsfiddle.net/r9ewnxzs/
Your mistake was to wrongly remove at untapping elements.

Event on second and third click

I'm currently in the process of making a website for my band, and one idea I had was that on a specific page, you would see a picture of each band member, and when you hover over it with your mouse the picture would change and you would hear that band member saying something, then when you click on them, they say something else and you'll be redirected to their page.
This alone isn't a problem, but for one member, I want it to take three clicks before you actually get redirected to his page; I also want him to say something different at each click.
So what I'm basically looking for is a way to create different events on the first, second and third click (preferably using javascript).
I hope you guys can help me out, thanks in advance!
Just use variable to count clicks:
var count = 0
$(".test").click(function() {
count++;
if(count == 1) {
$(".test").text("first");
}else if(count == 2){
$(".test").text("second");
}else if(count == 3){
$(".test").text("third");
count = 0;
}
})
http://jsfiddle.net/x83bf1gq/4/
An option could be to create an onclick handler that keeps saying things until all texts in an array of texts are said. Once all the texts have been said, you can just redirect or whatever action you need to be done. Example:
var johnTexts = [
'hello',
'how are you doing',
'come on'
];
var jamesTexts = [
'ready',
'go'
];
var sayTexts = function (texts) {
var i = 0;
return function () {
if (i < texts.length) {
alert(texts[i++]);
} else {
alert('do your redirect or whatever you need');
}
};
}
document.getElementById('john').onclick = sayTexts(johnTexts);
document.getElementById('james').onclick = sayTexts(jamesTexts);
See demo
Simply set an event listener that makes different actions based on a global counter of clicks.
Check this fragment of code:
//set counter
var counter = 0;
var component = document.getElementByID("ID-of-component");
component.addEventListener('mouseover', function(){
//do something
});
component.addEventListener('click', function() {
switch(++counter) {
case 1: /* do something */ break;
case 2: /* do something */ break;
case 3: /* do something */ break;
}
counter = 0; //reset counter
});
Obviously, you have to write this code for each component of your band.

Heatmap for Link clicks with only one function for all

I got a problem while writung a code for a Heatmap based on link clicks.
I have a Page with different articel teasers. When i click the link the artical itself opensup. Now i want to track in real time how often which articel is clicked so i can decide which articel is relevant to users and place it like on the first articel space and so on.
Therefor i wrote a function which tracks the clicks with a counter and show them in the div.
Now my problem is. That i cant write like 20 functions for counters and 20 for resets. I want to have one function which can decide which articel was clicked and add +1 to the counter of the link.
My Code for the counter and the reset + the link text gets red after more than 5 clicks.
var count1 = 0;
function heatmap(id) {
if(id==1){
count1++;
document.getElementById("count1").innerHTML = count1;
}
if(count1>=5){
document.getElementById("link1").style.color = "red";
}
}
function reset(id){
if(id==1){
count1=0;
document.getElementById("count1").innerHTML = 0;
}
}
My html code so far
<div style="text-align:center; font-size:20px; font-family: arial;">
<div id="link1" onclick="heatmap(1)">This is the first Link</div><br>
<div id="count1">0</div><br>
<button onclick="reset(1)">RESET</button>
<button onclick="save(1)">SAVE</button>
</div>
Now my main problem is that i only want one function for all the link tracking.
Is there a possibiblity to write the code in a way the variables are dynamicly so that the function can decide which link was clicked.
for example something like:
var count + ID = 0;
function heatmap(ID) {
count + ID ++;
document.getElementById("count" + ID).innerHTML = count+ID;
}
if(count + ID >=5){
document.getElementById("link + ID").style.color = "red";
}
}
function reset(id){
count + ID =0;
document.getElementById("count + ID").innerHTML = 0;
}
}
I already searched this homepage and did some google searches but all i found was working with arrays or lists. But i think this wouldnt realy work for me since i want to give my function an ID and later add this id to the varibale name like count + ID = count | count + ID = count2 same with the document.getElementById("count" + ID).innerHTML = count+ID; = document.getElementById("count1").innerHTML = count1;
As you can see the link got a onclick="heatmap(ID)" this ID will be added to every articel Link and will go from the first articel with 1 to the last articel with like 20.
If i change an Articel the counter will be resetet and the ID will be changed to its position. So there will always be a identifier with the ID which can be used for the counter.
You could loop through all articles and store a counter on each element which you update or reset once the specific button inside the article was clicked:
var articles = document.querySelectorAll('article');
for(var i=0; i < articles.length; i++) {
articles[i].querySelector('.save').addEventListener('click', updateCounter);
articles[i].querySelector('.reset').addEventListener('click', resetCounter);
articles[i]['counter'] = 0;
}
function updateCounter(ev) {
var article = ev.target.parentNode;
article.counter++;
article.querySelector('.counter').innerHTML = article.counter;
/* Some server synchronisation should go here */
}
function resetCounter(ev) {
var article = ev.target.parentNode;
article.counter = 0;
article.querySelector('.counter').innerHTML = article.counter;
/* Some server synchronisation should go here */
}
DEMO
But to permanently store the counters you have to synchronize your results with a server, respectively a database.
If you just want to use one function you could realize it like this:
var articles = document.querySelectorAll('article');
for(var i=0; i < articles.length; i++) {
articles[i].querySelector('.save').addEventListener('click', checkCounter);
articles[i].querySelector('.reset').addEventListener('click', checkCounter);
articles[i]['counter'] = 0;
}
function checkCounter(ev) {
var article = ev.target.parentNode,
button = ev.target;
if(button.classList[0] === 'save') {
article.counter++;
} else if(button.classList[0] === 'reset') {
article.counter = 0;
}
article.querySelector('.counter').innerHTML = article.counter;
}
DEMO

Categories