Debounce for 500ms if True else execute - javascript

Spoiler; I'm completely new to jQuery/Javascript.
I have a boolean field CheckMe and an input field textField.
If textField is empty, CheckMe should not be shown else it should (this means if goes from not-empty to empty CheckMe should be hidden right away again). I want to parse a delay, say 500 ms, i.e CheckMe is shown if text is not empty and after 500 ms of the last keypress
I have tried using the debounce function from this SO answer (see my implementation below), but the problem is, CheckMe is also first hidden after 500 ms of textField being empty
<script type="text/javascript">
function debounce(fn, duration) {
var timer;
return function(){
clearTimeout(timer);
timer = setTimeout(fn, duration);
}
}
$(document).ready(function () {
const textField= $("#textField");
const CheckMe= $("#CheckMe");
CheckMe.hide();
textField.on("input", debounce(()=> {
if (textField.val()) {
CheckMe.show();
} else {
CheckMe.hide();
}
},500));
});
</script>
but that removes checkMe after 500 ms when I clear textField.
I have tried moving the debounce into the True statement i.e
...
textField.on("input", function() {
if (textField.val()) {
debounce(function(){CheckMe.show();},500)
} else {
CheckMe.hide();
}
}
but that does not show CheckMe at any point

The reason the attempt with if () { debouce(()={}); } else { immediate(); } doesn't work is due to how event handlers store the function and how debounce stores its timer.
When you run .on("input", function() { }) that function definition is stored within the event handler, ready to run. However with debounce, that's not what is being done, instead it's:
.on("input", function())
there's no function definition - it calls the function itself, which happens to return another function to be called when the event runs.
It's why there there are so many questions on SO saying something like "my code runs immediately when I do .on("input", myfunction()) when it should be .on("input", myfunction)
So that one function (the debounce) runs once per event setup - not once per input event fire, but just once when setting up the event. So there's only one instance of var timer and it's all contained within the debounce function. The event fire then calls the code inside the return function() which already has var timer defined previously in its outer scope (the previous debounce call).
If you call debounce again with a 2nd input $("#secondInp").on("input", debounce(() => ... you get a second instance of the function with its own variable (so they don't conflict between inp1 and inp2).
So you can then see that if you put this inside the event handler (in the if), you're calling a new debounce each time (not the same one).
Your attempt did nothing because your code debounce(function(){CheckMe.show();},500) simply returns the function - so you would need to do
debounce(function(){CheckMe.show();},500)();`
but even that won't work as each time it's called (each event) you get a new instance of the debounce function and a new instance of var timer.
You can use two events. Inside each event check if the "check me" should be shown or not.
The debounced one will run after 500ms and the not-debounced one will run immediately.
function debounce(fn, duration) {
var timer;
return function() {
clearTimeout(timer);
timer = setTimeout(fn, duration);
}
}
$(document).ready(function() {
const textField = $("#textField");
const checkMe = $("#checkMe");
checkMe.hide();
textField.on("input", debounce(() => {
if (textField.val()) {
checkMe.show();
}
}, 500));
textField.on("input", () => {
if (!textField.val()) {
checkMe.hide();
}
});
});
#checkMe { color: red; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input id="textField" type="text">
<div id='checkMe'>^-- check this</div>
is it possible to keep it in a single if-else
Given the explanation above, it is not possible using your debounce function as it is; because of the return function() { } and the single instance of timer. The key being the single instance of timer.
Without the debounce, this can be implemented in a single function, using an outer variable for the timer, but will need the debounce code repeated each time (eg for a 2nd input) - just the clearTimeout and setTimeout code - so not much - it's the "global"/outer-scope variable that becomes an issue.
$(document).ready(function() {
var textFieldTimer = null;
const textField = $("#textField");
const checkMe = $("#checkMe");
checkMe.hide();
textField.on("input", () => {
if (textField.val()) {
clearTimeout(textFieldTimer);
textFieldTimer = setTimeout(() => checkMe.show(), 500);
}
else {
checkMe.hide();
}
});
});
#checkMe { color: red; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input id="textField" type="text">
<div id='checkMe'>^-- check this</div>
So how can we use both: a reusable function and inside an if inside the event handler?
By storing the single instance of timer on the element itself - using .data() in the code below, but any method to store a single instance per element will also work.
Here's an example, using a single event with an if and repeated for a second input to show how it might work.
function debounce2(fn, duration)
{
var timer = $(this).data("timer");
clearTimeout(timer);
$(this).data("timer", setTimeout(fn, duration));
}
$(document).ready(function() {
$("#checkMe, #checkMe2").hide();
$("#textField, #textField2").on("input", function() {
if ($(this).val()) {
debounce2.call(textField, () => $("#checkMe").show(), 500);
}
else {
$("#checkMe").hide();
}
});
// second input confirming `timer` is per element.
$("#textField2").on("input", function() {
if ($(this).val()) {
debounce2.call(textField, () => $("#checkMe2").show(), 500);
}
else {
$("#checkMe2").hide();
}
});
});
#checkMe, #checkMe2 { color: red; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input id="textField" type="text">
<div id='checkMe'>^-- check this</div>
<hr/>
<input id="textField2" type="text">
<div id='checkMe2'>^-- check this</div>

Related

vBulletin - onKeyUp Pause when creating a new thread

I am attempting to integrate my keyup function into vBulletin 3 when a user is creating a new thread. The ID of the text area is a variable, but in HTML returns:
<textarea name="message" id="vB_Editor_001_textarea"></textarea>
I have tried replacing the ID in my function with the variable for the ID this also had no effect. my jQuery function is as follows:
function delay(fn, ms) {
let timer = 0
return function() {
clearTimeout(timer)
timer = setTimeout(fn.bind(this), ms || 0)
}
}
$('#vB_Editor_001_textarea').keyup(delay(function (e) {
console.log('Time elapsed!', this.value);
}, 1000));
I have a live working example here.
Within vBulletin the textarea is contained within the editor_toolbar_on template, its raw code looks like:
<textarea name="message" id="{$editorid}_textarea" rows="10" cols="60" style="display:block; width:$stylevar[messagewidth]; height:{$editor_height}px" tabindex="1" dir="$stylevar[textdirection]">$newpost[message]</textarea>
I have attempted placing my script in my footer template (along with jQuery above it). This failed so I went straight to the textarea and placed the script right below the textarea, which also had no effect.
After those unsuccessful attempts I went ahead and placed the entire code right into the template (creating another textarea) upon getting an error for duplicate IDs I numbered this one 2:
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script>
function delay(fn, ms) {
let timer = 0
return function() {
clearTimeout(timer)
timer = setTimeout(fn.bind(this), ms || 0)
}
}
$('#vB_Editor_002_textarea').keyup(delay(function (e) {
console.log('Time elapsed!', this.value);
}, 1000));
</script>
I am not returning any console errors, yet the log never posts.
How can I receive the keypress event from vBulletin's textarea?
As far as I can tell, there appears to be an issue with jQuery somewhere within vBulletin. This pure JS version provides the same functionality as the original post and works: (I would like to hear from others regarding the jQuery method though)
function debounce(fn, duration) {
var timer;
return function(){
clearTimeout(timer);
timer = setTimeout(fn, duration);
}
}
const txt = document.querySelector('#vB_Editor_001_textarea')
const out = document.querySelector('#out')
const status = document.querySelector('#status')
const onReady = () => {
txt.addEventListener('keydown', () => {
out.classList.remove('idle')
out.classList.add('typing')
status.textContent = 'typing...'
})
txt.addEventListener('keyup', debounce(() => {
out.classList.remove('typing')
out.classList.add('idle')
status.textContent = 'idle...'
}, 2000))
}
document.addEventListener('DOMContentLoaded', onReady)

Method runs multiple times [duplicate]

I want to trigger an ajax request when the user has finished typing in a text box. I don't want it to run the function on every time the user types a letter because that would result in A LOT of ajax requests, however I don't want them to have to hit the enter button either.
Is there a way so I can detect when the user has finished typing and then do the ajax request?
Using jQuery here!
So, I'm going to guess finish typing means you just stop for a while, say 5 seconds. So with that in mind, let's start a timer when the user releases a key and clear it when they press one. I decided the input in question will be #myInput.
Making a few assumptions...
//setup before functions
var typingTimer; //timer identifier
var doneTypingInterval = 5000; //time in ms, 5 seconds for example
var $input = $('#myInput');
//on keyup, start the countdown
$input.on('keyup', function () {
clearTimeout(typingTimer);
typingTimer = setTimeout(doneTyping, doneTypingInterval);
});
//on keydown, clear the countdown
$input.on('keydown', function () {
clearTimeout(typingTimer);
});
//user is "finished typing," do something
function doneTyping () {
//do something
}
The chosen answer above does not work.
Because typingTimer is occassionaly set multiple times (keyup pressed twice before keydown is triggered for fast typers etc.) then it doesn't clear properly.
The solution below solves this problem and will call X seconds after finished as the OP requested. It also no longer requires the redundant keydown function. I have also added a check so that your function call won't happen if your input is empty.
//setup before functions
var typingTimer; //timer identifier
var doneTypingInterval = 5000; //time in ms (5 seconds)
//on keyup, start the countdown
$('#myInput').keyup(function(){
clearTimeout(typingTimer);
if ($('#myInput').val()) {
typingTimer = setTimeout(doneTyping, doneTypingInterval);
}
});
//user is "finished typing," do something
function doneTyping () {
//do something
}
And the same code in vanilla JavaScript solution:
//setup before functions
let typingTimer; //timer identifier
let doneTypingInterval = 5000; //time in ms (5 seconds)
let myInput = document.getElementById('myInput');
//on keyup, start the countdown
myInput.addEventListener('keyup', () => {
clearTimeout(typingTimer);
if (myInput.value) {
typingTimer = setTimeout(doneTyping, doneTypingInterval);
}
});
//user is "finished typing," do something
function doneTyping () {
//do something
}
This solution does use ES6 but it's not necessary here. Just replace let with var and the arrow function with a regular function.
It's just one line with underscore.js debounce function:
$('#my-input-box').keyup(_.debounce(doSomething , 500));
This basically says doSomething 500 milliseconds after I stop typing.
For more info: http://underscorejs.org/#debounce
Late answer but I'm adding it because it's 2019 and this is entirely achievable using pretty ES6, no third party libraries, and I find most of the highly rated answers are bulky and weighed down with too many variables.
Elegant solution taken from this excellent blog post.
function debounce(callback, wait) {
let timeout;
return (...args) => {
clearTimeout(timeout);
timeout = setTimeout(function () { callback.apply(this, args); }, wait);
};
}
window.addEventListener('keyup', debounce( () => {
// code you would like to run 1000ms after the keyup event has stopped firing
// further keyup events reset the timer, as expected
}, 1000))
Yes, you can set a timeout of say 2 seconds on each and every key up event which will fire an ajax request. You can also store the XHR method and abort it on subsequent key press events so that you save bandwith even more. Here's something I've written for an autocomplete script of mine.
var timer;
var x;
$(".some-input").keyup(function () {
if (x) { x.abort() } // If there is an existing XHR, abort it.
clearTimeout(timer); // Clear the timer so we don't end up with dupes.
timer = setTimeout(function() { // assign timer a new timeout
x = $.getJSON(...); // run ajax request and store in x variable (so we can cancel)
}, 2000); // 2000ms delay, tweak for faster/slower
});
Hope this helps,
Marko
var timer;
var timeout = 1000;
$('#in').keyup(function(){
clearTimeout(timer);
if ($('#in').val) {
timer = setTimeout(function(){
//do stuff here e.g ajax call etc....
var v = $("#in").val();
$("#out").html(v);
}, timeout);
}
});
full example here: http://jsfiddle.net/ZYXp4/8/
Both top 2 answers doesn't work for me. So, here is my solution:
var timeout = null;
$('#myInput').keyup(function() {
clearTimeout(timeout);
timeout = setTimeout(function() {
//do stuff here
}, 500);
});
Declare the following delay function:
var delay = (function () {
var timer = 0;
return function (callback, ms) {
clearTimeout(timer);
timer = setTimeout(callback, ms);
};
})()
and then use it:
let $filter = $('#item-filter');
$filter.on('keydown', function () {
delay(function () {
console.log('this will hit, once user has not typed for 1 second');
}, 1000);
});
Modifying the accepted answer to handle additional cases such as paste:
//setup before functions
var typingTimer; //timer identifier
var doneTypingInterval = 2000; //time in ms, 2 second for example
var $input = $('#myInput');
// updated events
$input.on('input propertychange paste', function () {
clearTimeout(typingTimer);
typingTimer = setTimeout(doneTyping, doneTypingInterval);
});
//user is "finished typing," do something
function doneTyping () {
//do something
}
I like Surreal Dream's answer but I found that my "doneTyping" function would fire for every keypress, i.e. if you type "Hello" really quickly; instead of firing just once when you stop typing, the function would fire 5 times.
The problem was that the javascript setTimeout function doesn't appear to overwrite or kill the any old timeouts that have been set, but if you do it yourself it works! So I just added a clearTimeout call just before the setTimeout if the typingTimer is set. See below:
//setup before functions
var typingTimer; //timer identifier
var doneTypingInterval = 5000; //time in ms, 5 second for example
//on keyup, start the countdown
$('#myInput').on("keyup", function(){
if (typingTimer) clearTimeout(typingTimer); // Clear if already set
typingTimer = setTimeout(doneTyping, doneTypingInterval);
});
//on keydown, clear the countdown
$('#myInput').on("keydown", function(){
clearTimeout(typingTimer);
});
//user is "finished typing," do something
function doneTyping () {
//do something
}
N.B. I would have liked to have just added this as a comment to Surreal Dream's answer but I'm a new user and don't have enough reputation. Sorry!
I don't think keyDown event is necessary in this case (please tell me why if I'm wrong). In my (non-jquery) script similar solution looks like that:
var _timer, _timeOut = 2000;
function _onKeyUp(e) {
clearTimeout(_timer);
if (e.keyCode == 13) { // close on ENTER key
_onCloseClick();
} else { // send xhr requests
_timer = window.setTimeout(function() {
_onInputChange();
}, _timeOut)
}
}
It's my first reply on Stack Overflow, so I hope this helps someone, someday:)
const inText = document.getElementById('inText')
const outText = document.getElementById('outText')
const delay = 1000
let timer
inText.addEventListener('input', code => {
clearTimeout(timer);
timer = setTimeout(x => {
outText.innerHTML = inText.value
}, delay, code)
})
<textarea id='inText'>edit this and...</textarea>
<pre id='outText'>see the results after you stop typing for one second</pre>
Well, strictly speaking no, as the computer cannot guess when the user has finished typing. You could of course fire a timer on key up, and reset it on every subsequent key up. If the timer expires, the user hasn't typed for the timer duration - you could call that "finished typing".
If you expect users to make pauses while typing, there's no way to know when they are done.
(Unless of course you can tell from the data when they are done)
agree with the #going 's answer. Another similar solution that worked for me is the one below. The only difference is that I am using .on("input"...) instead of keyup. This only captures changes in the input. other keys like Ctrl, Shift etc. are ignored
var typingTimer; //timer identifier
var doneTypingInterval = 5000; //time in ms (5 seconds)
//on input change, start the countdown
$('#myInput').on("input", function() {
clearTimeout(typingTimer);
typingTimer = setTimeout(function(){
// doSomething...
}, doneTypingInterval);
});
I was implementing the search at my listing and needed it to be ajax based. That means that on every key change, searched results should be updated and displayed. This results in so many ajax calls sent to server, which is not a good thing.
After some working, I made an approach to ping the server when the user stops typing.
This solution worked for me:
$(document).ready(function() {
$('#yourtextfield').keyup(function() {
s = $('#yourtextfield').val();
setTimeout(function() {
if($('#yourtextfield').val() == s){ // Check the value searched is the latest one or not. This will help in making the ajax call work when client stops writing.
$.ajax({
type: "POST",
url: "yoururl",
data: 'search=' + s,
cache: false,
beforeSend: function() {
// loading image
},
success: function(data) {
// Your response will come here
}
})
}
}, 1000); // 1 sec delay to check.
}); // End of keyup function
}); // End of document.ready
You will notice that there is no need to use any timer while implementing this.
I feel like the solution is somewhat a bit simpler with the input event:
var typingTimer;
var doneTypingInterval = 500;
$("#myInput").on("input", function () {
window.clearTimeout(typingTimer);
typingTimer = window.setTimeout(doneTyping, doneTypingInterval);
});
function doneTyping () {
// code here
}
I just figured out a simple code to wait for user to finish typing:
step 1.set time out to null then clear the current timeout when the user is typing.
step 2.trigger clear timeout to the variable define before keyup event is triggered.
step 3.define timeout to the variable declared above;
<input type="text" id="input" placeholder="please type" style="padding-left:20px;"/>
<div class="data"></div>
javascript code
var textInput = document.getElementById('input');
var textdata = document.querySelector('.data');
// Init a timeout variable to be used below
var timefired = null;
// Listen for keystroke events
// Init a timeout variable to be used below
var timefired = null;// Listen for keystroke events
textInput.onkeyup = function (event) {
clearTimeout(timefired);
timefired = setTimeout(function () {
textdata.innerHTML = 'Input Value:'+ textInput.value;
}, 600);
};
This is the a simple JS code I wrote:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="pt-br" lang="pt-br">
<head><title>Submit after typing finished</title>
<script language="javascript" type="text/javascript">
function DelayedSubmission() {
var date = new Date();
initial_time = date.getTime();
if (typeof setInverval_Variable == 'undefined') {
setInverval_Variable = setInterval(DelayedSubmission_Check, 50);
}
}
function DelayedSubmission_Check() {
var date = new Date();
check_time = date.getTime();
var limit_ms=check_time-initial_time;
if (limit_ms > 800) { //Change value in milliseconds
alert("insert your function"); //Insert your function
clearInterval(setInverval_Variable);
delete setInverval_Variable;
}
}
</script>
</head>
<body>
<input type="search" onkeyup="DelayedSubmission()" id="field_id" style="WIDTH: 100px; HEIGHT: 25px;" />
</body>
</html>
Why not just use onfocusout?
https://www.w3schools.com/jsreF/event_onfocusout.asp
If it's a form, they will always leave focus of every input field in order to click the submit button so you know no input will miss out on getting its onfocusout event handler called.
Multiple timers per page
All the other answers only work for one control (my other answer included).
If you have multiple controls per page (e.g. in a shopping cart) only the last control where the user typed something will get called. In my case this is certainly not the wished behaviour - each control should have its own timer.
To solve this, you simply have to pass an ID to the function and maintain a timeoutHandles dictionary as in the following code:
Function Declaration:
var delayUserInput = (function () {
var timeoutHandles = {};
return function (id, callback, ms) {
if (timeoutHandles[id]) {
clearTimeout(timeoutHandles[id]);
}
timeoutHandles[id] = setTimeout(callback, ms);
};
})();
Function Usage:
delayUserInput('yourID', function () {
//do some stuff
}, 1000);
Here is a solution that fires after 1 second of not typing, but also fires instantly when the input is blank. This is useful when clearing search results after the user deletes the input query. This solution also supports copying and pasting into the search box. The $(() => { ... }); wrapping the top portion of code simply means "do this when the page is loaded" in simple Jquery terms.
var searchTimer;
var searchInterval = 1000;
$(() => {
$('#search-box').on('input', (event) => {
clearTimeout(searchTimer);
searchTimer = setTimeout(() => {
searchContacts(event.target.value);
}, (event.target.value.length > 0) ? searchInterval : 0);
});
});
function searchContacts(val) {
console.log('searching: ' + val);
}
You can use the onblur event to detect when the textbox loses focus:
https://developer.mozilla.org/en/DOM/element.onblur
That's not the same as "stops typing", if you care about the case where the user types a bunch of stuff and then sits there with the textbox still focused.
For that I would suggest tying a setTimeout to the onclick event, and assuming that after x amount of time with no keystrokes, the user has stopped typing.
If there is necessity for the user to move away from the field, we can use "onBlur" instead of Onchange in Javascript
<TextField id="outlined-basic" variant="outlined" defaultValue={CardValue} onBlur={cardTitleFn} />
If that is not necessary setting timer would be the good option.
for alpine.js users <input #input.debounce.500ms="fn()">
Once you detect focus on the text box, on key up do a timeout check, and reset it each time it's triggered.
When the timeout completes, do your ajax request.
If you are looking for a specific length (such as a zipcode field):
$("input").live("keyup", function( event ){
if(this.value.length == this.getAttribute('maxlength')) {
//make ajax request here after.
}
});
Not sure if my needs are just kind of weird, but I needed something similar to this and this is what I ended up using:
$('input.update').bind('sync', function() {
clearTimeout($(this).data('timer'));
$.post($(this).attr('data-url'), {value: $(this).val()}, function(x) {
if(x.success != true) {
triggerError(x.message);
}
}, 'json');
}).keyup(function() {
clearTimeout($(this).data('timer'));
var val = $.trim($(this).val());
if(val) {
var $this = $(this);
var timer = setTimeout(function() {
$this.trigger('sync');
}, 2000);
$(this).data('timer', timer);
}
}).blur(function() {
clearTimeout($(this).data('timer'));
$(this).trigger('sync');
});
Which allows me to have elements like this in my application:
<input type="text" data-url="/controller/action/" class="update">
Which get updated when the user is "done typing" (no action for 2 seconds) or goes to another field (blurs out of the element)
If you need wait until user is finished with typing use simple this:
$(document).on('change','#PageSize', function () {
//Do something after new value in #PageSize
});
Complete Example with ajax call - this working for my pager - count of item per list:
$(document).ready(function () {
$(document).on('change','#PageSize', function (e) {
e.preventDefault();
var page = 1;
var pagesize = $("#PageSize").val();
var q = $("#q").val();
$.ajax({
url: '#Url.Action("IndexAjax", "Materials", new { Area = "TenantManage" })',
data: { q: q, pagesize: pagesize, page: page },
type: 'post',
datatype: "json",
success: function (data) {
$('#tablecontainer').html(data);
// toastr.success('Pager has been changed', "Success!");
},
error: function (jqXHR, exception) {
ShowErrorMessage(jqXHR, exception);
}
});
});
});
Simple and easy to understand.
var mySearchTimeout;
$('#ctl00_mainContent_CaseSearch').keyup(function () {
clearTimeout(mySearchTimeout);
var filter = $(this).val();
mySearchTimeout = setTimeout(function () { myAjaxCall(filter); }, 700);
return true;
});
For passing parameters to your function along with ES6 syntax.
$(document).ready(() => {
let timer = null;
$('.classSelector').keydown(() => {
clearTimeout(timer);
timer = setTimeout(() => foo('params'), 500);
});
});
const foo = (params) => {
console.log(`In foo ${params}`);
}

As user types get the input value but only once every second (as in google's search bar)

I have this function that gets the input value every second but only when user has stopped typing.
var timeout;
jQuery('#icName').keypress(function () {
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
timeout = setTimeout(makeSearch, 500);
});
var makeSearch = function () {
console.log("value", document.getElementById('icName').value)
}
How to get the value once every second even if user is still typing?
This isn't very hard; you just restructure the code so that the timer gets kicked off if there isn't already a timer, rather than destroying the old timer every keystroke. Here's your same code, revised to do it:
var timeout;
jQuery('#icName').keydown(function () {
if (!timeout) {
timeout = setTimeout(function() {
timeout = null;
makeSearch();
}, 500);
}
});
var makeSearch = function () {
console.log("value", document.getElementById('icName').value)
};
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<input id="icName" />
This also uses keydown and not keypress, which will capture all keystrokes.

Not using correctly javascript setTimeout / clearTimeout?

I have problem with simple javascript and some ajax.
I have link that calls javascript function like this:
<div id="Button11" onmouseover="changeContent4()">Actions</div>
Javascript function that is called above is like this:
function changeContent4()
{
BubbleOn()
document.getElementById("text1").innerHTML='Some text here';
clearTimeout(BOffi);
var BOffi = setTimeout(BubbleOff, 20000);
}
This works, it runs BubbleOn function, places text to element text1, most likely it empties BOffi timeout and sets new timeout 20000ms for it.
Here is BubbleOn:
function BubbleOn() {
$("#bubble").fadeIn(function() {
})
}
And here is BubbleOff:
function BubbleOff() {
$("#bubble").fadeOut(function() {
})
}
As in functions BubbleOn and BubbleOff works. They just hide or show div named bubble which contains text1 element. When BOffi goes timeout it just runs the BubbleOff function. This works fine. The problem is that when BubbleOff has been run and mouse is placed immediately over link that runs changeContent4(), It does make the bubble div visible again and places text there again but then bubble div fades out inside a second! Not after 20000ms. After this if the mouse is placed again to run changeContent4() everything works great. If there is about millisecond longer time than a second between the bubble fadeout and placing the mouse over changeContent4() launcher it works and waits 20000ms. Less than a second and bubble is shown about second...
What can cause this? Could it be that fadeOut is still running even the bubble is vanished from the screen and therefore it does not reset the BOffi counter? Which could have 1 second or less time left and then runs BubbleOff again after that magical second?
Two ideas to try:
put "clearTimeout(BOffi);" at the top of the function before "BubbleOn();".
declare BOffi as a global variable.
So:
var BOffi;
function changeContent4()
{
clearTimeout(BOffi);
BubbleOn();
document.getElementById("text1").innerHTML='Some text here';
BOffi = setTimeout(BubbleOff, 20000);
}
or you can use window.BOffi instead.
At a first glance I notice this var BOffi = setTimeout(BubbleOff, 20000);. This creates a local variable. After the function is executed, is lost. Second time function is called Boffi is some random residual value.
Make it global and you should be ok (remove var).
function BubbleOn() {
$("#bubble").fadeIn(function(){},1000)
}
function BubbleOff() {
$("#bubble").fadeOut(function() {},1000)
}
You should set duration for both fadeIn and fadeOut functions.
Because the animations are queued but your script keep running, try this one :
function changeContent4()
{
bubble(function(){
document.getElementById("text1").innerHTML='Some text here';
});
}
var fadeTimeout = null;
function bubble(callback) {
if(fadeTimeout==null)
$("#bubble").fadeIn(1000, function(){
if($.isFunction(callback))
callback();
fadeTimeout = setTimeout(bubbleOff, 20000);
});
}
function bubbleOff() {
$("#bubble").fadeOut(1000, function(){
fadeTimeout =null;
});
}
Fiddle here
You might wanna move the callback() call before the fadeIn as you are modifying the text inside the bubble but that exemple is just to let you see the order of every changes
EDIT : now allowing multiple call
var i = 0;
function changeContent4() {
bubble(function () {
$("#text1").text('Some text here ' + (i++));
});
}
var fadeTimeout = null;
function bubble(callback) {
if ($.isFunction(callback)) callback();
if (fadeTimeout == null) {
$("#bubble").fadeIn(1000, function () {
fadeTimeout = setTimeout(bubbleOff, 20000);
});
} else {
clearTimeout(fadeTimeout);
fadeTimeout = setTimeout(bubbleOff, 20000);
}
}
function bubbleOff() {
$("#bubble").fadeOut(1000, function () {
fadeTimeout = null;
});
}
FIDDLE

javascript/jquery - add debounce to a button

I want to add a debounce to a button, but i want to perform some actions each time user clicks button, but only after 5 second after user hits button, then perform SQL update. Usually the throttle seems to be applied directly to the listener. Here I want some actions performed each time the button is clicked, and then an update after a reasonable waiting period.
I am not sure how to use the function in this case...
reference: http://code.google.com/p/jquery-debounce/
$('#myButton').click(function() {
// do a date calculation
// show user changes to screen
// wait until user has has stopped clicking the
// button for 5 seconds, then update file with "process" function.
});
function process(){
// update database table
}
debounce syntax
$('input').bind('keyup blur', $.debounce(process, 5000));
You could still use $.debounce like so:
// create new scope
(function() {
// create debounced function
var dprocess = $.debounce(process, 5000);
// bind event handler
$('#myButton').click(function() {
// do a date calculation
// show user changes to screen
// call the function
dprocess();
});
}());
Alternative without $.debounce (you can always debounce your code this way, without jQuery):
// create new scope
(function() {
var timer;
// bind event handler
$('#myButton').click(function() {
if(timer) {
clearTimeout(timer);
}
// do a date calculation
// show user changes to screen
// call the function
timer = setTimeout(process, 5000);
});
}());
Debounce using native/vanilla JS and jquery/underscore.js.
Example
JS
//Native/Vanilla JS
document.getElementById('dvClickMe').onclick = debounce(function(){
alert('clicked - native debounce');
}, 250);
function debounce(fun, mil){
var timer;
return function(){
clearTimeout(timer);
timer = setTimeout(function(){
fun();
}, mil);
};
}
//jQuery/Underscore.js
$('#dvClickMe2').click(_.debounce(function(){
alert('clicked - framework debounce');
}, 250));
HTML
<div id='dvClickMe'>Click me fast! Native</div>
<div id='dvClickMe2'>Click me fast! jQuery + Underscore</div>
var timer;
$('#myButton').click(function() {
//Called every time #myButton is clicked
if(timer) clearTimeout(timer);
timer = setTimeout(process, 5000);
});
function process(){
//Called 5000ms after #myButton was last clicked
}
Using a global variable might not be the best solution if the debounce function is used to debounce multiple functions. For that we should scope the timer to the function being denounced.
function debounce(func, timeout = 2000) {
if (func.timer) clearTimeout(func.timer);
func.timer = setTimeout(func, timeout);
}
debounce(func1, 4000);
debounce(func2, 1000);
NOTE: this won't work with an anonymous function;
Why not just use setTimeOut(function() { process(); }, 5000);

Categories