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.
Related
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.
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.
I have a table with a list of 5 items that is dynamically generated using AJAX with a PHP/MySQL backend. Each item has an INPUT Checkbox next to it.
Upon page load, some of the checkboxes must appear "checked" depending on a set of values returned by PHP/MySQL. Thus:
var checked_items = [];
// var checked_items = new Array(); // this is not working either
var GetItemsFromXML;
var SplitItems;
var XMLItemsObj = new Object;
// function...
function DisplayItems() {
CreateRObj(function() {
if (xmlhttp.readyState==4 && xmlhttp.status==200) {
GetItemsFromXML=xmlhttp.responseText; // returns pike delim list
SplitItems = GetItemsFromXML.split("|"); // split at pike
// status - either "all" or "selected"
XMLItemsObj.checkedstatus = SplitItems[0];
// drop last ","
SplitItems[1] = SplitItems[1].substr(0,SplitItems[1].lastIndexOf(","));
// split at comma and pass to variable
checked_items = SplitItems[1].split(",");
}
});
xmlhttp.open("POST","loaditems.php",true);
xmlhttp.send();
}
At this point, the variable "checked_items", contains the values returned by PHP/MySQL.
These values will determine which checkbox is checked off.
I've confirmed that this variable is populated by testing to see if it is empty (""),
undefined, or null.
Now, Once the page loads, the code below loops through all the checkboxes and checks them off depending on the "XMLItemsObj.checkedstatus" status. If status is "selected", then only some are checked off. If status is "all", all checkboxes are supposed to be checked off.
Here's the code that does this:
if (XMLItemsObj.checkedstatus=="selected") {
for (i=0; i < checked_items.length; i++) {
$(":checkbox[value="+checked_items[i]+"]").attr("checked",true);
}
} else if (XMLItemsObj.checkedstatus=="all") {
$("input:checkbox").each(function() {
$("input:checkbox").attr("checked",true);
});
}
Here's where things start getting weird...
The above only seems to work when I use the "alert" function
to check for the values in the "checked_items" variable. It works
when ".checkedstatus" is "all" or when its "selected".
Thus, if I were to use, for example this code:
alert("The values of the checked_items variable are: "+checked_items);
Then, the above code will work perfectly.
However, it doesn't work otherwise. Neither when status is "all", nor
when status is "selected".
I'm at a loss, but my guess it that the variable is either losing its
value, or the event(s) are not being triggered. However, I'm not sure.
Anyone has any ideas? All comments, insights, solutions, and thoughts
are welcome. Thanks in advance!
Here is catch Upon page load, some of the checkboxes must appear "checked" Ensure your script runs when DOM structure of target is created. Then it must work.
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.
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