Unable to add classList property to an array's index - javascript

Need bit of help to make memory-game, as I am trying to click element to show and open, compare inner html and if equal add the class match to keep two elements opened BUT when i click (element) card it throws error of "uncaught TypeError: cannot read property classList of undefined at HTMLLIElement .cardClick" which i dont know how to rectify, any help will be appreciated. Thanks.
Complete code at
https://codepen.io/ma-halepoto/pen/OwEqNr
window.onload = function () {
let openedCards = [];
matchedCards = [];
//currentCard = [];
//previouseCard= 0 ;
moveCount = 0 ;
restart = document.getElementsByClassName ('restart');
modal = document.getElementById('myModal');
span = document.getElementsByClassName('close')[0];
displayCards = [];
openedCardsLength=null;
// console.log (restart); just to see if restart works
restart[0].addEventListener ('click', function (){
location.reload();
})
// console.log("It's loaded!") to check if this works
const cards = ['fa-diamond','fa-diamond', 'fa-paper-plane-o','fa-paper-plane-o', 'fa-anchor', 'fa-anchor', 'fa-bolt', 'fa-bolt', 'fa-cube', 'fa-cube', 'fa-leaf', 'fa-leaf', 'fa-bicycle', 'fa-bicycle',
'fa-bomb','fa-bomb' ];
let shuffleCards = shuffle (cards);
// console.log (shuffleCards); to check if this works
let cardElements = document.getElementsByClassName('symbols');
// console.log (cardElements); to check if this works
for (i=0; i < cardElements.length; i++ ) {
cardElements[i].className = shuffleCards[i]+ ' fa symbols';
}
// initialising popup
function popup() {
modal.style.display = "flex";
document.getElementById('p1').innerHTML = 'You did it in '+ moveCount+ ' moves' + ' and ' + seconds+ ' seconds.';
}
// Closing popup by clicking x
span.onclick = function closeX () {
modal.style.display = "none";
}
// function close popup by clicking any where
window.onclick = function(event) {
if (event.target == modal) {
modal.style.display = "none";
}
}
// Stopwatch initialisation
let stopWatch = document.getElementById ('timer');
time = 0;
seconds=0
// start time
function startTime () {
time = setInterval ( function (){
seconds++;
stopWatch.innerHTML = seconds + ' s';
}, 1000);
}
// stop the time function
function stopTime () {
clearInterval (time);
}
displayCards = document.getElementsByClassName ('card');
console.log(displayCards);
// Click Event
function cardClick () {
openedCards = this;
openedCards.removeEventListener ('click', cardClick);
console.log (openedCards);
// openedCards[1].removeEventListener ('click', cardClick);
// updating move counts
let countMoves = document.getElementById ('moves');
moveCount++ ;
countMoves.innerHTML= moveCount;
console.log(countMoves);
// star ranking;
if ( moveCount === 20) {
let removeStar = document.getElementById('star3');
removeStar.style.display = 'none';
} else if (moveCount ===30) {
let removeStarTwo = document.getElementById ('star2');
removeStarTwo.style.display = 'none';
}
// start stopwatch at the first click.
if ( moveCount ===1) {
startTime ();
}
//function clickedCards () {
//openedCards.push(this);
openedCardsLength = openedCards.length;
Error here
openedCards[0].classList.add('open','show');
openedCards[1].classList.add('open', 'show');
console.log (openedCards[0]);
console.log (openedCards[1]);
console.log (openedCardsLength);
if (openedCardsLength ===2) {
if (openedCards[0].type === openedCards[1].type){
openedCards[0].classList.add('match');
openedCards[1].classList.add('match');
} else {
openedCards[0].classList.remove('open','show');
openedCards[1].classList.remove('open','show');
openedCards[0].addEventListener ('click', cardClick);
openedCards[1].addEventListener ('click', cardClick);
}
}
}
// event listener function
for (i=0; i < displayCards.length; i++) {
displayCards[i].addEventListener('click', cardClick);
}
}`

Related

How to move on from one item at a time through an array? (no setInterval)

I am making a quiz. How would I be able to make the user, after they answer the question by clicking a button in the multiple choice answer, move onto the next question in the same container div?
function runQuizQuestions() {
// shuffling questions
let theQuestions = shuffle(myQuestions);
console.log('shuffled Questions');
for (let i = 0; i < myQuestions.length; i++) {
// fill in the questions HTML
document.querySelector('#quizQ').innerText = myQuestions[i].question;
document.querySelector('#a1').innerText = myQuestions[i].answer1;
document.querySelector('#a2').innerText = myQuestions[i].answer2;
document.querySelector('#a3').innerText = myQuestions[i].answer3;
document.querySelector('#a4').innerText = myQuestions[i].answer4;
// store correct answer
rightAnswer = myQuestions[i].correct;
// clicking inside the container
let quizContainer = document.querySelector('#quiz');
quizContainer.addEventListener("click", function (event) {
event.preventDefault;
let answerClick = event.target;
console.log(`you clicked on:`, answerClick);
if (answerClick.matches("button")) {
// store button's value
let answer = answerClick.innerText;
// check answer
if (answer === rightAnswer) {
document.querySelector('#result').innerText = "Correct!";
document.querySelector('#result').classList.remove('d-none', 'alert-danger');
setTimeout(function () { document.querySelector('#result').classList.add('d-none', 'alert-danger'); }, 2000);
console.log('right answer');
} else {
document.querySelector('#result').innerText = "Wrong!";
document.querySelector('#result').classList.remove('d-none', 'alert-success');
setTimeout(function () { document.querySelector('#result').classList.add('d-none', 'alert-success'); }, 2000);
console.log('wrong answer');
}
}
// move on to next question.
})
}

Can someone please assist with a JS function for var length

i am trying to display a modal based on a number of matched cards in a memory game.
i have already set var to be
let matchedCards = document.querySelectorAll('.match');
and then checking if matchedCards.length == 4 display a a modal (function) but I am unable to even enter that function for some reason.
// function to intialise game
function initGame() {
//set initial timer value
timer.innerHTML = '0 mins 0 secs';
//set initial star rating
for (var i = 0; i < stars.length; i++) {
stars[i].style.color = "#FFD700";
stars[i].style.visibility = "visible";
}
//deck selection and populating with generated card
let deck = document.querySelector('.deck');
let cardHTML = shuffle(cards).map(function(card) {
return generatedCard(card);
});
deck.innerHTML = cardHTML.join('');
allCards = document.querySelectorAll('.card');
//card function
allCards.forEach(function(card) {
card.addEventListener('click', function(e) {
openCards.push(card);
card.classList.add('open', 'show', 'disabled');
// setting move counter and matched/unmatched cards
if (openCards.length === 2) {
movesCounter();
if (openCards[0].dataset.card === openCards[1].dataset.card) {
matched();
} else {
unmatched();
}
} else {
allMatched();
}
})
});
};
//function for all matched
function allMatched() {
let matchedCards = document.querySelectorAll('.match');
if (matchedCards.length === 4) {
console.log(fourcards);
congratulations();
}
}
// match the 2 cards that are open if they are of the same type
function matched() {
openCards[0].classList.add("match", "disabled");
openCards[1].classList.add("match", "disabled");
openCards = [];
}
expecting to see the congrats modal popup if there are 4 matched cards on the deck.

My tic tac toe game (jquery) is not working properly. Lost Part is not working properly

The 'Win' and 'Draw' parts are showing up on time, but the 'Lost' part doesn't show the message 'you lost'until I click on an empty cell once again. Please check out my code and help me find any errors.
Below is my code:
Marked is a class that changes the opacity of the clicked cell.
1,2,3...are the id's of respective cells in the table(html).
I tried delay() too instead of setTimeout(), but it didn't work as well.
$(document).ready(function() {
var timer;
var x = 0;
$("td").click(function() {
if($(this).text()=='') {
$(this).text("0").addClass("marked");
x = 1;
}
}).click(function() {
if(x==1) {
timer = setTimeout(function() {
var choose = $("td").not(".marked");
var random = choose[Math.floor(Math.random()*choose.length)];
$(random).text("X").addClass("marked");
},1000);
x=0;
showResult();
}
});
function showResult() {
var one = $("#1").text();
var two = $("#2").text();
var three = $("#3").text();
var four = $("#4").text();
var five = $("#5").text();
var six = $("#6").text();
var seven = $("#7").text();
var eight = $("#8").text();
var nine = $("#9").text();
if(one==two && two==three)
result(one)
else if (four==five && five==six)
result(four)
else if(seven==eight && eight==nine)
result(seven)
else if (one==four && four==seven)
result(one)
else if (two==five && five==eight)
result(two)
else if (three==six && six==nine)
result(three)
else if (one==five && five==nine)
result(one)
else if(three==five && five==seven)
result(three);
else {
var z = $("td").not(".marked");
if(z.length == 0) {
$("p").text("Draw!");
$("td").removeClass("marked");
$("td").text("");
$("#demo1").append('<img src="https://media.tenor.com/images/54c63f726505bfdb455eb4c29e626ad8/tenor.gif">');
clearTimeout(timer);
}
}
}
function result(y) {
var result = y;
if(result=="X"){
clearTimeout(timer);
$("p").text("You Lost!");
$("td").removeClass("marked");
$("td").text("");
$("#demo1").append('<img src="https://media.tenor.com/images/08902a85a6107684f8614846f4a54218/tenor.gif">');
}
if(result=="0") {
$("td").text("");
$("p").text("You Won!");
$("#demo1").append('<img src="https://i.gifer.com/4OuC.gif">');
$("td").removeClass("marked");
clearTimeout(timer);
}
}
});
You are calling showResult immeadiately when the user clicked, so it cant't recognize the X put into the table one second later.
Just do:
$("td").click(function() {
[...]
}).click(function() {
if (x == 1) {
timer = setTimeout(function() {
var choose = $("td").not(".marked");
var random = choose[Math.floor(Math.random() * choose.length)];
$(random).text("X").addClass("marked");
/******** ADD ANOTHER CHECK HERE ********/
showResult();
}, 1000);
x = 0;
showResult();
}
});
It might also be a good idea to add a return to showResult that returns false when a result was achieved. This way you could do something like
x = 0;
if (showResult()) {
timer = setTimeout(function() {
[...]
}
}
And the user can't get a loose message right after a win message.
Also: Why do you need the 2 click listeners? You can just use the if statement in the top one and then you don't need the (x == 1)

How to break from Jquery click callback function

I am making my own version of simon Game, but the callback function inside click is not existing after user press wrong input. As a result function handler.patternRepeatPlayer() is getting called recursively for each element of array pattern.I want a solution to break from the else after callinghandler.patternRepeatPlayer just once. The program is working fine in strict mode, but only in non-strict mode inside else , I am not able to break from else.
-- can access the project on Git.
https://github.com/santosh1357/simonGame.git
The flow is like from html -> Function simonnGame.PatternGen from PatternGen -> handler.PatternRepeatPlayer -> PatternRepPlayer -> PatternMatcher -> userInput(here if wrong user input in non-strict mode) -> patternRepeatPlayer
This case is failing as in this case else is not existing after calling the function only once.
//Problematic Function.
userInput: function(){
var userPattern = new Array();var id;
$('img').click(function(){
id = parseInt(this.id,10); userPattern.push(id);handler.effect(id);
if(userPattern.indexOf(id) !== simonGame.PATTERN.indexOf(id)){
if($('.chkStrict:checked').val() === "on"){
var audio = new Audio('sounds/wrong.mp3');
audio.play();
setTimeout(function(){window.location.reload(true)},1000);
} else {
var audio = new Audio('sounds/wrong.mp3');
audio.play();
userPattern.length = 0;
handler.repeatFlag = true;
handler.patternRepeatPlayer(); ****//this is getting called recursivelly rather than quiting after calling once****
return ;
}
}
//End Problematic Functiom
I think there is some misunderstanding on how click callback functions work.
//Fullcode
var simonGame = {
COUNT: 0,
PATTERN: [],
SOUND:[{file:'sounds/sa.mp3'},{file:'sounds/re.mp3'},{file:'sounds/ga.mp3'},{file:'sounds/ma.mp3'},{file:'sounds/pa.mp3'},{file:'sounds/dha.mp3'},{file:'sounds/nee.mp3'}],
patternGen: function(){
var randomId;
randomId = Math.floor(Math.random() * 7);
simonGame.PATTERN.push(randomId);
if(simonGame.COUNT > 20){
alert("You have won the game!!");
window.location.reload(true);
}
simonGame.COUNT += 1;
//debugger;
//console.log("increase count true calling count display " + simonGame.COUNT);
handler.countDisplay();
//console.log("count gen true calling patternPlayer with PATTERN " + simonGame.PATTERN );
handler.patternRepeatPlayer();
}, //close patternGen
patternMatcher: function(genPattern){
//console.log("inside patternMatch");
var genPattern = simonGame.patternGen;
//setTimeout(function(){
//console.log("PATEERN: " + simonGame.PATTERN + "COUNT " + simonGame.COUNT );
//calling user input
console.log("calling user Input");
handler.userInput();
setTimeout(function(){
if(handler.repeatFlag === false){ //execute count gen only if repeat flag is false inside user INPUT
genPattern();
}
},simonGame.COUNT*2000);
//console.log("pattern check true, calling pattern gen");
//},simonGame.COUNT*5000); //c`enter code here`lose setTimeout
}, //close patternMatcher
} //close simonGame
var handler = {
countRepPlayer: 0,
repeatFlag: false,
patternRepeatPlayer: function(){
var repeater = setInterval(function(){
handler.effect(simonGame.PATTERN[handler.countRepPlayer]);
handler.countRepPlayer += 1;
if(handler.countRepPlayer > simonGame.COUNT){
clearInterval(repeater);
//setTimeout(function(){
simonGame.patternMatcher();
//},1000);
handler.countRepPlayer = 0;
}
},1000);//close sestInterval
}, //close patternRepeatPlayer
effect: function(id){
var img = document.getElementById(id);
if(img !== null && id !== undefined){
$( img ).fadeIn(100).fadeOut(100).fadeIn(100);//fadeOut(200).fadeIn(200);
//debugger;
var audio = new Audio(simonGame.SOUND[id].file);
audio.play();
//console.log("id inside effect " + id)
}
},//close effect
countDisplay: function(){
document.getElementById("count").innerHTML = "Count: " + simonGame.COUNT;
}, //close countIncrease
userInput: function(){
var userPattern = new Array();var id;
$('img').click(function(){
id = parseInt(this.id,10);
userPattern.push(id);
handler.effect(id);
console.log(" user " + userPattern);
console.log(" pattern " + simonGame.PATTERN);
if(userPattern.indexOf(id) !== simonGame.PATTERN.indexOf(id)){
console.log(" WRONG USER INPUT ");
if($('.chkStrict:checked').val() === "on"){
var audio = new Audio('sounds/wrong.mp3');
audio.play();
setTimeout(function(){window.location.reload(true)},1000);
} else {
console.log("inside else " );
var audio = new Audio('sounds/wrong.mp3');
audio.play();
userPattern.length = 0;
handler.repeatFlag = true;
handler.patternRepeatPlayer(); ****//this is getting called recursivelly rather than quiting after calling once**.**
return ;
}
}
//reset the userPattern Array
if(userPattern.length === simonGame.PATTERN.length){
userPattern.length = 0;
}
}); //close click.
}
} //close handler
Yes, It will be called recursively, because you set interval for it.
Here you can modify your code:
patternRepeatPlayer: function(){
var repeater = setInterval(function(){
handler.effect(simonGame.PATTERN[handler.countRepPlayer]);
handler.countRepPlayer += 1;
if(handler.countRepPlayer > simonGame.COUNT){
clearInterval(repeater);
//setTimeout(function(){
simonGame.patternMatcher();
//},1000);
handler.countRepPlayer = 0;
}
},1000);//close sestInterval
}
To: (EDITED)
function myCallbackFunction(repeater){
handler.effect(simonGame.PATTERN[handler.countRepPlayer]);
handler.countRepPlayer += 1;
if(handler.countRepPlayer > simonGame.COUNT){
clearInterval(repeater);
simonGame.patternMatcher();
handler.countRepPlayer = 0;
}
}
patternRepeatPlayer: function(){
var repeater = setInterval(function() {myCallbackFunction(repeater);}, 1000);
}
And where you need to call it once, just call myCallbackFunction(repeater)

Closing a menu on anywhere click

I am working on a website and here is the link file:///D:/fahim/HTML/menu/index.html. If you click the menu its closing on its own "X" button, but i want it to close by clicking outside the menu. This is the javascript used on the home page.
<script>
var popupView = new popup();
document.querySelector('#btn_1').addEventListener('click', function () {
popupView.show(document.querySelector('#popup_1'));
});
document.querySelector('#btn_2').addEventListener('click', function () {
popupView.show(document.querySelector('#popup_2'), function () {
console.log('show do something');
});
});
document.querySelector('#btn_3').addEventListener('click', function () {
popupView.show(document.querySelector('#popup_3'), '', function () {
console.log('CLOSE');
});
});
</script>
And this is the code which is attached as popup_view.js file on server
(function () {
var popup = function() {
function hide(dom, dosomething) {
if (!dom) {
console.error('hide function not set dom object');
return;
}
if (dosomething) {
dosomething();
}
dom.className += ' ' + 'popup_hide';
}
function show(dom, dosomethingShow, dosomethingClose) {
if (!dom) {
console.error('show function not set dom object');
return;
}
if (dosomethingShow) {
dosomethingShow();
}
var className = 'popup_hide',
reg = new RegExp('(^|\\b)' +
className.split(' ').join('|') +
'(\\b|$)', 'gi');
dom.className = dom.className.replace(reg, '').trim();
var nodes = dom.childNodes;
for (var i = nodes.length - 1; i >= 0; i--) {
if (nodes[i].className === 'pop_up_close') {
var close = function (e) {
if (dosomethingClose) {
dosomethingClose();
}
dom.className += ' ' + 'popup_hide';
nodes[i].removeEventListener('click', close);
};
nodes[i].addEventListener('click', close);
break;
}
}
}
this.show = show;
this.hide = hide;
};
window.popup = popup;
})();
Please help as i have tried lot of codes except these but non of them works
$(document).on('click', function(e){
//your close function
e.stopPropagation();
}
Put that code anywhere in your $(document).ready(... block

Categories

Resources