I have two quantity selector buttons.
After the user clicks in any of these buttons (increasing or decreasing quantity), I need to run a function.
But the user can click several times in a row, and I want to execute the function only once.
Like, wait 1 second after each click to run the function. If within this 1 second the user clicks the button again, reset the timer and wait for another second to run the function. When the user doesn´t click again within 1 second, run the function.
What´s the best way to do that in vanilla javascript?
You just need to start a 1 second timer and reset it whenever the button click happens.
let timer
function handleClick() {
clearTimeout(timer)
timer = setTimeout(doSomething, 1000);
}
function doSomething() {
let div = document.getElementById("list")
let p = document.createElement("p")
p.textContent = "1 second passed without a click"
div.append(p)
}
<!DOCTYPE html>
<html>
<body>
<button onclick="handleClick()">Click me</button>
<div id=list></div>
</body>
</html>
I see two solutions:
If each click triggers some request, then disable button until the request is completed
Use throttling for the function invoke. You can use RxJS's throttle or you can write your own throttle method.
https://medium.com/nerd-for-tech/debouncing-throttling-in-javascript-d36ace200cea
You described a classic debounce technique. Here is an implementation:
Taken from https://github.com/you-dont-need/You-Dont-Need-Lodash-Underscore#_debounce
Another way is to dig into Lodash source codes and copy it from there
function debounce(func, wait, immediate) {
var timeout;
return function() {
var context = this, args = arguments;
clearTimeout(timeout);
timeout = setTimeout(function() {
timeout = null;
if (!immediate) func.apply(context, args);
}, wait);
if (immediate && !timeout) func.apply(context, args);
};
}
// Avoid costly calculations while the window size is in flux.
jQuery(window).on('resize', debounce(calculateLayout, 150));
Here is a nice article and a playground about it https://css-tricks.com/debouncing-throttling-explained-examples/
You can try this in Vanilla JS :
const btn = document.querySelector('#btn');
const callback = event => {
console.log(event);
clearInterval(interval);
}
let interval;
btn.addEventListener('click', event => {
clearInterval(interval);
interval = setInterval( () => callback(event), 1000);
});
And the HTML :
<html>
<body>
<button id="btn">test</button>
</body>
</html>
That way you can pass the event instance to your callback.
Related
The below javascript code auto refresh the page every 10 sec.
My question is how can I reset the timer on mousemove and keypress.
Java-Script
<script type="text/javascript" language="JavaScript">
var secs;
var timerID = null;
var timerRunning = false;
var delay = 1000;
function InitializeTimer(seconds) {
//length of the timer, in seconds
secs = seconds;
StopTheClock();
StartTheTimer();
}
function StopTheClock() {
if (timerRunning)
clearTimeout(timerID);
timerRunning = false;
}
function StartTheTimer() {
if (secs == 0) {
StopTheClock();
window.location.href = window.location.href;
}
else {
document.getElementById("lbltime").innerText = secs + " ";
secs = secs - 1;
timerRunning = true;
timerID = self.setTimeout("StartTheTimer()", delay);
}
}
</script>
Thank you in advance.
If I understood you correctly, something like this (really basic example: the timer is reset on mousemove and keypress):
var elem = document.getElementById("timer"), timeout, startTimer = function timer() {
elem.textContent++;
timeout = setTimeout(timer, 1000)
}
function resetTimer() {
// here you reset the timer...
clearTimeout(timeout);
elem.textContent = -1;
startTimer();
//... and also you could start again some other action
}
document.addEventListener("mousemove", resetTimer);
document.addEventListener("keypress", resetTimer);
startTimer();
<span id="timer">-1</span> - Move cursor or press a key to reset timer.
As I guess, you want to delay page reloading if there was some user activity on the page. In your reload() function you just need to check, if timer's value reached the limit (like 60 seconds). If yes, execute reloading. However, notice, that handling mousemove event can make browser's tab have high CPU usage, if, for example, I just start to move my cursor quickly. You might want to pick some important areas and set handlers on the corresponding elements. For example, keypress event can be listened to only on a comment form or mousemove can be listened to only on a menu or on an article.
PS. Also take into account that
The keypress event is fired when a key is pressed down, and that key normally produces a character value
so, if user presses, for example, ctrl or alt the event will not be fired.
Your code seems a bit too branched out in my opinion, what I would do is simplify it a bit.
window.refreshTimeout = null;
function refreshPage(){
location.reload();
}
function refresh(){
clearTimeout(window.refreshTiemout);
window.refreshTimeout = setTimeout(refreshPage, 2000)
}
window.refreshTimeout = setTimeout(refreshPage, 2000)
window.addEventListener('mousemove', refresh)
<h4>HelloWorld</h4>
As you can see, you attach an event listener to the window for a mouse move (you can attach others too) within which you cancel the previous timeout and start a new one. Initially you of course start a timeout.
My objective is to keep a user in a view as long as he/she keeps clicking a button within a certain lapse.
I'm using Rails and was exploring a solution via an embedded JS in the pertinent view.
So far I'm able to set a time after which the user will be redirected to root path with the following script:
var delayedRedirect = function (){
window.location = "/";
}
var delay = 10000;
$(document).ready(function() {
setTimeout('delayedRedirect()', delay);
});
I've been trying to write a function that resets the value of 'delay'or that calls the setTimeoutFunction again.
$('#btn-persist').click(function() {
delay = 3000;
// or calling again setTimeout('delayedRedirect()', delay);
});
But I noticed that changing the variable won't affect the setTimeout function that has already been called.
I've also tried to use the clearTimeout function as below without success
var delayedRedirect = function (){
window.location = "/persists";
}
var delay = 3000;
var triggerRedirect = function() { setTimeout('delayedRedirect()', delay);
}
var stopRedirect = function (){
clearTimeout(triggerRedirect);
}
$(document).ready(function() {
triggerRedirect();
$('#btn-persist').click(function() {
stopRedirect();
});
});
I wonder why this may not be working and if there's any other way to stop the execution of the setTimeout function that has already been called so I can call it again to effectively reset the time to the original value of 'delay'.
At the same time, I don't want to stop any other JS functions that are running in parallel.
Do you see a better solution to achieve this?
The main problem why clearTimeout is not working. because you are clearing a anonymous function instead of a setTimeout variable
change this
var triggerRedirect = function() { setTimeout('delayedRedirect()', delay);
}
to this
var triggerRedirect = setTimeout('delayedRedirect()', delay);
Edit:
also change this (if you want to restart the inactive redirect trigger)
$('#btn-persist').click(function() {
stopRedirect();
});
to this
$('#btn-persist').click(function() {
stopRedirect();
triggerRedirect();
});
I have that Javascript counter:
var x=100;
function timerCountdown()
{
document.getElementById('timer1').value=x;
x--;
t=setTimeout("timerCountdown()",1000);
if (x<-1)
{
document.getElementById('timer1').value='Done!';
clearTimeout(t);
}
}
function stopCounter(){
clearTimeout(t);
x=x+1;
}
Then I use:
<body onFocus='timerCountdown()' onBlur='stopCounter()'>
But the problem is, the countdown doesn't start when the page loads. It waits for me to click on another window and to reFocus on the window again.
So I tried this:
<body onLoad='timerCountdown()' onFocus='timerCountdown()' onBlur='stopCounter()'>
But this time, the countdown goes pretty fast. Probably because timerCOuntdown is called twice every second.
Alternatively, I could just use the onFocus and onBlur in the body tag, but I need a function to trigger the Focus upon body load. Is that possible?
Does anyone have a suggestion to solve this problem?
thanks a lot!
The simple answer is because setTimeout is invoked twice, running timerCountdown() once for two times separately, and continually setting two setTimeout IDs.
This would be what you want:
var x = 100;
var t = 0;
function timerCountdown()
{
if (t == 0) t = setInterval(timerCountdown, 1000);
document.getElementById('timer1').value=x;
x--;
if (x < 0)
{
document.getElementById('timer1').value='Done!';
clearTimeout(t);
ticker = 0;
}
}
function stopCounter()
{
clearTimeout(t);
t = 0;
x++;
}
setInterval is much more suited for countdown timers, and things you need to run continually since setTimeout only runs once and you need to keep on calling it.
Edit: This fixes the initial rapid triggering of the timer on Firefox.
Remove the handler from <body onload= and add this to the end of the script block above:
t = setInterval(timerCountdown, 1000);
I'm designing a web site and I would like to be able to call a function 1 second after the last user input. I tried using onKeyUp, but it waited 1 second after the first keystroke.
Does anyone know how would this be possible?
Another similar approach, without globals:
var typewatch = function(){
var timer = 0;
return function(callback, ms){
clearTimeout (timer);
timer = setTimeout(callback, ms);
}
}();
...
<input type="text" onKeyUp="typewatch(function(){alert('Time elapsed!');}, 1000 );" />
You can this snippet here.
You can use a keyDown (or keyUp) event that sets a function to run in 1 second and if the user types another key within that second, you can clear the timeout and set a new one.
E.g.
var t;
function keyDown()
{
if ( t )
{
clearTimeout( t );
t = setTimeout( myCallback, 1000 );
}
else
{
t = setTimeout( myCallback, 1000 );
}
}
function myCallback()
{
alert("It's been 1 second since you typed something");
}
Nevermind, I found a way to do it. I call a function on each onkeyup() which increment a counter and then wait 1 second. After the 1 second elapsed, it decrement the counter and check if it's equal to 0.
var keystrokes = 0;
function askAgain()
{
++keystrokes;
setTimeout(reveal, 1000);
}
function reveal()
{
--keystrokes;
if (keystrokes == 0)
alert("Watch out, there is a snake!");
}
Just modify your html input and toss that first line into your existing functions so you're not having to recode anything you have. It will not affect any old code-calling functions either, since if onkeypress is not set then mykeypress will always be < 1.
var mykeypress=0;
var mysleep=1000; //set this higher if your users are slow typers
function mytest(id,text) {
mykeypress--; if(mykeypress > 0) { return; }
//anything you want when user stops typing here
alert("Keypress count at "+mykeypress+" ready to continue
id is "+id+" arguement is "+text);
}
input type="text" name="blah" id="55" onkeypress="mykeypress++"
onkeyup="myid=this.id;setTimeout(function (){mytest(myid,'a test')},mysleep)"
REVERT to old way seamlessly:
input type="text" name="blah" id="55" onkeyup="mytest(this.id,'a test')"
There is some simple plugin I've made that does exacly that. It requires much less code than some proposed solutions and it's very light (~0,6kb)
First you create Bid object than can be bumped anytime. Every bump will delay firing Bid callback for next given ammount of time.
var searchBid = new Bid(function(inputValue){
//your action when user will stop writing for 200ms.
yourSpecialAction(inputValue);
}, 200); //we set delay time of every bump to 200ms
When Bid object is ready, we need to bump it somehow. Let's attach bumping to keyup event.
$("input").keyup(function(){
searchBid.bump( $(this).val() ); //parameters passed to bump will be accessable in Bid callback
});
What happens here is:
Everytime user presses key, bid is 'delayed' (bumped) for next 200ms. If 200ms will pass without beeing 'bumped' again, callback will be fired.
Also, you've got 2 additional functions for stopping bid (if user pressed esc or clicked outside input for example) and for finishing and firing callback immediately (for example when user press enter key):
searchBid.stop();
searchBid.finish(valueToPass);
// Get the input box
let input = document.getElementById('my-input');
// Init a timeout variable to be used below
let timeout = null;
// Listen for keystroke events
input.addEventListener('keyup', function (e) {
// Clear the timeout if it has already been set.
// This will prevent the previous task from executing
// if it has been less than <MILLISECONDS>
clearTimeout(timeout);
// Make a new timeout set to go off in 1000ms (1 second)
timeout = setTimeout(function () {
console.log('Input Value:', input.value);
}, 1000);
});
<!-- a simple input box -->
<input type="text" id="my-input" />
Credits to:
Wait for User to Stop Typing, in JavaScript
I have a form with an <input type=text /> and I want to call a javascript function after 5 seconds of the last key press, and every time a new key is pressed, this timer should reset and only call the function after 5 seconds.
How can I do this?
I'm using jQuery.
thanks!
Something like this should get you started:
var timeout;
$('input[type=text]').keypress(function() {
if(timeout) {
clearTimeout(timeout);
timeout = null;
}
timeout = setTimeout(myFunction, 5000)
})
This answer is great, but remember that you need to enable this code after the documents loads and after the function loads to clear the timeout.
Here is the complete code:
var timeout;
$(document).ready(function(){
$('input[type=text]').keypress(function() {
if(timeout) {
clearTimeout(timeout);
timeout = null;
}
timeout = setTimeout(myFunction, 5000);
});
});
var myFunction = new function() {
alert('myFunction is running');
clearTimeout(timeout); // this way will not run infinitely
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js"></script>
Although it doesn't use jQuery, you could also have a look at the debouncing function described here.
Steve