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
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.
})
}
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.
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)
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)
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