How to avoid repeated requests in textbox, textarea interacting with server - javascript

In Javascript, does someone/anyone have a exhaustive list of key combination to avoid. For example a textbox sending webservice requests for each keypress. It often happens for key combinations like HOME[for cursor returning to start of textbox] , DELETE, SHIFT + HOME + DELETE the event fires and same request params is sent to webservice.[I know caching query results will solve for this scenario but what about other]. I would love to see a list for this, i couldn't find a similar question before hence this
Update:
Since the earlier question title seemed too way off and likely closable i changed it. I would like to know how to optimize this process of sending request in ajax manner less intensive.

Assuming the user is writing in a textarea or input, you can check the value of that element for each keystroke; if it's different from its last value, send it to the server. If it's the same, then do nothing.
Example:
function updateOnKeypress(input) {
var lastValue;
input.addEventListener('keypress', function() {
if( input.value.trim() !== lastValue ) {
// trim() isn't available in every browser, but you can add it
// Of course, if leading/trailing whitespace is supposed to trigger
// a request, then skip the trim()
lastValue = input.value.trim();
// … send value to server
}
});
}
You might also want to throttle the number of requests with a timer, so not every change results in a request to the server. For instance, for each change start a timeout (say, 0.3 seconds), and then send the value when the timer executes.
function updateOnKeypressDelayed(input) {
var lastValue, timer;
input.addEventListener('keypress', function() {
if( !timer ) {
timer = setTimeout(function () {
if( input.value.trim() !== lastValue ) {
lastValue = input.value.trim();
timer = null;
// … send value to server
}
}, 300);
}
});
}

Instead of avoiding all those combinations, why don't you just limit what you accept.
For example make a JS function that checks if this is a character, letter or special character, if not then do not submit to server.

Related

Javascript: multiple events, one ajax request

I'm somewhat new to JS and I'm trying to think of the best way to design some asynchronous interaction in an application I'm working on.
I've got a list of records that are related to some live API-backed data. I show the user a list of these records, and the user can select specific records that they want to see more information about. I load this additional data from the API via an ajax call.
To make this a more real world example, let's say what I have is a list of stocks. I've got the name and yesterday's closing price for each stock. There's a check box next to each stock name, and if the user checks this box it plots the historic price of the stock for the past year on a graph.
When the user selects one stock in the way, the behavior is simple. I send one API request for the historical data for one stock, and I plot it on the graph.
However, the user might select a bunch of stocks at once, or in rapid succesion. I don't want to fire 10 or 20 or 50 requests back-to-back, I want to make one request for 10 or 20 or 50 stock histories.
Let's say my application has an event listener that looks up the stock history when the check box is toggled, something like this:
$('input.stock_toggle').change( function(event){
var symbol = $(this).data('symbol');
lookupStockHistory(symbol);
});
How could I define a lookupStockHistory function, or some other kind of event listener etc., that would wait a second and pool all the events that came in to send a single request instead of firing many times in row?
var lookupStockHistory = (function () {
"use strict";
var qeue = [], timeoutHandler = null, timeoutTime = 1000,
sendCall = function () {
//process qeue array and trigger ajax call
//and clean qeue
qeue = [];
},
add = function (symbol) {
if (timeoutHandler) {
clearTimeout(timeoutHandler);
timeoutHandler = null;
}
qeue.push(symbol);
timeoutHandler = setTimeout(sendCall, timeoutTime);
};
return add;}());
To trigger just call lookupStockHistory(symbol). This will gather symbol to array which will be processed after 1 second since last call
You can use push your request into a "global variable" with your namespace, and then use setTimeout to delay the AJAX call (a second or two maybe?).
The setTimeout would call a function that gets the requests from the "global variable", empties the variable, and then constructs your request. Any subsequent calls to the setTimeout function would see that the "global variable" was empty and not construct future AJAX requests.
In the example below, I also remove the current pending timeout as a new one has been initiated.
Here's a pseudo-code example using jQuery for selection and event capture:
var DELAY_FOR_INPUT = 2000; // 2 seconds
var g_MyDataToRequest = [];
var g_currentAJAXCallTimeout = null;
function _callAPI = new function() {
g_currentAJAXCallTimeout = null;
var dataToGet = g_MyDataToRequest;
g_MyDataToRequest = []; // clear out global request cache
// TODO: Loop over dataToGet and construct AJAX request
// TODO: Perform AJAX call...
}
$('.myCheckbox').click(function() {
var myRequest = $(this).attr("ID"); // or Val(), or whatever you'd like to track your item
g_MyDataToRequest.push( myRequest );
// If have a pending request, kill it
if (g_currentAJAXCallTimeout != null) {
clearTimeout(g_currentAJAXCallTimeout);
g_currentAJAXCallTimeout = null;
}
g_currentAJAXCallTimeout = setTimeout( _callAPI, DELAY_FOR_INPUT );
});
This is, as noted, pseudocode and may not quite work right, but it should get you going.
You could implement a timer and start it with the first click or change event. And with each additional click or change event you can reset the timer. Also, with each event you can add or remove the symbol value to an array accordingly. Once the timer expires, you join the array to be a comma-delimited string and post that back via ajax and get a JSON result.

