clearTimeout doesn't clear timer, it seems too late to clear it - javascript

I'm trying to console.log the input value when the keyup event get fired, but similar to debounce implementation, from all posible events happened within a range of 1000 miliseconds I want the last one to be fired.
With clearTimeout(timer) any previous timer get cleared then a fresh timer is set. So from my view, on entering a string the logs would be like:
g
ggg
gggggg
ggggggg
Never be repeted.
let timer = null;
function withdelay() {
clearTimeout(timer);
timer = setTimeout(() => {
handleChange();
}, 1000);
}
function handleChange() {
console.log(document.getElementById("texto").value);
}
<input type="text" id="texto" onkeyup="withdelay()" />
The problem comes when the time between keyup events is close to the delay (1000 miliseconds), the logs are the same.
Why does that happen?, is there any solution based on this lines?
Here is what I think; once the callback of the timer is load from the task queue to the call stack to be executed the clearTimeout(timer) cannot take effect so the callback continue its execution.

The problem here is how keypress vs keyup fires.
keypress fires when the character is added.
keyup is fired when the key is done being pressed.
So when you release the key the value has already been written. It is not written when you release the key. So the timer picks up the change slightly before you release.
Depending on what you are actually trying to do you may want to use oninput or onkeydown

Related

onChange / onInput alternatives?

I'm creating a typing test page and running into a small issue.
I'm trying to start the timer when the user types, and using onInput works, but the problem is that it registers every time I type into the textArea which causes the starttimer function to repeat itself.
onChange works but it only works after I click outside the page which makes sense, but is not what im looking for. I need the timer to start as soon as the user starts typing. Also with onChange the stop function works, but the timer starts after i click outside of the page.
Is there a JS event that fits what i'm looking for, or is there a way to change onInput or onChange to fix the problem i'm having.
JavaScript
document.getElementById("test-area").onchange = function () {
startTimer(1);
};
Thank you.
You need to listen to the input event because as you said change is only triggered after the input loses focus which is not what you want. You also need to handle the event only once.
document.getElementById("test-area").addEventListener("input", () => {
startTimer(1);
}, {once: true});
Note that this removes the event handler after it is fired. If you need to run it again you will have to register the event one more time. Maybe in your timer callback.
Try like this, In JavaScript, using the addEventListener() method:
The addEventListener() method attaches an event handler to the specified element.
document.getElementById("test-area").addEventListener("change", function () {
if(!this.value.length){
startTimer(1);
}
}, {once : true});
Have you tried onfocus ? its not exactly when they start typing but it works. Another option would be that you use onInput and on the clocks start function change a boolian -isRunning - to true. then put a if (isRunning) return. something like:
function start() {
if(isRunning) return;
isRunning = true;
}
and then change the boolian to false when you stop onChange
Some solutions:
Variant 1
Just create a flag:
var timerStarted = false; // Was the timer started?
document.getElementById("test-area").oninput = function () {
if(timerStarted) return; // If timer was started - do nothing
startTimer(1); // Else - start the timer
timerStarted = true; // Remember what we've started the timer
};
Variant 2
(A bit shorter)
document.getElementById("test-area").addEventListener("input", function () {
startTimer(1);
}, {once: true}); // Good thing about addEventListener
// With this it will run the event only single time
More here: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener
Can you use JQuery? If so it has a method .one() which will execute the function only once. You can freely use keydown/keyup event handler then. For eg.
<script>
$(document).ready(function(){
$("#test-area").one("keydown", function() {
alert("hey");
});
});
</script>

Debounce function that fires FIRST then debounces subsequent actions

