How do I make a countdown feature in a button in HTML5 - javascript

I am creating a clicker-like game in HTML5+Javascript and for the clicking part, there is a timeout... I tried it my self but for some reason it does not work.
HTML:
function pursuit() {
var btn = document.getElementById("pursuit");
setInterval(function(){
timeOnPursuit -= 1;
}, 1000)
while(timeOnPursuit > 0){
btn.value = "In Pursuit: " + timeOnPursuit;
}
}
<div class="button">
<button id="pursuit" class="pursuit" onClick="pursuit()"> Go on a Pursuit </button>
</div>

A few things to consider here...
First, the code is producing an error in the browser console. You never defined the timeOnPursuit variable, so you aren't really controlling its scope. Simply declare that variable with an initial value (whatever value you like) before trying to use it:
var timeOnPursuit = 10;
Aside from that, your loop is going to block the thread. Because the variable is initially greater than 0, and because nothing in that loop modifies that value, it's going to run forever and never let the interval get a chance to modify the value.
Get rid of the loop entirely and rely on the interval instead. Within the interval you can update the UI. (Which, incidentally, should probably be innerText instead of value in this case.) Then within that interval you can also check when to stop the interval, which is what I suspect you were trying to do with the loop.
For a bonus, you might also want to disable the button so the user can't click it again, which would create another counter.
Overall, maybe something like this:
function pursuit() {
var btn = document.getElementById("pursuit");
// Start the "pursuit"
var timeOnPursuit = 10;
btn.innerText = "In Pursuit: " + timeOnPursuit;
btn.disabled = true;
// Repeat every second
var interval = setInterval(function(){
// Update the "pursuit"
timeOnPursuit -= 1;
btn.innerText = "In Pursuit: " + timeOnPursuit;
// Stop the interval at 0
if (timeOnPursuit <= 0) {
clearInterval(interval);
}
}, 1000)
}
<div class="button">
<button id="pursuit" class="pursuit" onClick="pursuit()"> Go on a Pursuit </button>
</div>

function pursuit() {
let timeOnPursuit = 10 // define time
var btn = document.getElementById("pursuit");
const timer = setInterval(function() {
console.log(timeOnPursuit)
if (timeOnPursuit > 0) {
btn.innerHTML = "In Pursuit: " + timeOnPursuit;
timeOnPursuit -= 1; // reduce time
} else {
btn.innerHTML = "Pursuit";
clearInterval(timer) // stop the timer
}
}, 1000)
}
<div class="button">
<button id="pursuit" class="pursuit" onClick="pursuit()"> Go on a Pursuit </button>
</div>

I was going to write a lot of what David wrote so it seems a little redundant now. Instead here's an alternative approach that uses setTimeout instead of setInterval.
// Cache the element and add a click listener to it
// (no need for inline JS)
const btn = document.querySelector(".pursuit");
btn.addEventListener('click', pursuit, false);
function pursuit() {
// Initialise timeOnPursuit
function loop(timeOnPursuit = 10) {
// Display the button text, and disable the button
btn.innerText = `In Pursuit: ${timeOnPursuit}`;
btn.disabled = true;
// If timeOnPursuit is greater than zero
// call loop again with a a decremented timeOnPursuit
if (timeOnPursuit > 0) {
setTimeout(loop, 1000, --timeOnPursuit);
}
}
// Call the loop function
loop();
}
<div class="button">
<button class="pursuit"> Go on a Pursuit </button>
</div>

Related

Button won't respond when execute the setInterval function