How to efficiently record user typing using javascript?

I'd like to be able to record then playback whatever happened in a textarea.
I've came across some solutions but they're not reliable, Like sending each keystroke via AJAX. In that case i'll end up having millions of rows in my DB.
The idea that i had in mind is to log the keystrokes to a variable in client side, updating that variable with the action, but keeping track of time between each keystoke. Also making sure that it supports deleting data as well.
At the end i'd send this whole variable to the db one time, then i can decode it later for playback.
Mind Map of what the variable would look like:
hellooo[1.2][backspace][0.6][backspace]World![return]
Idle time __^ Removes one char __^
I believe that google docs is doing something like that to playback whatever users were typing.
Any Ideas?
Luckily JavaScript events already take care of all the encoding issues for you. You can just toss the whole event object that a keyup/keydown/keypress/whatever was directly into an array.
Each object contains:
Type of event
Timestamp
What key was used (or combination of keys)
What was in focus
Some other stuff that might end up being useful.
You can then just encode the array and send it off with your favourite ajax method to the server for storage.
So your code only needs a function that can store data. Don't use this exact function it's just for demonstration purposes.
var handler = function (e) {
handler.data.push(e);
console.log(handler.data);
}
handler.data = [];
window.addEventListener("keyup", handler);
window.addEventListener("keydown", handler);
window.addEventListener("keypress", handler);
Since it's an array, they should all be in order, but in the odd event it goofs, you have timestamp data on each event (which also lets you find out the delay between events, which is AWESOME if you have mixed keypresses.).
You can then replay events however you wish to design them -- but now you don't have to invent your own spec because the lovely fokes at w3c did all the hard work for you when they designed the DOM event spec.
Store the time of each action and the result of that action and when you are finished serialise the log and store that.
It is over-complicated to replay each action individually. Say a user moves back a few characters and adds new ones there. You will need to keep track of the cursor position.
Just remember the entire value of the textarea for each keystroke. There is no need to remember how that occurred is there?
Here's an implementation. fiddle
<textarea id="recorder"></textarea>
<textarea id="playback"></textarea>
<script type="text/javascript">
var Playback = {
//store the time an action occured and the resulting state in an object
//don't use an array because they are not sparce - interstitial keys
//will have to be iterated over
record: {},
init: function( recorderId, playbackId ) {
this.recorder = document.getElementById( recorderId );
this.playback = document.getElementById( playbackId );
this.recorder.addEventListener( 'focus', function() {
Playback.record = {};
this.value = '';
}, false );
this.recorder.addEventListener( 'keyup', function( e ) {
Playback.record[ (new Date()).getTime() ] = this.value;
}, false );
this.recorder.addEventListener( 'blur', function( e ) {
Playback.playback.value = '';
//store the time the sequence started
//so that we can subtract it from subsequent actions
var mark = null;
for( var t in Playback.record ) {
if( mark ) {
var timeout = t - mark;
} else {
var timeout = 0;
mark = t;
}
// We need to create a callback which closes over the value of t
// because t would have changed by the time this is run
setTimeout( Playback.changeValueCallback( Playback.record[t] ), timeout );
}
}, false );
},
changeValueCallback: function( val ) {
return function() { Playback.playback.value = val }
}
}
Playback.init( 'recorder', 'playback' );
</script>
Caveat:
The event handling is for compliant browsers only, you'd need to accommodate internet explorer yourself
Databases are made to handle millions of records, so that's not really a problem.
If you still don't want to do that you could encode all the data related to a session as JSON and store it in a text field in the database or as a file on the server. In this case if the data is really large it may take a while to load the data and send it to the browser, causing a delay for the user.

