I'm a little confused. So I have a JavaScript game build in JS, HTML5 canvas, and Firebase. The weird thing I'm experiencing is when I play a game there isn't a problem as it sends the information to the backend. But if I refresh it's as if the previous score still is present so when information gets send, it gets sent twice. If I refresh again then it's 3X etc. In the image, you can see the name and score are present from previous rounds.
Backend showing repetition
The block of code where the information gets sent is
showInput() {
document.getElementById("inputName").type = "text";
document.getElementById("inputName").addEventListener("keyup", e => {
e.preventDefault();
if (e.keyCode === 13) {
const scores = firebase.database().ref("scores/");
let name = document.getElementById("inputName").value;
let score = this.score;
let highScore = { name, score };
scores.push(highScore);
document.getElementById("inputName").type = "hidden";
this.showLeaderBoard();
}
});
}
But it is strange since when a new game gets created. The information in it, for example, the score says this.score = 0 and it shows that. Can someone explain why a previous score still persist?
Thanks for all your help and explanation.
** Remove Listener **
showInput() {
document.getElementById("inputName").type = "text";
document.getElementById("inputName").addEventListener("keyup", e => {
console.log("event added");
e.preventDefault();
if (e.keyCode === 13) {
const scores = firebase.database().ref("scores/");
let name = document.getElementById("inputName").value;
let score = this.score;
let highScore = { name, score };
scores.push(highScore);
document.getElementById("inputName").type = "hidden";
this.showLeaderBoard();
}
});
document.getElementById("inputName").removeEventListener("keyup", e => {
console.log("event removed");
e.preventDefault();
if (e.keyCode === 13) {
const scores = firebase.database().ref("scores/");
let name = document.getElementById("inputName").value;
let score = this.score;
let highScore = { name, score };
scores.push(highScore);
document.getElementById("inputName").type = "hidden";
this.showLeaderBoard();
}
});
}
** Reset Function **
const Game = require("./game");
const Background = require("./background");
var GameInstance = null;
document.addEventListener("DOMContentLoaded", () => {
let preGame = () => {
const canvasStart = document.getElementById("start");
if (canvasStart.getContext) {
const ctxStart = canvasStart.getContext("2d");
ctxStart.font = "30px games";
ctxStart.fillStyle = "red";
ctxStart.fillText(
"Press R to Start!",
canvasStart.width / 2 - 110,
canvasStart.height / 2
);
}
};
document.getElementById("inputName").addEventListener("keyup", e => {
e.preventDefault();
if (e.keyCode === 13 && GameInstance) {
const scores = firebase.database().ref("scores/");
let name = document.getElementById("inputName").value;
let score = GameInstance.score;
let highScore = { name, score };
scores.push(highScore);
document.getElementById("inputName").type = "hidden";
GameInstance.showLeaderBoard();
}
});
preGame();
document.addEventListener("keypress", e => {
if (
e.key === "r" &&
document.getElementById("inputName").type === "hidden"
) {
let score = 0;
const canvasStart = document.getElementById("start");
if (canvasStart.getContext) {
const ctxStart = canvasStart.getContext("2d");
ctxStart.clearRect(0, 0, canvasStart.width, canvasStart.height);
}
const canvas = document.getElementById("canvas");
const canvasEnemy = document.getElementById("enemy");
const canvasScore = document.getElementById("scoreBoard");
const canvasGameOver = document.getElementById("gameOver");
if (canvas.getContext) {
const ctx = canvas.getContext("2d");
const ctxEnemy = canvasEnemy.getContext("2d");
const ctxScore = canvasScore.getContext("2d");
const ctxGameOver = canvasGameOver.getContext("2d");
GameInstance = new Game(
ctx,
canvas,
ctxEnemy,
canvasEnemy,
ctxScore,
canvasScore,
ctxGameOver,
canvasGameOver,
score
).start();
}
}
});
Your code need to be slightely refactored as the way it is now is causing some very serious problems:
Each instance of the game creates its own event listener for the input. That will send more and more requests with each new instance.
The instances you think are destroyed are still alive. They are trapped in the closures created by the multiple event listeners. That is a major memory leak.
Remove the event listener from showInput function. Define it only once. It should just check the current game instance and sends its score to the server when an input happens:
var gameInstance = null;
document.getElementById("inputName").addEventListener("keyup", function(e) {
e.preventDefault();
if (e.keyCode === 13 && gameInstance) { // check if there is a game instance
const scores = firebase.database().ref("scores/");
let name = this.value; // this is the input now
let score = gameInstance.score; // use gameInstance
let highScore = { name, score };
scores.push(highScore);
this.type = "hidden"; // here too
gameInstance.showLeaderBoard(); // ...
}
});
document.addEventListener("keypress", e => {
// ...
gameInstance = new Game(/* ... */); // don't forget to store the gameInstance so the event listener will have access to it
// ...
});
showInput should look like this:
showInput() {
document.getElementById("inputName").type = "text";
}
Which just shows the input as it supposed to be.
Related
i need to make a decibel meter
i try to use 2 libary but they wont work and they very old
i found a function that make this but it wont work as expected
first of all the issue is
inside this function i get the
" let decibelNumString = decibelNum.toString().substring(0, 2); "
which is get me the number of the decibel and make it to integear
but
i want to make the minNumber and MaxNumber as state because it should re render after the changes happen
the probme it make a lot of re render becuase every 100ms it change the numbers
i have also the current decibel so it need to be render every 100ms
and it not good for perfomance
// const [minNumber, setMinNumber] = useState(25);
// const [maxNumber, setMaxNumber] = useState(30);
let arr = [];
let volumeCallback = null;
let volumeInterval = null;
(async () => {
// Initialize
try {
const audioStream = await navigator.mediaDevices.getUserMedia({
audio: {
echoCancellation: true,
},
});
const audioContext = new AudioContext();
const audioSource = audioContext.createMediaStreamSource(audioStream);
const analyser = audioContext.createAnalyser();
analyser.fftSize = 512;
analyser.minDecibels = -127;
analyser.maxDecibels = 0;
analyser.smoothingTimeConstant = 0.4;
audioSource.connect(analyser);
const volumes = new Uint8Array(analyser.frequencyBinCount);
volumeCallback = () => {
analyser.getByteFrequencyData(volumes);
let volumeSum = 0;
for (const volume of volumes) volumeSum += volume;
const averageVolume = volumeSum / volumes.length;
// Value range: 127 = analyser.maxDecibels - analyser.minDecibels;
let decibelNum = (averageVolume * 100) / 127;
let decibelNumString = decibelNum.toString().substring(0, 2);
if (Number(decibelNumString) > maxNumber) {
// (maxNumber = Number(decibelNum))
// setMaxNumber((prev) => {
// return (prev = Number(decibelNumString));
// });
}
if (Number(decibelNumString) < minNumber) {
// setMinNumber((prev) => {
// return (prev = Number(decibelNumString));
// });
}
console.log(decibelNumString);
// console.log(minNumber + "min");
// console.log(maxNumber + "max");
arr.push(Number(decibelNumString));
// console.log(arr)
};
} catch (e) {
console.error("Failed to initialize volume visualizer, simulating instead...", e);
// Simulation
//TODO remove in production!
let lastVolume = 50;
volumeCallback = () => {
const volume = Math.min(Math.max(Math.random() * 100, 0.8 * lastVolume), 1.2 * lastVolume);
lastVolume = volume;
};
}
// Use
// start();
})();
const stop = () => {
clearInterval(volumeInterval);
// volumeInterval = null;
};
const start = () => {
// Updating every 100ms (should be same as CSS transition speed)
if (volumeCallback !== null && volumeInterval === null) volumeInterval = setInterval(volumeCallback, 100);
};
I have made a prank calculator which takes an input from the user of the answer to be displayed when the '=' button is pressed. The input is taken in the form of a prompt in JavaScript which is triggered by long-pressing the '0' button. The long press feature is implemented by setting a timeout of 2 seconds when the buttons is pressed (using onmousedown) and clearing the timeout when the mouse key is lifted (onmouseup). This works fine on computers, but I don't know how to implement this on mobile. I have tried the onpointerdown and onpointerup events too. These also work fine on computers, but when I long-press on mobile, the prompt displays a second time shortly afterwards. The HTML and JavaScript code relating to this is as follows:
HTML:
<td>
<button
class="calculator-button"
onclick="insert(0)"
onmousedown="activateMagic()"
onmouseup="disableMagic()"
>
0
</button>
</td>
JavaScript:
let answer = null;
const input = document.getElementById("calcInput");
let pressTimer;
const insert = (objToInsert) => {
input.value += objToInsert;
};
const clearInput = () => {
input.value = null;
answer = null;
};
const activateMagic = () => {
pressTimer = window.setTimeout(() => {
answer = prompt("Enter the prank answer: ");
}, 2000);
return false;
};
const disableMagic = () => {
clearTimeout(pressTimer);
};
const findAnswer = () => {
if (answer == null) {
let problemAnswer = eval(input.value);
setTimeout(() => {
input.value = problemAnswer;
}, 10);
}
input.value = answer;
};
Thanks in advance.
I found an answer!! #DanyloHalaiko was right. The touchstart and touchend event listeners actually work! To those of you interested or those of you seeing this in the future, this is my final code:
HTML:
<td>
<button
class="calculator-button"
onclick="insert(0)"
onmousedown="activateMagic()"
onmouseup="disableMagic()"
id="activateBtn"
>
0
</button>
</td>
JavaScript:
let answer = null;
const input = document.getElementById("calcInput");
const activateBtn = document.getElementById("activateBtn");
let pressTimer;
const insert = (objToInsert) => {
input.value += objToInsert;
};
const clearInput = () => {
input.value = null;
answer = null;
};
const activateMagic = () => {
pressTimer = window.setTimeout(() => {
answer = prompt("Enter the prank answer: ");
}, 2000);
return false;
};
const disableMagic = () => {
clearTimeout(pressTimer);
};
activateBtn.addEventListener("touchstart", activateMagic);
activateBtn.addEventListener("touchend", disableMagic);
const findAnswer = () => {
if (answer == null) {
let problemAnswer = eval(input.value);
setTimeout(() => {
input.value = problemAnswer;
}, 10);
}
input.value = answer;
};
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've been working on an alarm clock 'app' project to practice JavaScript. I'm fairly new to it so maybe I'm just not understanding clearInterval() correctly, and I was hoping someone could help.
JavaScript:
let sound = new Audio('./music/alarmsound.mp3');
let playAudio = () => sound.play();
const snooze = document.getElementById('snooze');
let pause = () => sound.pause();
let alarm = document.getElementById('alarm');
alarm.onclick = function setAlarm() {
let userHour = prompt("Please enter the hour of which you would like the alarm to be set 😄", "07");
if (userHour.charAt(0) > 0 && userHour < 10) {
userHour = "0" + userHour;
}
let userMinutes = prompt("Please enter the minutes of which you would like the alarm to be set 😄", "30");
let timeformat = [userHour, userMinutes];
function realTime() {
let realtime = new Date();
if(timeformat[0] == realtime.getHours() && timeformat[1] == realtime.getMinutes()) {
playAudio();
}
if(timeformat[0] != realtime.getHours && timeformat[1] != realtime.getMinutes()) {
pause();
}
}
let checkTime = () => {
setInterval(realTime, 1000)
}
checkTime();
let stopAlarm = () => {
clearInterval(checkTime);
}
snooze.addEventListener("click", stopAlarm());
}
When the user clicks the alarm button, a prompt asks for them to set the hours, and then the minutes, that they want the alarm to go off. That part works. In addition, once one minute has passed and the current time no longer matches the alarm time set by the user, the audio stops. However, I'm trying to add a snooze button functionality and I can't seem to get it working no matter what I try.
Any tips and tricks are greatly appreciated! Sorry if the code is messy, like I said, I just started with JS.
A few thing are making this not work as you expect. Check the suggested changes, I've added comments.
const snooze = document.getElementById('snooze');
const alarm = document.getElementById('alarm');
const sound = new Audio('./music/alarmsound.mp3');
const playAudio = () => sound.play();
const pause = () => sound.pause();
let intval; // 1. keep a reference to the interval outside of setAlarm()
alarm.onclick = function setAlarm() {
let userHour = prompt("Please enter the...", "07");
if (userHour.charAt(0) > 0 && userHour < 10) {
userHour = "0" + userHour;
}
let userMinutes = prompt("Please enter the...", "30");
let timeformat = [userHour, userMinutes];
function realTime() {
let realtime = new Date();
if(timeformat[0] == realtime.getHours() && timeformat[1] == realtime.getMinutes()) {
playAudio();
}
if(timeformat[0] != realtime.getHours && timeformat[1] != realtime.getMinutes()) {
pause();
}
}
intval = setInterval(realTime, 1000) // 2. assign interval to the variable
}
// 3. Dont set your event listener inside the setAlarm() function (unless it's required)
function stopAlarm() {
clearInterval(intval); // 4. Clear interval with correct reference
}
snooze.addEventListener("click", stopAlarm);
When searching for a way to warn a user before leaving web page if changes haven't been saved, I found this solution: https://stackoverflow.com/a/48238659/9512437, but the warning pops up even if the user is hitting the save button. I tried adding an event when the user clicks the submit button to set a variable to keep the warning from appearing, but now the warning never appears.
Here is what I tried:
<script>
"use strict";
var btn_click = false;
(() => {
const modified_inputs = new Set;
const defaultValue = "defaultValue";
// store default values
addEventListener("beforeinput", (evt) => {
const target = evt.target;
if (!(defaultValue in target || defaultValue in target.dataset)) {
target.dataset[defaultValue] = ("" + (target.value || target.textContent)).trim();
}
});
// detect input modifications
addEventListener("input", (evt) => {
const target = evt.target;
let original;
if (defaultValue in target) {
original = target[defaultValue];
} else {
original = target.dataset[defaultValue];
}
if (original !== ("" + (target.value || target.textContent)).trim()) {
if (!modified_inputs.has(target)) {
modified_inputs.add(target);
}
} else if (modified_inputs.has(target)) {
modified_inputs.delete(target);
}
});
addEventListener("beforeunload", (evt) => {
if (modified_inputs.size && !btn_click) {
const unsaved_changes_warning = "Changes you made may not be saved.";
evt.returnValue = unsaved_changes_warning;
return unsaved_changes_warning;
}
});
addEventListener("")
})();
document.getElementById("submit").onclick = function save() {
btn_click = true;
}
</script>
Any idea what I did wrong?
Turns out I was doing a couple things wrong. My guess is adding the document.getElementById("submit").onclick under "use strict" either caused an error (https://www.w3schools.com/js/js_strict.asp) or caused a problem with the detection since simply adding the function even without the && !btn_click caused it to not work. I also had to change if (modified_inputs.size && !btn_click) { to if (modified_inputs.size >> 0 && !btn_click) {.
In the end, the solution that ended up working for me is as follows:
<script>
var btn_click = false;
function save() {
btn_click = true;
}
"use strict";
(() => {
const modified_inputs = new Set;
const defaultValue = "defaultValue";
// store default values
addEventListener("beforeinput", (evt) => {
const target = evt.target;
if (!(defaultValue in target || defaultValue in target.dataset)) {
target.dataset[defaultValue] = ("" + (target.value || target.textContent)).trim();
}
});
// detect input modifications
addEventListener("input", (evt) => {
const target = evt.target;
let original;
if (defaultValue in target) {
original = target[defaultValue];
} else {
original = target.dataset[defaultValue];
}
if (original !== ("" + (target.value || target.textContent)).trim()) {
if (!modified_inputs.has(target)) {
modified_inputs.add(target);
}
} else if (modified_inputs.has(target)) {
modified_inputs.delete(target);
}
});
addEventListener("beforeunload", (evt) => {
if (modified_inputs.size >> 0 && !btn_click) {
const unsaved_changes_warning = "Changes you made may not be saved.";
evt.returnValue = unsaved_changes_warning;
return unsaved_changes_warning;
}
});
addEventListener("")
})();
</script>
Then add your onclick to the element:
<button type="submit" class="w3-button w3-right w3-theme" id="button" onclick="save()">Save</button>