I would like to have the second button ( "free five") to keep checking the condition that it would reappear when the number reached 5 again after I clicked it. However, it never showed up again after I pressed it, unless I press the first button.
Game instruction is in the p section.
let birdNum = document.getElementById("birdNum")
let catchBtn = document.getElementById("catch")
let freeBtn = document.getElementById("free")
function catchBird() {
birdNum.innerHTML++;
if (birdNum.innerHTML > 4) {
freeBtn.classList.add("fadeIn");
freeBtn.disabled = false;
}
}
document.getElementById("catch").addEventListener("click", catchBird);
function freeBird() {
birdNum.innerHTML -= 5;
setInterval(() => {
birdNum.innerHTML++;
}, 1000);
if (birdNum.innerHTML <= 4) {
freeBtn.classList.remove("fadeIn");
freeBtn.disabled = true;
}
}
document.getElementById("free").addEventListener("click", freeBird);
#free {
opacity: 0;
}
#free.fadeIn {
opacity: 1;
}
<P>
press "catch one" to catch one bird. <br> press "free five" to free five bird. <br>
<br> Once you free five birds, they will come back with their kids, one bird per second.
</P>
<span id="birdNum">0</span>
<button id="catch">
catch one
</button>
<button id="free" disabled=t rue>
free five
</button>
Try the following code, see if it works for you:
Edit: I realised your issue, I edited to work now.
So in your code, you were only checking whether or not to make the 'free' button visible once. And you only checked when it was pressed. For example, we are playing your game and get to 6. We press the 'free birds' button and we go down to one and the 'free birds' button disappears. Now the setInterval keeps making the number of birds go up. We get to 5 and the button does not reappear because we are not running any code to make it reappear. In the setInterval, as well as incrementing the counter, we need to check whether or not we can make the 'free birds' button visible again. See the resetInterval() function. In the setInterval's callback, we are now checking whether of not the birds counter is above 5. If the condition returns true, we will show the 'free birds' button.
With the below JS, it should work nicely.
const birdNum = document.getElementById("birdNum");
const catchBtn = document.getElementById("catch");
const freeBtn = document.getElementById("free");
const initialTimeout = 1000;
let timesBirdsFreed = 0;
let intervalId;
function resetInterval(delay) {
if (delay < 10) delay = 10;
if (intervalId) clearInterval(intervalId);
intervalId = setInterval(() => {
birdNum.innerHTML++;
// if statement from catch bird function is needed here too
if (birdNum.innerHTML >= 5) {
freeBtn.classList.add("fadeIn");
freeBtn.disabled = false;
}
}, delay);
}
function catchBird() {
birdNum.innerHTML++;
if (birdNum.innerHTML >= 5) {
freeBtn.classList.add("fadeIn");
freeBtn.disabled = false;
}
}
catchBtn.addEventListener("click", catchBird);
function freeBird() {
timesBirdsFreed++;
birdNum.innerHTML -= 5;
resetInterval(initialTimeout/timesBirdsFreed);
if (birdNum.innerHTML <= 4) {
freeBtn.classList.remove("fadeIn");
freeBtn.disabled = true;
}
}
freeBtn.addEventListener("click", freeBird);
#free {
opacity: 0;
}
#free.fadeIn {
opacity: 1;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>Bird Game</title>
<link href="style.css" rel="stylesheet" type="text/css" />
</head>
<body>
<p>press "catch one" to catch one bird. <br> press "free five" to free five bird. <br><br> Once you free five birds, they will come back with their kids, one bird per second.</p>
<span id="birdNum">0</span>
<button id="catch">catch one</button>
<button id="free" disabled=true>free five</button>
<script src="script.js"></script>
</body>
</html>
Edit 2: Further explanation and added links to setInterval() method and clearInterval() method.
function resetInterval(delay) {
// if delay is too small (less than 10 ms) make delay 10 ms
if (delay < 10) delay = 10;
// in beginning of the program, intervalId is undefined
// so we will only use clearInterval if intervalId is not undefined (if we've used setInterval already)
if (intervalId) clearInterval(intervalId);
// use setInterval method with a delay which was passed to the function as an argument
intervalId = setInterval(() => {
// make birds go up by one
birdNum.innerHTML++;
// if statement from catch bird function is needed here too
// (if birds counter is 5 or more, show 'free birds' button)
if (birdNum.innerHTML >= 5) {
freeBtn.classList.add("fadeIn");
freeBtn.disabled = false;
}
}, delay);
// all of this resets the interval but at a smaller delay so birds go up faster each time
}
The resetInterval() function just stops an existing setInterval() from executing code by using clearInterval(intervalId) which uses the ID returned when setInterval() when it was called. An ID can be, for example, 239 or maybe 146. After the existing setInterval() was cleared, another setInterval() is called and its ID is stored in the variable (intervalId) to be used to clear the new setInterval() the next time resetInterval() is called (the next time you free five birds). The difference though is with each call of the resetInterval() function, the setInterval's delay which was passed into the function is smaller, making the number of birds go up faster.
The way this is done is when the resetInterval function is called, a delay is passed into the function as a parameter like so resetInterval(initialTimeout/timesBirdsFreed). initialTimeout is set to always be 1000 (milliseconds). timesBirdsFreed is a number which tells you how many times you clicked the 'free birds' button. If you clicked 'free birds' once, initialTimeout/timesBirdsFreed will be 1000/1 which is 1000 (ms). So the first time you clicked 'free birds', the number of birds will go up by one bird every second. The second time you click 'free birds', initialTimeout/timesBirdsFreed will be 1000/2 which is 500 (ms), meaning now birds will go up by one every half a second.
Because of this, the more you click 'free birds', the faster they will go up (up to a limit - smallest delay possible is 10ms, so it won't go any faster than that).
References
MDN Web Docs
setInterval() method
W3Schools
setInterval() method
clearInterval() method
If you need to stop the setInterval you should keep its instance and then call clearInterval(instance)
I'm not sure what you want to achieve but you may get the idea from the this code:
let intervalInstance;
function freeBird() {
birdNum.innerHTML -= 5;
if (intervalInstance) {
clearInterval(intervalInstance);
}
intervalInstance = setInterval(() => {
birdNum.innerHTML++;
}, 1000);
if (birdNum.innerHTML <= 4) {
freeBtn.classList.remove("fadeIn");
freeBtn.disabled = true;
}
}

Adding a reset button to a Javascript HTML game?

I am second semester, taking a class in Javascript. Basically, we were given the HTML and CSS for a website, and it is supposed to do the following:
It's a number game. The computer generates a number, and you have ten tries to guess this number. If you get to zero, the computer wins, and there is a reset button which should reset all the variables and start again. Only problem is, I cannot for the life of me figure out how to reset the countDown variable after the score reaches 0. Please help. Also we are using only pure Javascript for this course for now. I don't want to cheat, I am more trying to figure out what the issue is that's holding me back.
var countDown = 10;
var computerNumber = Math.floor((Math.random() * 501) + 1);
function generate() {
playerNumber = document.getElementById("guess").value;
if (computerNumber == playerNumber && countDown > 0) {
alert("Congratulations! You've won!");
} else if (playerNumber < computerNumber && countDown > 0) {
countDown--;
document.getElementById("guesses").value = countDown;
document.getElementById("result").value = "Too Low";
} else if (playerNumber > computerNumber && countDown > 0) {
countDown--;
document.getElementById("guesses").value = countDown;
document.getElementById("result").value = "Too High";
} else if (countDown == 0) {
alert("Game Over. The Number Was " + computerNumber);
}
}
function reset() {
countDown = 10;
computerNumber = Math.floor((Math.random() * 501) + 1);
}
In the reset function, you would need to update the element that displays the countDown in the HTML
Here you need to add the reset button in the html:
<input type="button" class="reset-button" value="Reset Count">
Then grab that button in your JS below the reset function and attach an eventListener that fires the reset function when clicked:
const resetBtn = document.querySelector('.reset-button')
resetBtn.addEventListener('click', reset )
And that's it.
You would probably want the count displayed on the page, too. You could add a line in the reset function that pushes the new value of countDown into the html (with element.textContent = countDown.toString(), for example)
If you take a JS class in 2021 you should definitely use const and let instead of var, and let your teacher know why. Using var works, though, but will show a future employer that you're out of touch with what's going on in the JS world.

How to reset game score and button text

I'm making a whack a mole game for a school project and I've gotten the actual game to work, however I'm having an issue resetting the game once the 30 second timer runs out. The game is made with javascript and p5.js and as of right now when you load the page, the game div display is set to hidden, but when you click "begin" the display changes to block and the timer starts (the game is on github for reference https://abm96testgithub.github.io/whackamole/). When the 30 seconds are up, the "begin" button changes to "reset" and the game display goes back to "none" (both done using document.getElementByID).
Is there a way to make it so that when the player hits "reset" the entire page will reload or so that the button will read "begin" again and the score will reset?
I know I can make a separate reset button with a function for this, but I feel like it would mess up the aesthetic of the page to have two buttons.
The html for the button when the page first loads is
<button id="startButton" onclick="startGame();startTimer()">Begin!</button>
and the javascript for it is
function startGame() {
document.getElementById('sketch-holder').style.display = "block";
}
function countdown () {
var startCountdown = setInterval(function() {
document.getElementById('timerVar').innerHTML = counter + "s";
counter--;
if (counter < 0) {
clearInterval(startCountdown);
document.getElementById('sketch-holder').style.display = "none";
document.getElementById('startButton').innerHTML = "Reset";
}
}, 1000)
}
function startTimer() {
if (timerOn === false) {
timerOn = true;
countdown();
}
}
It's a little strange to use setInterval() when you're using P5.js. P5.js has its own internal timing mechanisms, which we talked about in your last question.
Instead of using setInterval(), I'd use the millis() function or the frameCount variable to perform timing logic. See my code in my answer to your last question:
function setup() {
createCanvas(200,200);
background(32);
}
function draw(){
// 60 frames is 1 second
if(frameCount % 60 == 0){
ellipse(random(width), random(height), 25, 25);
}
}
This code draws a circle once per second. This is just an example, but you could do something very similar to reset your game after 30 seconds.
Then to reset your game, all you really need to do is set any variables you're using back to their default values. Try writing a reset() function that does exactly that.
If I understood right, Instead of Button You can go witha a tag with void href then on click you can add href="javascript:window.location.href=window.location.href" which will reload the page.
function startGame() {
document.getElementById('sketch-holder').style.display = "block";
}
function countdown () {
var startCountdown = setInterval(function(){
document.getElementById('timerVar').innerHTML = counter + "s";
counter--;
if (counter < 0) {
clearInterval(startCountdown);
document.getElementById('sketch-holder').style.display = "none";
document.getElementById('startButton').innerHTML = "Reset";
document.getElementById('startButton').setAttribute("href", 'javascript:window.location.href=window.location.href"');
}
}, 1000)
}
function startTimer() {
if (timerOn === false) {
timerOn = true;
countdown();
}
}
Begin!

setTimeout executes itself right away/on clear

I'm making a webpage where user events are logged in.
To test the feature I made a small, independant webpage with a teaxtarea and a text input. The events logged are those performed on the input element.
I want to prevent the same event text to be shown multiple times in a row, but I can't seem to prevent them from showing up!
I also want to add a line to separate event groups 0.5 seconds after no other event happened, but the line seems to appear on every event trigger, evenif I use clearTimeout with the timeout ID.
Basically: I don't want any line to be repeated. If the last line is a separator line, then it must not add another one. Yet it doesn't see to work.
JSFiddle Demo
Here is my code:
JavaScript
var timerID = 0;
function addSeparateLine()
{
document.getElementById('listeEvenements').value += "--------------------\n";
}
function show(newEventText)
{
var eventListField = document.getElementById('listeEvenements');
var eventList = [];
if (eventListField.value.length > 0)
{
eventList = eventListField.value.split("\n");
}
var eventCounter = eventList.length;
if (eventList[eventCounter - 2] == newEventText)
{
clearTimeout(timerID);
newEventText = "";
}
timerID = setTimeout(addSeparateLine, 500);
if (newEventText !== "")
{
eventListField.value += newEventText + "\n";
}
return true;
}
HTML
<fieldset id="conteneurLogEvenements">
<legend>Events called from HTML attribute</legend>
<textarea id="listeEvenements" rows="25"></textarea>
<input id="controleEcoute" type="text" onBlur="show('Blur');" onchange="show('Change');" onclick="show('Click');" onfocus="show('Focus');" onMousedown="show('MouseDown');" onMousemove="show('MouseMove');" onMouseover="show('MouseOver');" onkeydown="show('KeyDown');"
onkeypress="show('KeyPress');" onkeyup="show('KeyUp');" />
</fieldset>
http://jsfiddle.net/z6kb4/2/
It sounds like what you want is a line that prints after 500 milliseconds of inactivity, but what your code currently says to do is "print a line 500 milliseconds after any action, unless it gets canceled". You can get better results by structuring the code more closely to your intended goal.
Specifically, instead of scheduling a new timeout every time an event occurs, simply start a loop when the first event occurs that checks the time that has elapsed since the most recent event received and then prints a line when the elapsed time exceeds the desired threshold (500 milliseconds). Something like:
function addSeparateLine() {
var elapsed = new Date().getTime() - lastEventTime;
if (elapsed >= 500) {
document.getElementById('listeEvenements').value += "--------------------\n";
clearInterval(timerID);
timerID = -1;
}
}
...and then you schedule it like:
if(newEventText !== "") {
lastEventTime = new Date().getTime();
eventListField.value += newEventText+"\n";
if (timerID == -1) {
timerID = setInterval(addSeparateLine,100);
}
}
Working example here: http://jsfiddle.net/z6kb4/4/
Because you are not actually stopping the show function in any way. The clearTimeout only applies to the separator add. I have updated your fiddle. You need to wrap your function with
if (+new Date() - lastfire < 500) return;
and
lastfire = +new Date();
(before the last return--see the updated fiddle). Also, make sure to stick the global definition var lastfire = -1; somewhere up top.

How do I implement press and hold button javascript?

I'm a complete novice, looking for instructions on implementing javascript. I am attempting to replace a YUI slider with buttons and a text field. I am trying to achieve buttons that, when held down, will continue to make the text field increase, preferably at a faster and faster rate. (http://www.blackbird502.com/white.htm)I have this in the java tag in the head:
function holdit(btn, action, start, speedup) {
var t;
var repeat = function () {
action();
t = setTimeout(repeat, start);
start = start / speedup;
}
btn.mousedown = function() {
repeat();
}
btn.mouseup = function () {
clearTimeout(t);
}
/* to use */
holdit(btn, function () { }, 1000, 2);
/* x..1000ms..x..500ms..x..250ms..x */
I have no clue how to implement the press and hold into the following in the body:
<form><input type=button value="UP" class="btn" onClick="javascript:this.form.amount.value++;"><br /><input type=text name=amount value=5 class="text"><br /> <input type=button value="DOWN" class="btn" onClick="javascript:this.form.amount.value--;" ></form>
Is it possible? Thanks.
This code should do everything you're looking for; it's based very loosely on tj111's example. I tried to make it as reusable as possible, and it doesn't need JavaScript mixed in with the HTML.
You do need to add IDs to the buttons (btnUP and btnDOWN) and text field (amount). You can change these IDs in the window.onload statement.
// This function creates a closure and puts a mousedown handler on the element specified in the "button" parameter.
function makeButtonIncrement(button, action, target, initialDelay, multiplier){
var holdTimer, changeValue, timerIsRunning = false, delay = initialDelay;
changeValue = function(){
if(action == "add" && target.value < 1000)
target.value++;
else if(action == "subtract" && target.value > 0)
target.value--;
holdTimer = setTimeout(changeValue, delay);
if(delay > 20) delay = delay * multiplier;
if(!timerIsRunning){
// When the function is first called, it puts an onmouseup handler on the whole document
// that stops the process when the mouse is released. This is important if the user moves
// the cursor off of the button.
document.onmouseup = function(){
clearTimeout(holdTimer);
document.onmouseup = null;
timerIsRunning = false;
delay = initialDelay;
}
timerIsRunning = true;
}
}
button.onmousedown = changeValue;
}
//should only be called after the window/DOM has been loaded
window.onload = function() {
makeButtonIncrement(document.getElementById('btnUP'), "add", document.getElementById('amount'), 500, 0.7);
makeButtonIncrement(document.getElementById('btnDOWN'), "subtract", document.getElementById('amount'), 500, 0.7);
}
This is kind of quick and dirty, but it should give you a start. Basically you want to set up a few initial "constants" that you can play with to get the desired behavior. The initial time between increments is 1000 ms, and on each iteration if become 90% of that (1000, 990, 891, ... 100) and stops getting smaller at 100 ms. You can tweak this factor to get faster or slower acceleration. The rest I believe is pretty close to what I think you were going for. It seems like you were just missing the event assignments. In the window.onload you'll see that i assign the onmouseup, and onmousedown events to functions that just call the increment() or decrement() functions with your initial timeout, or the ClearTimeout() function to stop the counter.
EDIT: I changed this slightly to fix the bug. Now if you move your mouse pointer off the button and release it will stop the counter.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">
<head>
<title><!-- Insert your title here --></title>
<script>
// Fake Constants
var INITIAL_TIME = 1000;
var ACCELERATION = .9;
var MIN_TIME = 100;
// create global variables to hold DOM objects, and timer
var up = null,
down = null,
count = null,
timer = null;
// Increment the counter
function increment (time) {
// decrease timeout by our acceleration factor, unless it's at the minimum
time = (time * ACCELERATION > MIN_TIME) ? (time * ACCELERATION) : MIN_TIME;
count.value ++ ;
// set the timeout for the next round, and pass in the new smaller timeout
timer = setTimeout(
function () {
increment(time);
}, time);
}
// Same as increment only subtracts one instead of adding.
// -- could easily make one function and pass an pos/neg factor instead
function decrement (time) {
time = time * ACCELERATION > MIN_TIME ? (time * ACCELERATION) : MIN_TIME;
count.value --;
timer = setTimeout(
function () {
decrement(time);
}, time);
}
// Initialize the page after all the forms load
window.onload = function () {
// initialization function
// assign DOM objects to our vars for ease of use.
up = document.getElementById('up_btn');
down = document.getElementById('dwn_btn');
count = document.getElementById('count');
// create event handlers for mouse up and down
up.onmousedown = function () {
increment(INITIAL_TIME);
}
down.onmousedown = function () {
decrement(INITIAL_TIME);
}
document.onmouseup = function () {
clearTimeout(timer);
}
}
</script>
</head>
<body>
<!-- Insert your content here -->
<form name="the_form">
<input type="button" value="Up" id="up_btn" /><br />
<input type="button" value="Down" id="dwn_btn" /></br>
<br />
Count:
<input type="text" value="0" id="count" />
</form>
</body>
</html>
The easiest method would be to just add an ID to each of the buttons, then use those to retrieve the elements and add the events.
//should only be called after the window/DOM has been loaded
window.onload = function() {
//the buttons
var btnUP = document.getElementById('btnUP');
var btnDOWN = document.getElementById('btnDOWN');
//the amount
var amount = document.getElementById('amount');
//actions to occur onclick
var upClick = function() {
amount.value++;
}
var downClick = function() {
amount.value--;
}
//assign the actions here
holdit(btnUP, upClick, 1000, 2);
holdit(btnDOWN, downClick, 1000, 2);
}
<form>
<input type=button value="UP" class="btn" id='btnUP'>
<br />
<input type=text name=amount value=5 class="text" id='amount'>
<br />
<input type=button value="DOWN" class="btn" id='btnDOWN'>
</form>
One aspect not to be overlooked is that you're hooking into the onclick event - which happens on a complete click (Mouse key down and key up). It sounds like you would want to listen for another distinct event, http://www.w3schools.com/jsref/jsref_onmousedown.asp'>onMouseDown . I think if you were to then implement some of the other timer based solutions, already given you would get the functionality you're asking for.
Good luck!

Categories