passing variale to javascript callback function - javascript

Faced a problem with passing a variable into javascript callback function. Can't understand, why it doesn't work.
Here is the code. I pass variable 'i' through many functions. 'function1' is just an example, there is a big piece of code.
also the code in callback, 'var tmpl' is just an example, don't pay attention to that. The problem is why i can't pass 'i' variable.
function function1() {
for (var i = 0; i < 10; i++){
RequestData(i);
}
}
function RequestData(i, callback){
var xhr = new XMLHttpRequest();
xhr.open('GET', '/getitemID='+i, true);
xhr.send();
xhr.onreadystatechange = function() { // (3)
if (xhr.readyState != 4) return;
if (xhr.status != 200) {
alert(xhr.status + ': ' + xhr.statusText);
} else {
alert(xhr.responseText);
callback(JSON.parse(xhr.responseText));
}
xhr.close();
}
}
RequestData(i, function (json) {
alert('here!');
var tmpl = [
'<div id="content"><div id="bodyContent">',
'<button onclick="RequestData("+i+")">Load Data!</button></div>',
'<div>#here!</div>'
].join('');
var body = document.querySelector('body');
alert(body);
for (var i = 0; i < json.length; i++) {
var html = tmpl.replace('#here!', json[i].itemid);
body.insertAdjacentHTML('afterbegin', html);
}
});
if i try to calling callback like this: function RequestData(i, callback) {- i get 'unresolved type or variable 'i'' error, and callback doesn't work. else if i do not pass 'i' in callback - i do not get this error, but looks like callback doesn't work too, because this code for callback don't work RequestData(function (json) { alert('here!');} - i don't receive a message 'here', but no errors. in both situations callback call is: callback(JSON.parse(xhr.responseText));

First of all, i is undefined because you are calling RequestData(i, function()), while i is not defined.
You only call RequestData from function1() but that method is never executed and never has a callback specified.
To make it work, remove the RequestData(i) call from function1(). Then put the method call RequestData(i, function (json) { inside the the for loop. Finally call function1() and you will get your result. (not with clean code though).
function function1() {
for (var i = 0; i < 10; i++){
RequestData(i, function (json) {
alert('here!');
var tmpl = [
'<div id="content"><div id="bodyContent">',
'<button onclick="RequestData("+i+")">Load Data!</button></div>',
'<div>#here!</div>'
].join('');
var body = document.querySelector('body');
alert(body);
for (var i = 0; i < json.length; i++) {
var html = tmpl.replace('#here!', json[i].itemid);
body.insertAdjacentHTML('afterbegin', html);
}
});
}
}
function RequestData(i, callback){
var xhr = new XMLHttpRequest();
xhr.open('GET', '/getitemID='+i, true);
xhr.send();
xhr.onreadystatechange = function() { // (3)
if (xhr.readyState != 4) return;
if (xhr.status != 200) {
alert(xhr.status + ': ' + xhr.statusText);
} else {
alert(xhr.responseText);
callback(JSON.parse(xhr.responseText));
}
//xhr.close(); // this is not an existing function
}
}
// run the for loop by calling this method
function1();

Related

Unable to pass variables to onreadystatechange

This is the current source code:
var xhttp = new XMLHttpRequest();
function passVars(var1, var2, var3) {
if (var1.readyState == 4) {
if (var1.status == 200) {
var data = var1.responseText;
if (data) {
playSuccess();
classSwitch(data, var2, var3);
} else {
playFailure();
alert("Error: returned status code " + var1.status + " " + var1.statusText);
}
}
}
}
xhttp.onreadystatechange = function() {
passVars(xhttp, "tab", "p1234");
};
xhttp.open("POST", "index.php", true);
xhttp.send(formData); //formdata is a POST send to PHP's backend which returns responseText = 0 or 1 for var data
function classSwitch(state, sOrigin, stateButtonID) {
if (sOrigin === "tab") {
Monitored = state;
if (state === "1") {
if (document.getElementById("setmonitoring").classList.contains("productmonitoringdisabled")) {
document.getElementById("setmonitoring").classList.remove("productmonitoringdisabled");
}
document.getElementById("setmonitoring").classList.add("productmonitoringenabled");
}
if (state === "0") {
if (document.getElementById("setmonitoring").classList.contains("productmonitoringenabled")) {
document.getElementById("setmonitoring").classList.remove("productmonitoringenabled");
}
document.getElementById("setmonitoring").classList.add("productmonitoringdisabled");
}
}
if (sOrigin === "issues") {
if (state === "1") {
if (document.getElementById(stateButtonID).classList.contains("productmonitoringdisabled")) {
document.getElementById(stateButtonID).classList.remove("productmonitoringdisabled");
} else document.getElementById(stateButtonID).classList.add("productmonitoringenabled");
}
if (state === "0") {
if (document.getElementById(stateButtonID).classList.contains("productmonitoringenabled")) {
document.getElementById(stateButtonID).classList.remove("productmonitoringenabled");
} else document.getElementById(stateButtonID).classList.add("productmonitoringdisabled");
}
}
}
Tried a lot of ways to pass them using mainly SO anwsers and each time var2 and var2 are undefined. This is used in an inventory control system, early alpha version. The idea is to pass element's id to change button class when the backend returns product's current monitoring state
Any ideas how to pass those variables? Thanks in advance
Your structure is no good. You can't pass xhttp like that in a asynchronous call. Rewrite it directly like this...
xhttp.onreadystatechange = function() {
if (xhttp.readyState == 4) {
if (xhttp.status == 200) {
var data = xhttp.responseText;
if (data) {
playSuccess();
classSwitch(data, 'tab', '1234');
} else {
playFailure();
alert("Error: returned status code " + xhttp.status + " " + xhttp.statusText);
}
}
}
};
Below is a minimal example of how to pass parameters to the onreadystatechange callback function. You've overcomplicated it a bit - there's no need to pass your xhttp variable to the callback either since it's already available in scope.
const xhr = new XMLHttpRequest();
xhr.open("GET", "https://developer.mozilla.org/");
xhr.send();
xhr.onreadystatechange = function() {
callback("tab", "1234");
};
function callback(var1, var2) {
console.log(`var1 = ${var1}`);
console.log(`var2 = ${var2}`);
//Do your xhr.readyState and xhr.status checks in this function
console.log(`xhr.readyState = ${xhr.readyState}`);
console.log(`xhr.status = ${xhr.status}`);
}
used
function passVars(var1, var2, var3) {
xhr.onreadystatechange = function(e) {}
}
the "e" in the function bracket will call back all var you have before in the function passVars

Why is my function turning into a object?

I have several ajax requests in my project so to simplify my code i created a function to handle the requests.
I am passing a success function to load when status is == 200 however even though the data is being sent correctly i am receiving "uncaught type error success is not a function" error. Using typeof i can see success starts as a function but then displays as an object.
here is my code
//function to handle ajax requests
function sendRequest(link, requestType, success, data) {
var request = new XMLHttpRequest(),
url = link;
request.open(requestType, url, true);
request.onload = function () {
if (request.status >= 200 && request.status < 400) {
console.log("success");
var responseText = request.responseText;
console.log(typeof(success));
success(responseText);
} else {
consoloe.log("error");
}
}
request.send(data);
}
//calling function passing success function as argument
var jsonText = "json.php";
sendRequest(jsonText, 'GET', function (response) {
var json = JSON.parse(response),
postcodesArray = [],
jsonLength = json.length,
i;
for (i = 0; i < jsonLength; i++) {
postcodesArray.push(json[i].from_postcode);
}
var postcodes = postcodesArray.filter(Boolean),
pstcodeLength = postcodes.length,
n;
for (n = 0; n < pstcodeLength; n++) {
geocodeAddress(postcodes[n]);
}
});
//EDIT fiddle containing all my code
https://jsfiddle.net/x1qe73s8/
Reading your jsfiddle, your code is probably going through the sendLatLng function where you have
sendRequest(url, 'POST', null, data);
typeof null is "object".

Maintaining state of variables when changed inside a timer

I am using JavaScript.
I amusing a setInterval timer method.
Inside that method I am changing the values of module variables.
The thing is in IE the changes to the variables are not 'saved'. But in Chrome they are.
What is the accepted practice to do what I need to do?
this is my code:
function start()
{
var myVar = setInterval(function () { GetTimings() }, 100);
}
var currentts1;
var currentts2;
var currentts3;
var currentts4;
var frameCounter;
function GetTimings() {
if (frameCounter < 1) {
frameCounter++;
var xmlhttp = new XMLHttpRequest();
xmlhttp.open("GET", urlTS, false);
xmlhttp.onreadystatechange = function () {
if (xmlhttp.readyState == 4) {
var nextts = xmlhttp.responseText;
var bits = nextts.split('|');
if (currentts1 != bits[0]) {
currentts1 = bits[0];
postMessage("0|" + bits[0]);
}
if (currentts2 != bits[1]) {
currentts2 = bits[1];
postMessage("1|" + bits[1]);
}
if (currentts3 != bits[2]) {
currentts3 = bits[2];
postMessage("2|" + bits[2]);
}
if (currentts4 != bits[3]) {
currentts4 = bits[3];
postMessage("3|" + bits[3]);
}
frameCounter--;
}
}
xmlhttp.send();
}
}
The variables:
currentts1
currentts2
currentts3
currentts4
frameCounter
values are not preserved...
Try this, but notice I changed the currentts* to an Array when you try to view them
function start() {
var myVar = setInterval(GetTimings, 100);
}
var currentts = [null, null, null, null];
var in_progress = 0; // clear name
function GetTimings() {
var xhr;
if (in_progress > 0) return; // die
++in_progress;
xhr = new XMLHttpRequest();
xhr.open('GET', urlTS);
function ready() {
var nextts = this.responseText,
bits = nextts.split('|'),
i;
for (i = 0; i < currentts.length; ++i)
if (currentts[i] !== bits[i])
currentts[i] = bits[i], postMessage(i + '|' + bits[i]);
--in_progress;
}
if ('onload' in xhr) // modern browser
xhr.addEventListener('load', ready);
else // ancient browser
xhr.onreadystatechange = function () {
if (this.readyState === 4 && xhr.status === 200)
ready.call(this);
};
// listen for error, too?
// begin request
xhr.send();
}

Calling to ajax multiple times from javascript

for(var x=0 ; x<=23 ; x++)
{
AjaxRequest16 = null;
AjaxRequest16 = getXmlHttpRequestObject(); // method here to load the request
if(AjaxRequest16.readyState == 4 || AjaxRequest16.readyState == 0)
{
AjaxRequest16.open("GET", "ajax.php?id=16&AreaID=" +encodeURIComponent(AreaID)+ "&month="
+encodeURIComponent(document.getElementById("cboMonths").value)+ "&TimeSlot=" +encodeURIComponent(x), true);
AjaxRequest16.send(null);
AjaxRequest16.onreadystatechange = function()
{
if(AjaxRequest16.readyState == 4)
{
var innerHTML = AjaxRequest16.responseText.toString();
/* Retrieve data from the server and display. */
document.getElementById("divTime"+x).innerHTML = innerHTML;
}/* end if */
}/* end function */
}/* end if */
}/* end if */
I'm trying to call ajax multiple times to load data in a set of divs: 24 of them, they start with divTime0, divTime1, divTime2, divTime3...... divTime23. Each time its called, the value for the TimeSlot corresponds with the div e.g. TimeSlot=0 goes in divTime0.
I know the ajax calls here are overriding each other but have no idea how to solve it without writing out 24 blocks of code to get it working. N.B. this is working if i execute singularly without the for loop but it will just populate 1 of the 24 divs
The following code worked to load 24 divs with images:
for(var x=0 ; x<=23 ; x++)
document.getElementById("timeanalysisimg"+x).src="ajax.php?id=15&AreaID=" +encodeURIComponent(AreaID);
I'm trying to do something similar without having to write unnecessary code. Any ideas?
I got it working. Just pasting the solution
for(var x=0 ; x<=9 ; x++)
{
test(x, AreaID); // calling the function which resides externally to the loop
}
An external method:
function test(x, AreaID)
{
var AjaxRequest16 = null;
AjaxRequest16 = getXmlHttpRequestObject();
if(AjaxRequest16.readyState == 4 || AjaxRequest16.readyState == 0)
{
AjaxRequest16.open("GET", "ajax.php?id=16&AreaID=" +encodeURIComponent(AreaID)+ "&month="
+encodeURIComponent(document.getElementById("cboMonths").value)+ "&TimeSlot=" +encodeURIComponent(x), true);
AjaxRequest16.send(null);
AjaxRequest16.onreadystatechange = function()
{
if(AjaxRequest16.readyState == 4)
{
var innerHTML = AjaxRequest16.responseText.toString();
/* Retrieve data from the server and display. */
document.getElementById("divTime"+x).innerHTML = innerHTML;
}
}
}
}
Put the block into a function:
for(var x=0 ; x<=23 ; x++)
{
(function(x) {
var AjaxRequest16 = getXmlHttpRequestObject();
//rest of the code
}(x));
} //end of for loop
you can do something like:
for(var x=0 ; x<=23 ; x++)
{
req(x);
}
function req(x){
var AjaxRequest16 = null;
AjaxRequest16 = getXmlHttpRequestObject(); // method here to load the request
if(AjaxRequest16.readyState == 4 || AjaxRequest16.readyState == 0)
{
AjaxRequest16.open("GET", "ajax.php?id=16&AreaID=" +encodeURIComponent(AreaID)+ "&month="
+encodeURIComponent(document.getElementById("cboMonths").value)+ "&TimeSlot=" +encodeURIComponent(x), true);
AjaxRequest16.send(null);
AjaxRequest16.onreadystatechange = function()
{
if(AjaxRequest16.readyState == 4)
{
var innerHTML = AjaxRequest16.responseText.toString();
/* Retrieve data from the server and display. */
document.getElementById("divTime"+x).innerHTML = innerHTML;
}/* end if */
}/* end function */
}/* end if */
}
I changed all the code, but it does exactly what you want, without using asynchronous = false, and browser freezing:
function ajaxRequest(url, callback) {
var req = null;
if (window.XMLHttpRequest) req = new XMLHttpRequest();
else if (window.ActiveXObject) // if IE
{
try {
req = new ActiveXObject("Msxml2.XMLHTTP")
} catch (e) {
try {
req = new ActiveXObject("Microsoft.XMLHTTP")
} catch (e) {}
}
} else {
throw ("No Ajax support!");
return;
}
req.open('GET', url, true);
req.onreadystatechange = function () {
if (req.readyState == 4) {
if (typeof (callback) == "function") callback(req);
}
};
req.send(null);
return req;
}
function loadMyData() {
var x = parseInt(arguments[0]);
if (x > 23) {
alert("all 24 is loaded!");
}
var url = "ajax.php?id=16&AreaID=" + encodeURIComponent(AreaID) +
"&month=" + encodeURIComponent(document.getElementById("cboMonths").value) +
"&TimeSlot=" + encodeURIComponent(x);
var callback = Function('req', 'document.getElementById("divTime' + x + '").innerHTML =' +
' req.responseText;' +
'loadMyData(' + x + ');');
ajaxRequest(url, callback);
}
loadMyData(0);
you should make your ajax calls asenkron false try this:
for(var x=0 ; x<=23 ; x++)
{
AjaxRequest16 = null;
AjaxRequest16 = getXmlHttpRequestObject(); // method here to load the request
if(AjaxRequest16.readyState == 4 || AjaxRequest16.readyState == 0)
{
AjaxRequest16.open("GET", "ajax.php?id=16&AreaID=" +encodeURIComponent(AreaID)+ "&month="
+encodeURIComponent(document.getElementById("cboMonths").value)+ "&TimeSlot=" +encodeURIComponent(x), false);
AjaxRequest16.send(null);
AjaxRequest16.onreadystatechange = function()
{
if(AjaxRequest16.readyState == 4)
{
var innerHTML = AjaxRequest16.responseText.toString();
/* Retrieve data from the server and display. */
document.getElementById("divTime"+x).innerHTML = innerHTML;
}/* end if */
}/* end function */
}/* end if */
}/* end if */
Sequentially load content with ajax
here is a simple ajax function for modern browsers (chrome,safari,ie10,android,ios)
function ajax(a,b,c){//url,function,just a placeholder
c=new XMLHttpRequest;
c.open('GET',a);
c.onload=b;
c.send()
}
and this is how you load content sequentially
var current=0,
x=23;
function handler(){
document.getElementById("divTime"+current).innerHTML=this.response;
current++
if(current<x){
ajax('url.php?id='+current,handler)
}
}
ajax('url.php?id='+current,handler);
this way you don't overwrite previous ajax calls.
Multiple simultaneous ajax calls is a bad solution.
anyway to achieve multiple ajax calls at the same time you need to create multiple ajax request functions.
var ajaxcall=[];
ajaxcall[0]=new XMLHttpRequest;
ajaxcall[0].CUSTOMID=0;
ajaxcall[0].open('GET','url.php?id='+0);
ajaxcall[0].onload=function(){console.log(this.CUSTOMID,this.response)};
ajaxcall[0].send();
What it boils down to is the asynchronous nature of Ajax calls.
Each Ajax context must be kept alive until the request is over (completion or failure).
In your initial code, you use only one Ajax request context. The loop launches the first request, but then overwrites its context immediately with the second one long before the first was processed. When the server responds (a few milliseconds later), there is no handler left on the browser side to process the response (except for the 24th one).
What your workaround does is to create a different context and callback for each request, since your global function stores them in different closures.
However, as a result you will fire a hail of 24 Ajax requests simultaneously on the server, which is likely to cause unnecessary overhead, or even crashes if your PHP script does not expect to execute concurrently on the same request. Besides, synchronizing your code on completion of these requests will not be easy.
Here is what I use for my own apps :
// --------------------------------------------------------------------
// Ajax lite
// --------------------------------------------------------------------
function PageCache (target, void_contents)
{
this.text = {};
this.req = {};
this.void_contents = void_contents || 'void';
this.target = target;
}
PageCache.prototype = {
// synchronous load
load: function (page)
{
if (!this.text[page]) this.text[page] = this._launch_request (page);
return this.text[page];
},
// asynchronous load
fetch: function (page, action)
{
if (this.text[page])
{
action (this, page);
return;
}
if (this.req[page]) return;
this.req[page] = this._launch_request (page,
function(_this, _page, _action) {
return function(){
_this._loader(_page,_action);
};
}(this,page,action));
},
_launch_request: function (page, callback)
{
var req;
try {
req = window.XMLHttpRequest
? new XMLHttpRequest()
: new ActiveXObject("Microsoft.XMLHTTP");
}
catch (e) {}
req.open('GET', this.target.replace (/\$/, page), callback!=undefined);
if (callback) req.onreadystatechange = callback;
req.send(null);
return callback ? req : this._get_result (req);
},
_get_result: function (req)
{
return (req.status < 400)
? req.responseText
: this.void_contents;
},
_loader: function (page, action)
{
if (!this.req[page] || (this.req[page].readyState != 4)) return;
this.text[page] = this._get_result (this.req[page])
delete this.req[page];
if (action) action (this.text[page], page);
}
}
In your example, you could use it like so:
First, a bit of cleanup :
function compute_id (AreaID,x) {
return "id=16&AreaID="
+ encodeURIComponent(AreaID)
+ "&month="
+ encodeURIComponent(document.getElementById("cboMonths").value)
+ "&TimeSlot="
+ x; // I suspect all the encodeURIComponent() calls are unnecessary
}
var Ajax = new PageCache (
'ajax.php?$', // '$' will be replaced with fetch() parameter
'error loading data'); // contents in case of request failure
1) simultaneous requests (not recommended)
for (var x = 0; x != 24 ; x++) {
// store current x value in a closure
var request_done = function (_x) {
return function (responseText) {
document.getElementById("divTime"+_x).innerHTML = responseText;
}}(x);
}
Ajax.fetch (compute_id (AreaID,x), request_done);
}
2) sequential blocking requests (very bad, don't do it unless your code cannot proceed without the data)
for (var x = 0; x != 24 ; x++) {
document.getElementById("divTime"+x).innerHTML =
Ajax.load (compute_id (AreaID,x));
}
3) sequential non-blocking requests
var AjaxCtx = { x:0, max:24};
// launch first request
Ajax.fetch (compute_id (AreaID, AjaxCtx.x), request_done);
function request_done (responseText) {
document.getElementById("divTime"+AjaxCtx.x).innerHTML = responseText;
// request completion triggers the next
if (++AjaxCtx.x != AjaxCtx.max)
Ajax.fetch (compute_id (AreaID,AjaxCtx.x), request_done);
}