$.get() troubles before submitting page

Think a shopping basket with some "goods" in it.
I have a <li> list of elements and each of them has a field containing a number - the amount.
Conceptually this is what i want: When the user presses a button, i loop through each <li> element picking the amount. Then i do a $.Get() to call the server with the goods id + the amount to check if the store has enough of the particular item. The server replies either True og False.
This reply is temporary stored in a html field.
Now after the looping is done, i check if there were a False reply on any of the goods.
If so i highlight the "false" items. Or else i simply submit.
OK, the problem is that my code seams to continue past my $.get() call-back function, so the final check to see if any false was returned is evaluated before the $.get() actually receives a result from the server.
Anyway this is what i think is happening...
Now lets look at some code:
var tmp = new Array();
var id = '';
var c = '';
var n = 0;
var z=0;
$('#basket').find('li.list').each(function() {
c = $(this).find('input.fldamount').val(); // this is the amount field
id = $(this).attr('id'); // this is the id no of the item
$.get('./(RPC)?OpenAgent&cmd=movewhcheckamount&unid='+id+'&count='+c, function(data) {
$('#RPCResult').val(data); // i store the returned value in a html field
if ( $('#RPCResult').val() == "true" ) {
tmp.push( id+'|'+c ); // if true is returned, i push the id & amount to an array
} else {
$(this).addClass('red'); // else i tag the item
n=n+1; // and then increment a counter
}
} ); // $('#basket')
var t = window.setTimeout( function() {
if (tmp.length > 0 && n == 0) { // if i got items in the array AND my false counter is zero
$('#SelectedArtikler').val( tmp.join(";") ); // then i store the array as text in a field
document._FlyttArtikel.submit(); // and submit
} else if (n > 0) {
// show a popup
alert("You're trying to move more items than exists...");
} else {
alert("ops, nothing to move..."); // should never end up here...
}
}, 1000);
window.clearTimeout(t);
As you can see, i have tried to counter-act the code running past my call-back function with a setTimeout, so that i basically wait a sec to give the server time to respond.
I did have another loop around the setTimeout the continued as long as my #RPCResult field was empty, but that resulted in an infinite loop.
I have the #RPCResult field visible so i can see what happens and what i see is that the popup "You're trying to move more items..." is shown and RIGTH AFTER i press ok on that popup THEN the #RPCResult field gets the result true/false.
I now some of the code can be optimized but what i'm interested in right now is getting the $.get() result in a proper fashion.
Thanks in advance ;-)
You're going to have to place the code that's going to run when all the "$.get()" calls are finished inside the callback routine. That's the only place where you can be sure that you've actually gotten the server response(s). Keep a counter of how many calls have returned, and when the counter increments up to be equal to the total number of items, then you know that all of the responses are available.
Thus:
var basketSize = $('#basket').find('li.list').length, completed = 0;
gives you the number of items and initializes the counter. Inside the callback function passed to "$.get()" you can then do something like this:
if (++completed === basketSize) {
// code currently in the "setTimeout()" callback
}
Your code already appears to be doing some of this work (the "n" variable that you increment).
Now, that said, I'll also note that it's quite crazy to perform separate HTTP transactions for each of your "items". You should bundle them all up into one HTTP request that returns a list of answers.
Also, there's really no point at all in storing the response to the "$.get()" in that "RPCresult" field; just examine the value of "data", since nothing ever looks at "RPCresult" again anyway.

Request to Asp page misses if called very fast

I have a website with is made in classic asp. On one page there are 5 textboxes and 5 labels. When users type some value in the textbox then using javascript (added on Onchange event) another asp page is called which done some calculations, fetches some database values based on the value typed and return the result to caller page to be displayed in the corresponding label. Same Page is called for all the textboxes for fetching result. My problem is if user types at normal speed and move to other textbox(using tab) at a normal speed the second page is called properly and result displayed properly in labels, but if the user type very fast then the request for the value of one textbox is not completed and other is send and as a result there is no result value coming for first one. Is there is any way to handle this.
Thanks
If you don't want to refactor the whole thing to use XHRequests, there's a simple solution. Have a "request in progress" flag that is checked in your onchange event. If a request is in progress, queue the current action. In the result display code, check the queue and execute the next action, if any. If there are no remaining actions, reset the flag.
Here's a basic outline in code:
var requestInProgress = false;
var pendingActions = [];
function OnChangeHandler(onChangeFunction) {
if(requestInProgress) {
pendingActions.push(onChangeFunction);
}
else {
requestInProgress = true;
onChangeFunction();
}
}
function ResponseHandler()
{
//Your regular response handling code
if(pendingActions.length > 0) {
var nextFunction = pendingActions.shift();
nextFunction();
}
else {
requestInProgress = false;
}
}
I'm no JS expert, so I'm sure that this could be cleaned up a little bit, but it should handle overlapping events just fine.

How do I set up a timer to prevent overlapping ajax calls?

I have a page where search resuts are shown both in a grid and on a map (using KML generated on the fly, overlaid on an embedded Google map). I've wired this up to work as the user types; here's the skeleton of my code, which works:
$(function() {
// Wire up search textbox
$('input.Search').bind("keyup", update);
});
update = function(e) {
// Get text from search box
// Pass to web method and bind to concessions grid
$.ajax({
...
success: function(msg) {
displayResults(msg, filterParams);
},
});
}
displayResults = function(msg, filterParams) {
// Databind results grid using jTemplates
// Show results on map: Pass parameters to KML generator and overlay on map
}
Depending on the search, there may be hundreds of results; and so the work that happens in displayResults is processor-intensive both on the server (querying the database, building and simplifying the KML on the fly) and on the client (databinding the results grid, overlaying big KML files on the map).
I like the immediacy of getting progressively narrower results as I type, but I'd like to minimize the number of times this refreshes. What's the simplest way to introduce an N-second delay after the user stops typing, before running the update function?
Instead of calling update() directly, call a wrapper that checks to see if there are any pending delayed updates:
$('input.Search').bind("keyup", delayedUpdate);
function delayedUpdate() {
if (updatePending) {
clearTimeout(updatePending);
}
updatePending = setTimeout(update, 250);
}
function update() {
updatePending = false;
//$.ajax(...
}
You should also probably add:
$('input.Search').bind("blur", update);
This will do an immediate update when the user leaves the field. But make sure you also add handling for the case where the user leaves the field while there's a pending delayed update (cancel the delayed update first).
As a first approach, what about something like :
$('input.Search').bind("keyup", function() { setTimeout(update, 5) } );
(not sure about the exact setTimeout syntax).
You can also keep a variable to track whether the timeout has already been scheduled or not.
You can use Window.SetTimeOut(YourRefreshMethod) , when the YourRefereshMethod gets called, it will check number of characters being typed so far , and compare it to the some counter, the counter will starts with 0 value, so the initial call will do nothing other than updating the counter with the current characters typed count, the second time your method get called , it will check the number of characters typed, if it matches the previous number recorded by the counter , then it means the user didn't type anything new and you can fire your Refresh method, otherwise you will update the counter value

Categories

Resources