I have a input textbox for people to enter a username, it has a onkeyup event attribute attached which fires off an AJAX request to check if the username is taken.
What is the delay between the first key to the second ? Meaning if I type a 5 letter word one letter after the other. Does the AJAX fire 5 times ?
If there is no delay, would this be "computationally expensive" since there would so many database queries ? Or would there be no noticeable difference ?
If there is a difference in performance, what methods could I take using Javascript to check if the user is actually "done" typing. I still want it to automatically check after typing. Thus ruling out onblur attributes etc.
My Code: http://pastebin.com/hXfgk7nL
(code indentation wasn't working for me on stack overflow)
Yes it will fire EVERYTIME you type a character and you're not going to like that in terms of your page performance. You can implement delays on executing the call back if you like ie. it will not be executed until the user stopped typing. Here's an example:
$(document).ready(function(){
$('#txtboxid').keyup(function(){
clearTimeout(timer);
timer = setTimeout(function(){
//call your function here
}, 250) // you can change the 250 here to a higher number if you want the delay to be longer. In this case, 250 means a quarter of a second.
});});
Following are my replies/suggestion for your queries:
Yes , it will fire 5 fives because 5 times key-up event will be triggered.
it will be a performance issue, resulting in slow response from server,also since your making multiple ajax request sequentially, the response from each request may not be sequential.your code will suffer from logic issue.
instead of making ajax call for key-up, you can use it for blur event.
Suggestion: before making a ajax call validate the field for basic errors like blank string, numbers etc.,(depending on your requirement).
Yes it will fire multiple times, one for each keystroke. You are triggering multiple AJAX calls, so you are wasting network and server resources.
Additionally, you are not guaranteed on the order in which the calls will return, so if for some network reason, the first call issued return last, it will overwrite the results of the most recent request.
To tackle the problem, you are looking for something like this plugin: https://github.com/cowboy/jquery-throttle-debounce.
From the plugin usage example:
function ajax_lookup( event ) {
// Perform an AJAX lookup on $(this).val();
};
// Console logging happens on keyup, for every single key
// pressed, which is WAAAY more often than you want it to.
$('input:text').keyup( ajax_lookup );
// Console logging happens on window keyup, but only after
// the user has stopped typing for 250ms.
$('input:text').keyup( $.debounce( 250, ajax_lookup ) );
Note that despite the name the plugin can also be used stand alone, without jquery.
Yes, it will fire every key-up event. You can reduce the performance hit using following approach.
I am sure you must have a minimum character length for username. Wait until user type that number of characters to query the database.
You could always bring all the usernames starting with user typed username and process in local memory. This may not be real time but would reduce the number database queries. Also it would depend on the size of your current user list.
Double check the existence of the username before saving it to the database.
yes it will fire. if you don't need that means, you have to check conditions with a flag. check the given value with the previous value which is stored in a variable.
You can try to check if your input is modified in 500 ms. Is is not, then make only one ajax request. If the input is modified again than you must repeat the process again.
I suggest to use setInterval for that.
Related
I'm creating an input field where some data should be fetched (by AJAX) and displayed every time the user enters data into that field. However, if the user types several characters, say he types "test" to get all records who contain the string "test", it would not be necessary to do 4 queries after every character pressed, but one query after he stopped typing. I can think of several solutions with a global variable where I check if the same event has been fired again, but is there a really elegant way to do this? Maybe check if there is something in the keyboard buffer and only proceed if it is empty? Or is there an event that is only fired once the keyboard buffer is empty and all characters are in the input field?
The elegant way is to use a timeout, and to keep clearing the previous timeout with each key press
var tID;
function keyUp (e) {
if (tID) clearTimeout(tID);
tID = setTimeout(function() {
... // make web call
}, 2000);
}
This will ensure that the web call is only called after the last key is pressed (you may want to adjust the timeout value)
There are ways to achieve this that I can think of:
Use timeout, from the last keyup event. This is not always the best and not that precise with users that have low typing speed.
Use space character do regconize if the user has finished typing a word. Based on changes in length and total word count, you can decide if you would want to send AJAX or not.
Depends on the type of input you are working with, you may choose the most suitable method for you. The first one is somewhat quite rigid. The second method requires user to press space every time he finishs typing. A little bit of both could be a sweet spot perhaps. In modern day, I don't think sending request every keyup will cause huge performance effect though.
Is there any combination of rx operators so as to get the first and last debounced event?
This is going to be used in master detail scenarios (or even seach scenarios) where we want immediate loading of first selected item and the last after user stops changing selection.
This would prevent the debounce time to be injected when the user navigates slowly but also prevent bursts of changes.
If debounce operator had an option "immediate" like underscore.js debounce functoin then a merge of the 2 versions of debounce operator would generate the needed result.
To get the the first debounced element, you can use throttle. To get the last, you can use debounce. You however should make sure that the source you are debouncing or throttling is a hot source as you will apply both operators to the same source. Then if you want both elements in the same observable, then you can just merge the throttled and debounced streams.
For example, calling source$ your source observable:
firstAndLast$ = Rx.Observable.merge(source$.debounce(Xms), source$.throttle(Xms))
This should issue the first item and the last item of a burst in the same stream. Note that in the edge case that this is not a burst but a single value that is occuring within the Xms time period, you might have twice the same value, one at the beginning of the period, and one at the end. A way to protect against that, if that makes sense is to use distinctUntilChanged, so this becomes:
firstAndLast$ = Rx.Observable.merge(source$.debounce(Xms), source$.throttle(Xms)).distinctUntilChanged()
Just combining the throttle and debounce as suggested in the previous answer was not enough for me because it periodically emits new events when the throttle time runs out.
const debounced$ = source$.debounceTime(time)
return source$.throttleTime(time)
.merge(debounced$).distinctUntilChanged()
With a time=30 you would get:
-a-a-a-a-a-a----
-a---a---a----a-
To only get the first and last event with a minimum time in between I used:
const debounced$ = source$.debounceTime(time)
return source$.throttle(() => debounced)
.merge(debounced$).distinctUntilChanged()
Which results in:
-a-a-a-a-a-a----
-a------------a-
A hybrid operator with UX-friendly behavior: https://www.npmjs.com/package/rx-op-debounce-throttle
Emits first and last items in the batch, plus as much as possible events within the window, separated by at least X ms.
I want to implement an auto completion function on my text input, using jQuery by monitoring the change event and query my database on every change.
I know there are some autocompletion plugins but I'm doing something like fuzzy search, so keyword 'ad' may result in words like 'abort' or 'and'. Correct me if I'm wrong, I found most plugins do exact search.
However, I don't want to do database query too frequently, that is, I want to do query only when the text input is updated but haven't been updated for a small amount of time like 200ms. So that I can ignore the constantly high speed inputs by users.
Can anyone tell me how to do that?
You can use something like that to make queries.
var timeouted=null;
$(e).on('change',function(){
clearTimeout(timeouted);
timeouted = setTimeout(function(){
//database query
}, 200)
})
Use setTimeout() stored as a variable, then on input clear the timeout and reset. If elapsed time is >= desired timeout, make DB query.
Working example here: https://jsfiddle.net/c2s1sj6f/7/
I've got some issues with javascript. Which causes some problems.
I'm using DevExpress MVC GridView, ASP.Net MVC 3 and javascript.
This my problem:
I've got a gridview, with for example customers.
I want them to select the customers, and show them in a table generated by javascript so we dont get all those refreshes. And they can then add other information so that they can be saved again to another table, but thats not really important.
I perform some calculations before generating the table row from the selected customer. Another problem is, the devexpress gridview has an event that calls on each selection change instead of a nice ~100 ms wait so that the user can multiselect quick without triggering method 3/4 times.
Im keeping track of my own table through an array. And the GridView from DevExpress got his own events that can give me the right information, so no need to worry about that.
So I got a method receiveSelectionFields(Values){ //do something } where I receive that information from the gridview on every selection.
Then I check my array to see if they added or removed a selection, and which.
Then I call addtablerow(customer) or removetablerow(customer). Which removes the customer from my table and then from my array.
Because I make some heavy calculations in between, there is a ~60ms delay before the calculation is done (on mine computer). So if the users makes 2 selections in 60 ms. My array will have the wrong value (not being modified by the first call that adds/removes a customer) and my javascript will cause an error e.g. the table row is not deleted. I check on length of my own array and on the length of the received array to see if something has been added or removed.
So what did I try?
Making my method a recursive method, that when the problem occurs it waites 60 ms and then redo the method. But this isn't working properly.
I tried adding a global variable busy, which is true when the method is still busy. And false when it ends. But my browser just quits when doing that. This was the code:
while (true) {
setTimeout(function () {
if (busy === false) {
break;
}
}, 50);
}
But I got the feeling it just endlessly loops.
And these are all workarounds, there must be a nice way to solve this. Any thoughts?
In short:
I want a way to let the functions go off in synch. even if their being called asynch. by the user so that my array doesn't mess up.
Found the answer why my problem exists:
Since javascript is a synch. language (1 thread). the functions should've triggered at the right time. The problem is the callback from DevExpress Gridview for MVC Extensions. It makes a callback to the server, which responds in for example ~150ms with the selected field values. This will give an error if you quickly trigger the devexpress function twice. The second trigger has a window to return FASTER then the first trigger. Which would mean my coding of the table get ruined since I check if something has been added or removed. So when the first trigger (which returns after the second trigger) and my table gets updated. It shows the table prior to my last selection. Thus missing 1 row or has 1 more row then it should've.
So I got to make a function that retrieves all the events, and then place them in an order ~200 ms after each order. To make sure there is enough time for the callback to retrieve. Though this is ofcourse still not reliable, I think I will just change the requirements on this.
Your while loop condition is true, therefore the loop will just continue endlessly. You may want to try the following:
var int = setInterval(function () {
if(busy === false) {
clearInterval(int);
}
}, 50);
Try this instead of looping through the setTimeout over and over. If I had to guess, the break is breaking the if statement but not the while loop causing your browser to get stuck in an endless loop. With the above code, you can set an interval at which to run the code. In this instance, the code runs every 50ms. Once the condition inside the if statement is true, the setInterval is cleared causing the browser to continue executing its normal functionality.
Hope this helps.
I have a dropdown menu which basically sends some data back to the server via onchange. The data is stored in the database and the primary table using seconds to avoid duplicates. What happens is if I scroll the dropdown via my keyboard, for example, using the up/down keys, my script returns an error because there is a duplicate entry now in the database.
here is part of the code for the dropdown in html
onchange="GetmySQLData();"
My question is can i just do a setTimeout and would it be correct to use it in this format:
onchange="setTimeout('GetmySQLData();', 1750);"
If you have a better approach, please feel free to let me know.
Thanks
The following versions are interchangeable:
setTimeout('myFunction()',1750);
setTimeout(myFunction,1750)
setTimeout(function() { /* some anonymous function */ },1750)
What you might want is
<script>
var tId = ""; // create a global var
</script>
and have
onchange="clearTimeout(tId); tId=setTimeout('GetmySQLData()',1750)"
which will only execute the onchange after the user does not change the dropdown for the duration of the timeout, but if the user changes within the timeout period, a new timeout is triggered
If you are planning to use the setTimeOut then it has to called in the following way:
onchange="setTimeout(GetmySQLData(), 1750);" providing single quotes aroung the arguments makes it to be treated as a string. and the timeout value is in milliseconds.