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;
};
Related
I am creating a program where the timer starts when i hit "keyup" and stops when i hit "keydown" and resets the timer when i hit "keydown" next time.
const Timer = () => {
const [timer, setTimer] = useState(0);
let state = 0;
let isFired = false;
const increment = useRef(null);
useEffect(() => {
window.addEventListener('keydown', (e) => {
if (isFired) {
if (e.code === 'Space' && state === 2) {
e.stopPropagation();
isFired = false;
handleReset();
}
if (e.code === 'Space' && state === 1) {
clearInterval(increment.current);
state = 2;
}
}
});
window.addEventListener('keyup', (e) => {
if (!isFired) {
if (e.code === 'Space' && state === 0) {
isFired = true;
state = 1;
handleStart();
}
}
});
return () => {
window.removeEventListener('keydown', handleReset);
window.removeEventListener('keyup', handleStart);
};
}, []);
const handleStart = () => {
increment.current = setInterval(() => {
// DON'T COPY THIS BIT
setTimer((timer) => timer + 10);
}, 10);
};
const handleReset = () => {
clearInterval(increment.current);
setTimer(0);
state = 0;
};
// DON'T COPY THIS BIT
const formatTime = () => {
console.log(timer);
const getMilliSeconds = `0${timer % 60}`.slice(-2);
const seconds = `${Math.floor(timer / 60)}`;
const getSeconds = `0${seconds % 60}`.slice(-2);
const getMinutes = `0${Math.floor(timer / 3600)}`.slice(-2);
// return `${getHours} : ${getMinutes} : ${getSeconds}`;
if (getMinutes === '00') {
return `${getSeconds}.${getMilliSeconds}`;
} else {
return `${getMinutes} : ${getSeconds} : ${getMilliSeconds} `;
}
};
// const formatTime = () => {
// // const milliSeconds = `0${}`;
// const seconds = `0${Math.floor(timer / 100)}`;
// const minute = `0${Math.floor(timer / 3600)}`.slice(-2);
// return `${minute}.${seconds}`;
// };
return (
<div className="Timer">
<h1>{formatTime()}</h1>
</div>
);
};
i have tried this so far
it works most of the time but sometimes it gets glitchy
and i know the time is not formated properly and also the increment is also wrong. Sometimes it stops on the "keydown" and fires the "keyup" in the same stroke making it reset the timer starting from zero I don't exactly know that if it fires keyup on the same stroke but it seems like it does
this is a link to what i have done so far timer
I checked your code and found it needs too many changes.
You are using state and isFired as variables only but you need their persisted values on rerendering. Therefore, these must be states.
You are not unbinding the same listener which you binded on keyup and keydown.
As per your current code, your timer will start on 1st keyup and then next keydown it will stop as such where it was. Now, on next keydown it will reset to 0 and then on keyup, again timer will start from 0.
I modified your code and see working changes here,
https://codesandbox.io/s/xenodochial-shannon-im8zt?file=/src/App.js
From this How to delay the .keyup() handler until the user stops typing? question we've learned how to create delays. But any ideas on how do I cancel delayed event?
Check this out
In this example I don't want anything to be printed out after clicking the cancel button.
But I need more extensible solution. The solution might be to modify the delay() function somehow like this
delay(fn, ms, cancelCallback)
In here the cancelCallback would be a function that cancels the delay. By cancels the delay I mean to not call the fn() and just do nothing.
const inputElement = document.getElementById('input');
const buttonElement = document.getElementById('button');
const pElement = document.getElementById('p');
const delayInMs = 2000; // 2s delay
const delay = function (fn, ms) {
let timer = 0;
return function (...args) {
clearTimeout(timer);
timer = setTimeout(fn.bind(this, ...args), ms || 0);
};
};
const print = text => pElement.innerHTML = text;
const handleKeyUp = e => print(e.target.value);
inputElement.addEventListener('keyup', delay(handleKeyUp, delayInMs));
// Some new logic
const cancelDelay = () => {};
inputElement.addEventListener('click', cancelDelay);
<input id="input" />
<button id="button">Cancel</button>
<br />
<h6>You typed:</h6>
<p id="p"></p>
I figured this one out on my own. I think the solution is pretty clear.
const inputElement = document.getElementById('input');
const buttonElement = document.getElementById('button');
const pElement = document.getElementById('p');
const delayInMs = 2000; // 2s delay
// Modified
function delay(fn, ms) {
let timer = 0;
return {
call(...args) {
clearTimeout(timer);
timer = setTimeout(fn.bind(this, ...args), ms || 0);
},
cancel() {
clearTimeout(timer);
},
};
}
// Just show text
const print = text => pElement.innerHTML = text;
const myFunc = text => print(text);
const myFuncDelayed = delay(myFunc, delayInMs);
// Calling
const handleInputKeyUp = e => myFuncDelayed.call(e.target.value);
inputElement.addEventListener('keyup', handleInputKeyUp);
// Canceling
const handleBtnClick = () => { myFuncDelayed.cancel() };
buttonElement.addEventListener('click', handleBtnClick);
<input id="input" />
<button id="button">Cancel</button>
<br />
<h6>You typed:</h6>
<p id="p"></p>
First you need to add cancel handler for the buttonElement not for inputElement. In order to check if the cancel button has been clicked you can have one global variable flag and check if that is true which can be set to true when cancel button is clicked. Also, make sure to reset it to default on your input keyup handler.
const inputElement = document.getElementById('input');
const buttonElement = document.getElementById('button');
const pElement = document.getElementById('p');
const delayInMs = 2000; // 2s delay
let isCancelled = false;
const delay = function (fn, ms) {
let timer = 0;
return function (...args) {
clearTimeout(timer);
timer = setTimeout(fn.bind(this, ...args), ms || 0);
};
};
const print = text => pElement.innerHTML = text;
const handleKeyUp = e => {
if (!isCancelled) {
print(e.target.value)
}
isCancelled = false;
};
inputElement.addEventListener('keyup', delay(handleKeyUp, delayInMs));
// Some new logic
const cancelDelay = () => { isCancelled = true };
buttonElement.addEventListener('click', () => cancelDelay());
<input id="input" />
<button id="button">Cancel</button>
<br />
<h6>You typed:</h6>
<p id="p"></p>
var fired = false;
window.addEventListener('scroll', () => {
if (fired === false) {
fired = true;
setTimeout(() => {
let cartItem = document.getElementsByClassName('jstore-js-cart-total-count')[0];
let countItem = cartItem.textContent;
let cartCloudCount = document.getElementsByClassName('sc_layouts_cart_items_short')[0];
cartCloudCount.textContent = countItem
cartItem.addEventListener('DOMSubtreeModified', function () {
let countItem2 = cartItem.textContent;
let cartCloudCount2 = document.getElementsByClassName('sc_layouts_cart_items_short')[0];
cartCloudCount2.textContent = countItem2
});
}, 100)
}
});
I have a listener to catch events for adding to the cart.
But for some reason it works only for one product, if you work with one product, then it successfully changes the quantity, but if you add the second, then nothing changes, what's the problem?
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.
I am working on a phonegap project. I need to implement a long press event. How can we detect long press on a image/button using JavaScript?
$('#target').mousedown(function() {
alert('Handler for .mousedown() called.');
//start a timer
});
$('#target').mouseup(function() {
alert('Handler for .mouseup() called.');
//stop the timer and decide on long click
});
One way that comes in my mind is:
In the start of the onclick event, record the time, this gives you the time of the first click.
Then check the time span. Suppose, you say 5 seconds time span is a long press event. If check is success, this is a long press event.
head-on solution:
const btn = document.querySelector("button");
const info = document.querySelector("h3");
let time;
btn.onmousedown = () => {
time = new Date(Date.now()).getSeconds();
setTimeout(() => {
let timecurrent = new Date(Date.now()).getSeconds();
if (timecurrent - time == 5) {
info.innerText = "yes pressed";
time = undefined;
}
}, 5000);
}
btn.onmouseup = () => {
time = undefined;
info.innerText = "no pressed";
}
<button>press me</button>
<h3>No pressed</h3>
Function solution:
function catchLongPress(element, delay) {
const elem = document.querySelector(element);
let time;
elem.onmousedown = () => {
time = new Date(Date.now()).getSeconds();
setTimeout(() => {
let timecurrent = new Date(Date.now()).getSeconds();
if (timecurrent - time == delay) {
alert("yes pressed");
time = undefined;
}
}, delay * 1000);
}
elem.onmouseup = () => {
time = undefined;
alert("no pressed");
}
}
catchLongPress("button", 2);
<button>press me</button>
<h3>No pressed</h3>