function startGame(diff){
if (diff === 'easy'){
loadEasy();
}
}
function loadEasy(){
difficultyPage.style.display = 'none';
game.style.display = 'block';
medHardTemplate.style.display = 'none';
easyTemplate.style.display = 'block';
newGame.addEventListener('click', function(){
changeDifficulty('easy');
});
colors = randomColorsArray(3);
correctColor = colorToChoose(colors);
colorRGB.innerHTML = correctColor;
for (var i = 0; i < easySquares.length; i++) {
//add colors to squares
easySquares[i].style.backgroundColor = colors[i];
easySquares[i].addEventListener('click', function(){
var clickedColor = this.style.backgroundColor;
if(clickedColor === correctColor) {
changeColorsOnWinEasy(correctColor);
message.innerHTML = "Correct!"
again.textContent = "Play Again?"
again.addEventListener('click', function(){
again.textContent = "NEW COLORS";
header.style.backgroundColor = "#232323";
colorRGB.style.backgroundColor = "#232323";
message.innerHTML = "";
changeDifficulty('easy');
});
}
else {
this.style.backgroundColor = "#232323";
message.innerHTML = "Wrong!"
}
});
}
}
I don't know when to return the function so that I don't have many functions running at the same time if I spam the newGame button causing my app to lag. I added a return; at the end of the loadEasy function but that didn't seem to do anything.
You may set a flag indicating the current gametype, then you dont need to rebind a button handler everytime, you just need to define the button as start the current game:
let gametype = "easy":
function startGame(diff){
gametype = diff || gametype;
if (gametype === 'easy'){
loadEasy();
}
else {
throw Error('unknown gametype: ' + gametype);
}
}
function loadEasy(){
//... Whatever
}
newGame.addEventListener('onclick', function(){
startGame();
});
So to start a game with the current gametype do:
startGame();
To start a different one, pass a parameter:
startGame("medium");
Related
I'm creating a memory card game and it works until I try to click on the cards too fast. When I open two cards, I am calling compareCards function which adds document.body.style.pointerEvents = "none"; but obviously I can click on the third card if I am fast enough. How can I fix it? Here is my full JS code, note that class .flip adds pointer-events: none; among other things while .match adds short animation. I guess it probably has something to do with setTimeouts but I need them in order to show animatioins.
const playBtn = document.querySelector(".intro button");
const restartBtn = document.querySelectorAll(".restartBtn");
const introScreen = document.querySelector(".intro");
const game = document.querySelector(".game");
const gameContainer = document.querySelector("#gameContainer");
const timer = document.querySelector(".timer span");
const moves = document.querySelector(".moves span");
let time,
minutes = 0,
seconds = 0;
let numberOfMoves = 0;
moves.innerHTML = numberOfMoves;
let openCards = [];
let matchedCards = [];
function startGame() {
let shuffledDeck = shuffle(deckCards);
for (let i = 0; i < shuffledDeck.length; i++) {
const card = document.createElement("div");
card.classList.add("card");
const image = document.createElement("img");
image.setAttribute("src", "img/" + shuffledDeck[i]);
card.appendChild(image);
gameContainer.appendChild(card);
}
runTimer();
}
const deckCards = [
... images to add to game ...];
gameContainer.addEventListener("click", function (e) {
if (e.target.className === "card") {
flipCard();
}
function flipCard() {
e.target.classList.add("flip");
addCard();
}
function addCard() {
if (openCards.length == 0 || openCards.length == 1) {
openCards.push(e.target.firstElementChild);
}
compareCards();
}
});
function compareCards() {
if (openCards.length == 2) {
document.body.style.pointerEvents = "none";
}
if (openCards[0].src == openCards[1].src && openCards.length == 2) {
cardsMatched();
} else if (openCards[0].src !== openCards[1].src && openCards.length == 2) {
cardsNotMatched();
}
}
function countMoves() {
numberOfMoves++;
moves.innerHTML = numberOfMoves;
}
function cardsMatched() {
setTimeout(function () {
openCards[0].parentElement.classList.add("match");
openCards[1].parentElement.classList.add("match");
matchedCards.push(...openCards);
document.body.style.pointerEvents = "auto";
gameWon();
openCards = [];
}, 500);
countMoves();
}
function cardsNotMatched() {
setTimeout(function () {
openCards[0].parentElement.classList.remove("flip");
openCards[1].parentElement.classList.remove("flip");
document.body.style.pointerEvents = "auto";
openCards = [];
}, 500);
countMoves();
}
function gameWon() {
if (matchedCards.length == 16) {
stopTimer();
showModal();
}
}
const modal = document.querySelector(".modal");
function showModal() {
const closeModal = document.querySelector(".closeBtn");
modal.style.display = "block";
closeModal.addEventListener("click", () => {
modal.style.display = "none";
});
window.onclick = function (event) {
if (event.target == modal) {
modal.style.display = "none";
}
};
}
function resetEverything() {
modal.style.display = "none";
stopTimer();
timer.innerHTML = `00:00`;
numberOfMoves = 0;
moves.innerHTML = numberOfMoves;
matchedCards = [];
openCards = [];
startGame();
}
function shuffle(array) {
let currentIndex = array.length,
randomIndex;
while (currentIndex != 0) {
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex--;
[array[currentIndex], array[randomIndex]] = [
array[randomIndex],
array[currentIndex],
];
}
return array;
}
function runTimer() {
time = setInterval(() => {
seconds++;
if (seconds == 60) {
minutes++;
seconds = 0;
}
timer.innerHTML = `${minutes < 10 ? `0${minutes}` : minutes}:${
seconds < 10 ? `0${seconds}` : seconds
}`;
}, 1000);
}
function stopTimer() {
seconds = 0;
minutes = 0;
clearInterval(time);
}
playBtn.addEventListener("click", () => {
introScreen.classList.remove("fadeIn");
introScreen.classList.add("fadeOut");
game.classList.add("fadeIn");
startGame();
});
What you're running into is a common problem where it takes time for things like CSS to propagate through the website, especially when you have timeouts. What you should do is VERY RARELY rely on CSS classes for your logic and let CSS be what it's meant to be: a purely visual medium.
The solution is simple. Instead of relying on pointer events, set up a flag that toggles whether the user is allowed to click on something or not, and then reference that flag for all your logic. This allows you to decouple what you see from what's happening underneath the hood. Something like this (only the relevant bits):
let canAction = true; // add a flag
function startGame() {
// ... start game logic
canAction = true; // set the flag to allow actions
}
gameContainer.addEventListener("click", function (e) {
if (canAction) {
// ... card click logic
}
});
function compareCards() {
if (openCards.length == 2) {
canAction = false; // stop user from taking further action
}
// ... rest of compare card logic
}
function cardsMatched() {
canAction = true; // re allow the user to click on cards - put this inside the setTimeout or outside depending on what you need
setTimeout(function () {
// ...
}, 500);
countMoves();
}
function cardsNotMatched() {
canAction = true; // re allow the user to click on cards - put this inside the setTimeout or outside depending on what you need
setTimeout(function () {
// ...
}, 500);
countMoves();
}
Of course you are free to keep the pointer-events CSS stuff as well, but don't rely on it for your logic. You'll just be inviting a whole lot of messy situations like this.
The problem is that when I click the element with the class circulo it runs the function, but when I click on it again it does not do anything at all.
This is the code of the function.
function showTexto(texto) {
var status = 1;
document.querySelector('.circulo').addEventListener('click', function() {
console.log("hola")
if (status == 1) {
block = document.querySelector('.quedadas-content-body-p');
tostyle = block.style;
tostyle.minHeight = "100%";
tostyle.background = "#222"
block.innerHTML += `
<p class = "text-extra">${texto}</p>`;
tostyle.display = "block";
document.querySelector('.quedadas-content-body-p svg').style.transform = "rotate(180deg)";
document.querySelector('.quedadas-content-body-p p').style.margin = "0 10px";
status = 0;
} else {
console.log("status = 0");
block = document.querySelector('.quedadas-content-body-p');
block.removeAttribute("style");
document.querySelector('.text-extra').remove();
document.querySelector('.quedadas-content-body-p svg').removeAttribute("style");
status = 1;
}
console.log(status);
});
console.log("statusasdf");
}
I'm executing this function in my ajax implementation.
axios("json/").then(res=>{
responsiveHtml(res.data);
window.addEventListener('resize', _=> responsiveHtml(res.data));
showTexto( res.data.quedadas[3].texto);
})
I've stuck while calling close function in below code. This program create a new content respect to weekdays. After putting on to necessary place, if user want to delete the content the close.setSttribute("onclick", "removeItem(this,event)"); function have to invoke. But in my code something gone wrong. Please help to fix to invoke that function.
function addClick() {
var weekday = document.getElementById("weekdaysModal").value;
var name = document.getElementById("fname").value;
var itemContent = document.getElementById("textArea").value;
var divItem = document.createElement("div");
divItem.setAttribute("class", "divItem");
var close = document.createElement("img");
close.setAttribute("class", "close");
close.setAttribute("src", "./img/delete.png");
close.setAttribute("onclick", "removeItem(this, event)");
close.setAttribute("width", "20px");
close.setAttribute("height", "20px");
console.log(close);
divItem.appendChild(close);
var textDiv = document.createElement('div');
textDiv.setAttribute("class", "name");
textDiv.innerHTML = name;
divItem.appendChild(textDiv);
var content = document.createElement('div');
content.setAttribute("class", "itemContent");
content.innerHTML = itemContent;
content.style.display = 'none';
divItem.appendChild(content);
if (weekday == "monday") {
// divItem.setAttribute("index", monIndex);
divItem.style.background = "#d4f442";
document.getElementsByClassName('displayArea_mon')[0].appendChild(divItem);
} else if (weekday == "tuesday") {
// divItem.setAttribute("index", tueIndex);
divItem.style.background = "#65f441";
document.getElementsByClassName('displayArea_tue')[0].appendChild(divItem);
} else if (weekday == "wednesday") {
console.log("wed");
// divItem.setAttribute("index", wedIndex);
divItem.style.background = "#65f441";
document.getElementsByClassName('displayArea_wed')[0].appendChild(divItem);
} else if (weekday == "thursday") {
// divItem.setAttribute("index", thuIndex);
divItem.style.background = "#65f441";
document.getElementsByClassName('displayArea_thu')[0].appendChild(divItem);
} else if (weekday == "friday") {
// divItem.setAttribute("index", friIndex);
divItem.style.background = "#65f441";
document.getElementsByClassName('displayArea_fri')[0].appendChild(divItem);
} else {
console.log("Error");
}
closeButton();
console.log(name + content + weekday);
}
function removeItem() {
var x = getElementsByClassName('divItem');
x.delete(x.selectedIndex);
}
I'm building a DTMF dial emulator. After a certain function runs, the function dial() is executed. Currently, I hear the ringback tone once but I cannot get it to ring twice.
I originally tried to get the same file to play twice by resetting the audio currentTime to 0 and playing it again after a delay of 4 seconds (the duration between ringback tones in North America).
That didn't work so I thought perhaps JS didn't want me playing the same audio twice. So I recreated it again as a second variable and created a new function for that next in the sequence for dial().
That didn't work, but if I added an alert box before ringingTone2 played, it played as soon as I dismissed the alert.
Obviously, I can't have an alert dialog when this is done. How can I have the sound play twice, but with a 4 second gap in between? I've had no success with setTimeout(ring(), 4000) either.
Here is some of the code pertaining to this issue:
var ringingTone1 = new Audio('DTMF-ringbackTone.mp3');
var ringingTone2 = new Audio('DTMF-ringbackTone.mp3');
function dial() {
ring();
function ring() {
ringingTone1.play();
setTimeout(ringingTone2.play(),4000);
}
Right now, JS basically plays the ringback tone once and moves onto to what is after this in the script.
The following causes it to stop working:
function dial() {
ring();
function ring() {
var played = 0;
var maxPlay = 2;
var ringingTone = document.getElementById('music');
var playBtn = document.getElementById('playbtn');
ringingTone.onplay = ring() {
//played counter
played++;
};
ringingTone.addEventListener("ended", ring() {
//reset to start point
ringingTone.currentTime = 0;
if (played < maxPlay) {
ringingTone.play();
} else {
played = 0;
}
});
playBtn.addEventListener("click", ring() {
ringingTone.play();
});
}
This is my complete script:
var availableNumbers = ["0", "911"];
function numberSuggestion() {
var randomNumber = Math.random() * availableNumbers.length -1;
var suggestedNumber = availableNumbers[randomNumber];
document.getElementById("suggestion").innerHTML = "How about dialing " + suggestedNumber + "? Don't like this number? Click the button above again!";
}
var dialTone;
function offHook() {
document.getElementById("WE2500").style.display = "none";
document.getElementById("dialPad").style.display = "block";
dialTone = new Audio('dialTone.m4a');
dialTone.play();
}
var number = "";
var timeout;
function numberDial() {
if (dialTone) {
dialTone.pause();
dialTone.currentTime = 0;
}
clearTimeout(timeout);
timeout = setTimeout(dial, 2000);
}
function dial1() {
numberDial();
number = number + "1";
var tone1 = new Audio('DTMF-1.wav');
tone1.play();
}
function dial2() {
numberDial();
number = number + "2";
var tone2 = new Audio('DTMF-2.wav');
tone2.play();
}
function dial3() {
numberDial();
number = number + "3";
var tone3 = new Audio('DTMF-3.wav');
tone3.play();
}
function dial4() {
numberDial();
number = number + "4";
var tone4 = new Audio('DTMF-5.wav');
tone4.play();
}
function dial5() {
numberDial();
number = number + "5";
var tone5 = new Audio('DTMF-5.wav');
tone5.play();
}
function dial6() {
numberDial();
number = number + "6";
var tone6 = new Audio('DTMF-6.wav');
tone6.play();
}
function dial7() {
numberDial();
number = number + "7";
var tone7 = new Audio('DTMF-7.wav');
tone7.play();
}
function dial8() {
numberDial();
number = number + "8";
var tone8 = new Audio('DTMF-8.wav');
tone8.play();
}
function dial9() {
numberDial();
number = number + "9";
var tone9 = new Audio('DTMF-9.wav');
tone9.play();
}
function dial0() {
numberDial();
number = number + "0";
var tone0 = new Audio('DTMF-0.wav');
tone0.play();
}
function dialStar() {
numberDial();
number = number + "*";
var toneStar = new Audio('DTMF-star.wav');
toneStar.play();
}
function dialPound() {
numberDial();
number = number + "#";
var tonePound = new Audio('DTMF-pound.wav');
tonePound.play();
}
//var ringingTone1 = new Audio('DTMF-ringbackTone.mp3');
//var ringingTone2 = new Audio('DTMF-ringbackTone.mp3');
function dial() {
ring();
function ring() {
var played = 0;
var maxPlay = 2;
var ringingTone = document.getElementById('music');
var playBtn = document.getElementById('playbtn');
ringingTone.onplay = ring() {
//played counter
played++;
};
ringingTone.addEventListener("ended", ring() {
//reset to start point
ringingTone.currentTime = 0;
if (played < maxPlay) {
ringingTone.play();
} else {
played = 0;
}
});
playBtn.addEventListener("click", ring() {
ringingTone.play();
});
}
switch(number) {
case "0":
break;
case "911":
var pickup911 = new Audio('911-xxx-fleet.mp3');
pickup911.play();
break;
default:
}
}
The idea is simple, one counter for played time, one constant for max play.
Use the onplay event to increase the counter everytime a audio played.
Use the ended event to replay the audio if max play time not reached yet. Otherwise, set played count to 0.
var played = 0;
var maxPlay = 2;
var ringingTone = document.getElementById('music');
var playBtn = document.getElementById('playbtn');
ringingTone.onplay = function() {
//played counter
played++;
};
ringingTone.addEventListener("ended", function() {
//reset to start point
ringingTone.currentTime = 0;
if (played < maxPlay) {
ringingTone.play();
} else {
played = 0;
}
});
playBtn.addEventListener("click", function() {
ringingTone.play();
});
<audio id="music" src="http://www.noiseaddicts.com/samples_1w72b820/3732.mp3"></audio>
<button id="playbtn">Play me</button>
Calling a function in setTimeout (and I think it applies to all callbacks) using () will cause the code inside to be evaluated immediately (which in your case meant play both audios at the same time).
Hence my comment above to use .play without (), which looks like doesn't work. However, this does work:
function dial() {
function ring() {
ringingTone.play();
}
ring();
setTimeout(ring, 4000);
}
dial();
http://jsfiddle.net/tL3monyp/
(audio src unscrupulously copied from Daniel's answer ;-)
I've also added 2 timeouts to illustrate calling the function with and without ().
I was wondering why my program crashes after its made its first match....any ideas would be greatly appreciated. Below is the code snippet. Thanks for the input!
var clicks = 0; //counts how may picks have been made in each turn
var firstchoice; //stores index of first card selected
var secondchoice; //stores index of second card selected
var match = 0; //counts matches made
var backcard = "deck.jpg"; //shows back of card when turned over
var faces = []; //array to store card images
faces[0] = 'pic1.jpg';
faces[1] = 'pic2.jpg';
faces[2] = 'pic3.jpg';
faces[3] = 'pic3.jpg';
faces[4] = 'pic2.jpg';
faces[5] = 'pic1.jpg';
function choose(card) {
if (clicks === 2) {
return;
}
if (clicks === 0) {
firstchoice = card;
document.images[card].src = faces[card];
clicks = 1;
} else {
clicks = 2;
secondchoice = card;
document.images[card].src = faces[card];
timer = setInterval("check()", 1000);
}
}
/* Check to see if a match is made */
function check() {
clearInterval(timer); //stop timer
if (faces[secondchoice] === faces[firstchoice]) {
match++;
document.getElementById("matches").innerHTML = match;
} else {
document.images[firstchoice].src = backcard;
document.images[secondchoice].src = backcard;
clicks = 0;
return;
}
}
The first parameter of setInterval needs to be a function not a string pretending to be a function. So you would want this:
timer = setInterval(function() { check(); }, 1000);
Of course, you can simplify:
timer = setInterval(check, 1000);
Not sure why you're using setInterval() here. You could more easily just do:
timer = setTimeout(check, 1000);
The advantage is there is no interval to clear in the check() function.
The other issue is that you are not resetting your 'clicks' counter to 0 when there is a match.
You want this:
function check() {
clearInterval(timer); //stop timer
if (faces[secondchoice] === faces[firstchoice]) {
match++;
document.getElementById("matches").innerHTML = match;
} else {
document.images[firstchoice].src = backcard;
document.images[secondchoice].src = backcard;
}
clicks = 0;
}
I think you have to declare you timer function globally. Its only defined in the scope of the first function, so in the second when you try to clear it nothing happens:
var timer = ''; //Declare timer up here first!
function choose(card) { ... }
function check() { ... }