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.
Related
Whenever I need to make an ajax request to the server without refreshing the page, Genexus should emit some event that I could use in my UC.
What exactly would be this event, and if there is none, how could I know every events in JS to use in an Genexus User Control?
I.e.:
If I click in an UserAction that may search for values in another table and then retrieve then for me, how can I capture this?
Another Scenario:
I'm making a request to the server and retrieving information of it. I need to get the
$(gx-warning-message).text();
but if my request is still loading, when I execute my function, it will return nothing, because the event is still loading up informations.
To avoid this, I'm making a looping, but it's not an elegant way to solve the issue.
$(".Button").click(function() {
var timesRun = 0;
var interval = setInterval(function(){
timesRun += 1;
if(timesRun === 60){
clearInterval(interval);
}
if ($(".gx-warning-message").text().length > 0) {
toastrgx();
$(".gx-warning-message").text('');
}
}, 200);
});
So, how can I make it better?
Thanks
If you want to be notified every time a GeneXus user event is fired, you can subscribe to gx.onafterevent event. For example:
gx.fx.obs.addObserver('gx.onafterevent', scope, function () {
// Here goes the code you want to execute
// every time a GeneXus event is fired
});
scope is the object you want to set as the this of the third parameter function.
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();
I have a field where a user can manually enter a code for a new product. It cannot be a db-generated ID as the user needs control over the code.
As this will require a round-trip to the server to check, my intention was to trigger this check as the "keyup" event happens (a bit like a live filter would work) so that there is instantaneous feedback about the entered code (and probably CSS-based colour/image reinforcement). Admittedly for this particular function I may choose to do it onblur instead of keyup but there may well be other situations where I require a check on keyup.
Is this fundamentally a bad thing or not? A round-trip to the server to check if an item code exists (primary key) should be a very fast process but with a slow connection I'm just wondering if there could be a race condition whereby a fast typer stacks up the db calls faster than it can return and then a situation may emerge whereby a "submit" button is enabled when it shouldn't be. Of course I would also back this up with db-level checking on submission of the form, but I'm trying to make this as apparently responsive as possible.
Is it generally a bad idea to do keyup checking on any data source that's not held in memory or is this an acceptable practise?
Since you use knockout you can use throttle
ViewModel = function() {
this.text = ko.observable().extend({ throttle: 500 });
this.text.subscribe(this.onText, this);
};
ViewModel.prototype = {
onText: function(value) {
console.log("ajax call");
}
};
http://jsfiddle.net/TT3AB/
There's never going to be a race condition if you abort any previous ajax request on the next keyup.
EDIT
(function() {
var xhr; //a reference to an XMLHttpRequest Object
var onKeyUpCallback = function() {
if(xhr) {
xhr.abort();
}
xhr = ... //build your XHR object
//....
xhr.send();
}
yourInputElement.addEventListener('keyup',onkeyUpCallback);
}());
If you want to check at real time, you can approach in this way. When user types data and halts for a while then send you ajax request to check for existence of that particular thing in database.
Call a function on key up event but do not send request until user stops for a while
var timer;
function onkeyups()
{
if(timer)
{
clearTimeout(timer);
}
timer = setTimeout(function(){callRequest();},500) // the delay, after how much millisecond the ajax call should be made when user has stopped typing
}
function callRequest()
{
// Make your ajax call or whatever
}
There are several divs and handler to send ajax requests when they are clicked. My problem is that i don't know how to force my handler not to exceed limit of 1 request per 30 seconds.
Appreciate your help!
The excellent Underscore.js has a throttle function. You pass in the handler that you want to throttle and get back a rate-limited version of the same function.
var throttled = _.throttle(someHandler, 100);
$(div).click(throttled);
http://documentcloud.github.com/underscore/#throttle
Here's a simplified version that I've used in my own code:
function throttle(func, wait) {
var timeout;
return function() {
var context = this, args = arguments;
if (!timeout) {
// the first time the event fires, we setup a timer, which
// is used as a guard to block subsequent calls; once the
// timer's handler fires, we reset it and create a new one
timeout = setTimeout(function() {
timeout = null;
func.apply(context, args);
}, wait);
}
}
}
A good way to test it is by firing off a bunch of scroll events and watching your handler log to the Firebug console:
document.addEventListener("scroll", throttle(function() {
console.log("test");
}, 2000), false);
Here's a version that limits click-events on divs to once every 30 seconds, as requested (requires jQuery):
$("div").click(throttle(function() {
// ajax here
}, 30000));
If you want to rate limit, then unfortunately the _.throttle method that underscore.js provides is not your solution. Throttle will simply ensure your method is never called more than X seconds, and therefore all subsequent function calls will be disregarded until that period has passed.
If you want to rate limit so that you never call your function more than X times per second, but don't lose those function calls altogether, then you need a wholly different solution.
I have written an underscore extension at https://gist.github.com/1084831
You can see a working example at http://jsbin.com/upadif/8/edit#preview
Create a boolean canFireRequest, or whatever, flag and set it to false after each ajax request. Then create a 30 second time span that sets it back to true; check the flag's value before each new request.
Here's a rough example:
if ($(this).data('canFireRequest')) {
// Ajax request goes here
$(this).data('canFireRequest', false);
}
setTimeout(function() {
$(this).data('canFireRequest', true)
}, 30000);
I have a text box on a web page, whose value I want to send to a XMLHttpRequest. Now I want the user to just type the value, without pressing a button. But If i just send the request int he keyboard events, it will fire every time a key is pressed.
So basically I want something liek this
function KeyUpEvent()
{
if (user is still typing)
return;
else
//do processing
}
It would be great if the solution could come from plain javascript or mootools. I dont want to use any other library.
The way this is usually done is by restarting a timer on the keyup event. Something like this:
var keyupTimer;
function keyUpEvent(){
clearTimeout(keyupTimer);
keyupTimer = setTimeout(sendInput,1000); // will activate when the user has stopped typing for 1 second
}
function sendInput(){
alert("Do AJAX request");
}
Basically, you want to start a timer on KeyUp, and when KeyUp starts again, reset the timer. When the user stops typing, the timer runs out, and your request can go at that point.
Example:
var timout_id;
function keyup_handler(event) {
if (timout_id) {
clearTimeout(timout_id);
}
timout_id = setTimeout(function(){
alert('sending data: \n' + event.target.value)
}, 800);
}
Just attach the function to the input using your preferred method, and replace the alert with your preferred action.
Of course there are many ways you could generalize this approach and make it more reusable, etc, but I think this illustrates the basic idea.
I always use this simple function to handle a timer, that will fire a callback function, after the user has stopped typing for a specified amount of time:
var typewatch = (function(){
var timer = 0;
return function(callback, ms){
clearTimeout (timer);
timer = setTimeout(callback, ms);
}
})();
Usage (example with MooTools):
$('textInput').addEvent('keyup', function(e){
typewatch(function () {
// executed only 500 ms after the last keyup event
// make Ajax request
}, 500);
});
The main difference between this solution and solutions from other answers is that all the timer logic is handled by the typewatch function itself, the event handler doesn't need to know anything about the timer, it just invokes the function. Also, there are no global variables to take care (the timer id is not stored on a global variable).
You never know when a user is really "finished" typing. The user might take a sneeze break, or a stretch break, or a coffee break, and then continue typing.
However, if you're implementing something like an autocomplete mechanism, you can set a timer (cf. window.setTimeout(...)) to see if the user hasn't typed anything in a certain amount of time. If you get another key-up event while the timer is running, you can start the timer over.
var keyTimer;
function onKeyUp(){
clearTimeout(keyTimer);
setTimeout(stoppedTyping,1500);
}
function stoppedTyping(){
// Profit! $$$
}
EDIT: Damn ninjas
I wrote a custom jQuery event because I use this logic a lot:
jQuery.event.special.stoppedtyping = {
setup: function(data, namespaces) {
jQuery(this).bind('keyup', jQuery.event.special.stoppedtyping.keyuphandler);
},
teardown: function(namespaces) {
jQuery(this).bind('keyup', jQuery.event.special.stoppedtyping.keyuphandler);
},
keyuphandler: function(e) {
var interval = 1000;
var el = this;
if (jQuery.data(this, 'checklastkeypress') != null) {
clearTimeout(jQuery.data(this, 'checklastkeypress'));
}
var id = setTimeout(function() {
jQuery(el).trigger('stoppedtyping');
}, interval);
jQuery.data(this, 'checklastkeypress', id);
}
};
You can use it like this:
$('input.title').bind('stoppedtyping', function() {
// run some ajax save operation
});
For some reason I could never get it to work with .live( ... ). I'm not sure why...
Use onBlur and maybe an onKeyDown to check for the user pressing the return/enter key.