Unexpected } tokens in js - trying to learn ajax

I'm just trying to learn some ajax so I wrote some code for basically an address book to pull some data. My javascript is rubbish but I cannot seem to understand what I am doing wrong, the error points to function ajaxCall but I see no issue with that function either:
(function () {
var searchForm = document.getElementById("search-form"),
searchField = document.getElementById("q"),
getAllButton = document.getElementById("get-all"),
target = document.getElementById("output");
var addr = {
search: function (event) {
var output = document.getElementById("output");
//start ajax call
ajaxCall("data/contacts.json", output, function (data) {
var searchValue = searchField.value,
addrBook = data.addressBook,
count = addrBook.length,
i;
//stop default behavior
event.preventDefault();
//clear target
target.innerHTML = "";
if (count > 0 && searchValue !== "") {
for (i = 0; i < count; i++) {
var obj = addrBook[i],
isItFound = obj.name.indexOf(searchValue);
if (isItFound !== -1) {
target.innerHTML += '<p>' + obj.name + ', ' + obj.email + '<p>';
} //end if isItFound
} //end for loop
} //end if count check
}); //end ajax call
}, //end method search
getAllContacts: function () {
var output = document.getElementById("output");
ajaxCall("data/contacts.json", output, function (data) {
var addrBook = data.addressBook,
count = addrBook.length,
i;
target.innerHTML = "";
if (count > 0) {
for (i = 0; i < count; i++) {
var obj = addrBook[i];
target.innerHTML += '<p>' + obj.name + ', ' + obj.email + '<p>';
} //end for loop
} //end if
}); //end ajax call
}, //end method getAllContacts
setActiveSection: function () {
this.parentNode.setAttribute("class", "active");
}, //end method setActiveSection
removeActiveSection: function () {
this.parentNode.removeAttribute("class");
}, //end method removeActiveSection
addHoverClass: function () {
searchForm.setAttribute("class", "hovering");
}, //end method addHoverClass
removeHoverClass: function () {
searchForm.removeAttribute("class");
} //end method removeHoverClass
} //end addr object
searchField.addEventListener("keyup", addr.search, false);
searchField.addEventListener("focus", addr.addActiveSection, false);
searchField.addEventListener("blur", addr.removeActiveSection, false);
getAllButton.addEventListener("click", addr.getAllContacts, false);
searchForm.addEventListener("submit", addr.search, false);
searchForm.addEventListener("mouseover", addr.addHoverClass, false);
searchForm.addEventListener("mouseout", addr.removeHoverClass, false);
})(); //end anon function
function getHTTPObject() {
var xhr;
//in most cases this first if is executed
if (window.XMLHttpRequest) {
xhr = new XMLHttpRequest();
}
//otherwise support crappy IE6 and below
else if (window.ActiveXObject) {
xhr = new ActiveXObject("Msxml2.XMLHTTP");
}
return xhr;
}
function ajaxCall(dataUrl, outputElement, callback) {
//get ajax object
var request = getHTTPObject();
outputElement.innerHTML = "Loading...";
request.onreadystatechange = function () {
if (request.readyState === 4 && request.status === 200) {
//good ajax response..now save it
var contacts = JSON.parse(request.responseText);
if (typeof callback === "function")
callback(contacts);
} //end upper if
} //end onreadystatechange
request.open("GET", dataUrl, true);
request.send(null);
}
The javascript development tools keeps giving me an unexpected token } on line 97 but that changes all so often. Am I missing a curly brace somewhere?
I did put your code to this fiddle and fixed the errors as far as i can.
You missed some curly braces and semicolons. Also, you used ajaxCall() and getHTTPObject() before they were declared. Check it out. Unfortunately, i dont know if the problem is already fixed, but now the code is valid at least :)
Btw: (in my opinion) such long Code-Samples are always better pasted into a fiddle. Not only because you can focus on the probably messy code here while referring to the complete code sample somewhere else, also because you can make sure that there are no syntax-errors as you can quickly validate you code using jsLint before asking the question here.
You must re-check what your JSON response is, in console, and see if it is invalid.
Because at that very 97 line you say that you are parsing a response.

Categories