Every example of a debounce function that I've seen so far prevents an action from happening multiple times for a specified time span, and then executes the action one time when the specified time span has elapsed, then resets the timer. For example, the $mdUtil.debounce function that is included in Angular Material.
What I'm looking for is a debounce function that executes the action immediately and then prevents subsequent multiple actions from firing until the timer resets. This has the benefit of the user not having to wait until the debounce time has elapsed until their action is taken while still achieving the goal of debouncing the actions.
Has anyone seen one or had luck creating one?
Update After some more thought, the debounce function should fire the action immediately and then, if the debounced function was called again within the debounce time span, it should fire the action a second time before resetting the timer in case the second call changed any values.
edit: adding jsbin implementation
Lodash's debounce can do both. You'll have to specify whether it's leading or trailing.
https://lodash.com/docs#debounce
_.debounce(sendMail, 300, {
'leading': true,
'trailing': false
})
you can also write your own debounced function in just few lines jsbin example:
This will click first then debounce subsequent clicks.
function debounce(func, delay) {
console.log('debounce called with delay', delay);
var timer = 0;
return function debouncedFn() {
if (Date.now() - timer > delay) {
func();
}
timer = Date.now();
};
}

Using Underscore _.debounce() to trigger a single event

I have the following function that is being called from a keyPress listener on an input text field. The _.debounce() is working correctly, except instead of only firing the function 1 time after the time period, it is firing as many times as the keyPress event happened.
console.log("Pre Debounce");
var debounced = _.debounce(function() {
console.log("Field updated!");
}, 2000);
debounced();
Is there a way to limit the _.debounce function to only fire 1 time after the time period?
Possibly you are constructing the debounce function each time inside the event. If that's your case then take your debounce function outside the event response code. As far as I know the debounced function should be generated only one time and then be called multiple times.
Another thing that could go weird is when you are making an async call (ex. with an ajax autocomplete) and it takes more than your wait time for debounce then the requests could fire later making appear that debouncing is not working.
It's possible that your debounce function is taking longer to execute than the user to type. In this case, you want to make sure that you prevent double debouncing by passing in a third argument (immediate) as true.
The debounce function signature is: _.debounce(function, wait, [immediate])
So change the code to:
console.log("Pre Debounce");
var debounced = _.debounce(function() {
console.log("Field updated!");
}, 2000, true);
debounced();

On event start calling function periodically

I have this problem:
I have real time form validation, but the inputs have autocomplete on, so when you choose a value from it, it wont trigger eny event and I cant start validating the new value.
Here is some solution which I have figured
I was thinking when input triggers onfocus event, i can start periodically (for example every one second) calling some validating functions on input, whether the value changes or not.
And on blur event I want to stop calling the function.
But I cant figure it out how to start call the function periodically...
What I have so far:
$('input[name="'+input_name+'"]').focus(function(){
checkInput(input_name);
});
function checkInput(input_name){
setInterval(doneTyping(input_name),700);
console.log(1);
}
But this only happens once, how could I do it?
Thanks.
setInterval needs an anonymous function
myInterval = setInterval(function(){doneTyping(input_name);},700);
//To clear
clearTimeout(myInterval);
do it like
var intervalID = window.setInterval(function(){ doneTyping(input_name); }, 700);
see this fiddle.

How to limit AJAX requests per custom time

If I had a search box which loads suggestion on every keyup event. I think it would get worst for me server to respond on every request on every keyup event which could be about 10 times in a second or more. I mean there should be some AJAX request limiting functions that limit request(s) per sec(s) or any specific time. Can you tell me through coding examples on how to limit ajax request ?
Second thing, I would like to share that if we use JSP to limit AJAX request(s). Would it be a good idea being JSP a client side language and a malicious user could easily remove those ajax-request-limiting function(s) and doom the server.
OR ! Is there is any remedy for that ?
Thanks for reading.
You can write a debounce method by your self:
var timer = null;
searchInput.addEventListener('keyup', function (evt) {
clearTimeout(timer);
timer = setTimeout(function () {
// this event listener will postpone its execution until after 1 second have elapsed since the last time it was invoked
// send your ajax request here
}, 1000);
}, false);
Or you can use underscore to limit keyup event:
searchInput.addEventListener('keyup', _.debounce(function (evt) {
// this event listener will postpone its execution until after 1 second have elapsed since the last time it was invoked
}, 1000), false);
You need to set up a timer that will reset everytime an key event occurs. if no key was pressed until the timer is out , then send your ajax request.

Categories