How to access method variables from within an anonymous function in JavaScript? - javascript

I'm writing a small ajax class for personal use. In the class, I have a "post" method for sending post requests. The post method has a callback parameter. In the onreadystatechange propperty, I need to call the callback method.
Something like this:
this.requestObject.onreadystatechange = function() {
callback(this.responseText);
}
However, I can't access the callback variable from within the anonomous function. How can I bring the callback variable into the scope of the onreadystatechange anonomous function?
edit:
Here's the full code so far:
function request()
{
this.initialize = function(errorHandeler)
{
try {
try {
this.requestObject = new XDomainRequest();
} catch(e) {
try {
this.requestObject = new XMLHttpRequest();
} catch (e) {
try {
this.requestObject = new ActiveXObject("Msxml2.XMLHTTP"); //newer versions of IE5+
} catch (e) {
this.requestObject = new ActiveXObject("Microsoft.XMLHTTP"); //older versions of IE5+
}
}
}
} catch(e) {
errorHandeler();
}
}
this.post = function(url,data,callback)
{
var response;var escapedData = "";
if (typeof data == 'object') {
for (i in data) {
escapedData += escape(i)+'='+escape(data[i])+'&';
}
escapedData = escapedData.substr(0,escapedData.length-1);
} else {
escapedData = escape(data);
}
this.requestObject.open('post',url,true);
this.requestObject.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
this.requestObject.setRequestHeader("Content-length", data.length);
this.requestObject.setRequestHeader("Connection", "close");
this.requestObject.onreadystatechange = function()
{
if (this.readyState == 4) {
// call callback function
}
}
this.requestObject.send(data);
}
}

Just pass the callback function together with the rest of the arguments
this.post = function(url, data, callback) {
...
this.requestObject.onreadystatechange = function() {
if (this.readyState == 4) {
callback(this.responseText);
}
};
...
}
And then
foo.post("foo.html", {foo:"bar"}, function(result){
alert(result);
});
By the way, this is a better way to convert the data into a proper string
var q = [];
for (var key in data) {
if (data.hasOwnProperty(key)) {
q.push(key + "=" + encodeURIComponent(data[key]));
}
}
data = q.join("&"); //data can now be passed to .send()
encodeURIComponent is the proper function to use here as encode will not escape data properly
If you want to get a ready made function for all of this you can take a look here http://github.com/oyvindkinsey/easyXDM/blob/master/src/easyXDM.js#L358

var that = this;
Then use that instead of this inside the anonymous function.

If callback is a variable in the containing function, it should be in scope. If it is not a variable, but is in scope in in the containing function, you may have to do something like
var cb = callback;
var xhrRequest = this;
then
cb(xhrRequest.responseText);

Related

Understanding XHR request object in javascript... (confused)

I'm following a simple book and It says:
function createRequest()
{
try
{
request = new XMLHttpRequest();
}
catch (tryMS)
{
try
{
request = new ActiveXObject("Msxml2.XMLHTTP");
}
catch (otherMS)
{
try
{
request = new ActiveXObject("Microsoft.XMLHTTP");
}
catch (failed)
{
request = null;
}
}
}
return request;
}
function getDetails(itemName)
{
var request = createRequest();
if (request==null)
{ alert("Unable to create request");
return;
}
var url= "getDetails.php?ImageID=" + escape(itemName);
request.open("GET",url,true);
request.onreadystatechange = displayDetails;
request.send(null);
}
function displayDetails()
{
if (request.readyState == 4)
{
if (request.status == 200)
{
detailDiv = document.getElementById("description");
detailDiv.innerHTML = request.responseText;
}
}
}
And all this code above is fine and it's okay to me.. but after few pages it says:
ITS VERY IMPORTANT TO REMOVE VAR KEYWORD BEFORE request VARIABLE so the callback can reference the variable...
but how come in example above it worked? is it coincidence if we call a variable 'request' that it will map with global variable in a createRequest method?
Take a look on image below:
Why is this happening ? in one example var before request variable is used and everything is fine, in another var is avoided so the method in callback might access it.. but how come method in a callback is accessing a request variable in first example...
It's confusing because there are 2 similar examples, with different explanations..
EDIT
P.S it says request has to be a global ? :o
Thanks guys
Cheers
In both examples, implicit global variables are created so they can be shared with the callback.
When the second request variable is created, it creates a local variable inside the getDetails function. So when createRequest() returns the global variable, the local variable becomes a reference to it.
This is rather bad advice and shows a lack of understanding on the writers' part. But it seems to be an old text, since activeX objects are deprecated by now, so maybe globals used to be less frowned upon. The proper way is to either send the responseText or responseXML as a parameter to the callback or send the entire request as the parameter for the callback.
Maybe the writer didn't want to make the request code more complex, but imho, this is not a good way to teach people things.
function createRequest( method, url, callback, payload ) {
var request = new XMLHttpRequest();
if ( !request ) {
alert( "Unable to create request" );
return null;
}
request.open( method, url );
request.onreadystatechange = function() {
if (request.readyState === 4 && request.status === 200 ) {
callback( request.responseText );
}
};
request.send( payload );
};
function getDetails( itemName, callback ) {
createRequest( "GET", "getDetails.php?ImageID=" + escape(itemName), callback, null );
};
function displayDetails( detail ) {
var detailDiv = document.getElementById("description");
detailDiv.innerHTML = detail;
};
getDetails( "someItemName", displayDetails );
you are right, in your first example, function createRequest is not using var, which mean you are creating a global variable request when excute request = new XMLHttpRequest();.
We should avoid using gobal var in most situation.
function createRequest() {
try {
// add var so it's not global variable
var request = new XMLHttpRequest();
} catch (tryMS) {
try {
request = new ActiveXObject("Msxml2.XMLHTTP");
} catch (otherMS) {
try {
request = new ActiveXObject("Microsoft.XMLHTTP");
} catch (failed) {
request = null;
}
}
}
return request;
}
function getDetails(itemName)
{
var request = createRequest();
if (request==null)
{ alert("Unable to create request");
return;
}
var url= "getDetails.php?ImageID=" + escape(itemName);
request.open("GET",url,true);
// create anonymous function to call your callback and pass `request` as local variable
request.onreadystatechange = function(){
displayDetails(request);
};
request.send(null);
}
function displayDetails(request)
{
if (request.readyState == 4)
{
if (request.status == 200)
{
detailDiv = document.getElementById("description");
detailDiv.innerHTML = request.responseText;
}
}
}

Get data from PHP to Javascript depending on Javascript variable value

I have variable _randNum in JavaScirpt which generate random number between 1 and 50.
I need to select some data from database depending on value of this variable.
I've used following JavaScript code to select data from database via PHP (but without sending JavaScript variable to PHP).
// handles the click event for link 1, sends the query
function getSuccessOutput() {
getRequest(
'questions.php', // demo-only URL
drawOutput,
drawError
);
return false;
}
// handles drawing an error message
function drawError () {
var container = document.getElementById('output');
container.innerHTML = 'Bummer: there was an error!';
}
// handles the response, adds the html
function drawOutput(responseText) {
$.getJSON("questions.php", function(theObject){
var d1 = theObject.data1; // Get the data from PHP
var d2 = theObject.data2;
}
// helper function for cross-browser request object
function getRequest(url, success, error) {
var req = false;
try{
// most browsers
req = new XMLHttpRequest();
} catch (e){
// IE
try{
req = new ActiveXObject("Msxml2.XMLHTTP");
} catch (e) {
// try an older version
try{
req = new ActiveXObject("Microsoft.XMLHTTP");
} catch (e){
return false;
}
}
}
if (!req) return false;
if (typeof success != 'function') success = function () {};
if (typeof error!= 'function') error = function () {};
req.onreadystatechange = function(){
if(req .readyState == 4){
return req.status === 200 ?
success(req.responseText) : error(req.status)
;
}
}
req.open("GET", url, true);
req.send(null);
return req;
}
How can I send _randNum variable's value to PHP?
In PHP I could do something like that:
$rnd = mysqli_real_escape_string($con, $_POST['randNum']);
But no clue, how to send It from Javascript in same function.
I could create function like this:, but how to use It correctly with getSuccessOutput() function?
function sendFtId() {
$.post( "questions.php", { randNum : _randNum })
.done(function( data ) {
});
}
Have you any ideas?
You can use isset function to check any post request is coming or not.
E.g:
function getSuccessOutput($randNum) {
echo $randNum;
}
if(isset($_POST['randNum'])){
getSuccessOutput($_POST['randNum']);
}

Async xhr and callback

I have a problem with waiting for DOM elems to exist.
First of all, I make an XHR to my backend and get some info from there:
$(document).ready(function() {
var searchParam, searchStr;
// some values to vars
loadTags(15,highlightAndSearchTags(searchParam,searchStr));
});
The functions are here:
function highlightAndSearchTags(searchParam, searchStr) {
if (searchParam == 'tags') {
var selectedTags = searchStr.split(',');
console.log($("#my_favorite_latin_words").children().length); // sometimes returns 0, sometimes returns number of <span> in the div (see loadTags())
for (var i = 0; i < selectedTags.length; i++) {
$("#" + selectedTags[i]).toggleClass("tag-selected");
}
}
}
function loadTags(showedTagsLength, callback) {
var xhr = new XMLHttpRequest();
xhr.open('GET', apiUrl + "tags/", true);
xhr.withCredentials = true;
xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if (xhr.status != 200) {
console.log(xhr.responseText);
}
else {
tagList = JSON.parse(xhr.responseText);
tagList = tagList.results;
for (var i = 0; i < showedTagsLength; i++) {
$("#my_favorite_latin_words").append("<span id=\'" + tagList[i].tag_pk + "\'>" + tagList[i].name + "</span>");
}
}
setTimeout(callback, 1); //found this trick somewhere on stackoverflow
}
};
xhr.send();
}
As you can see there is a callback which is executed after 1ms timeout (I found this trick somewhere on stack a while ago), but then another function does not see the appended elements from time to time.
I have also tried
callback.call()
with no luck so far.
Can anybody advise how to wait for the elements correctly in this case?
loadTags(15,function(searchParam,searchStr){highlightAndSearchTags(searchParam,searchStr)});
As multiple comments already mentioned, you have to wrap it into a function so that it isnt called when you call the loadTags function
You are not passing any callback function. You are immediately invoking the function and passing the returned value of highlightAndSearchTags function which is undefined.
An anonymous function can be created and passed as
loadTags(15,function(){
highlightAndSearchTags(searchParam,searchStr)
});
loadTags(15,highlightAndSearchTags(searchParam,searchStr));
This code will execute your function highlightAndSearchTags immediately and the result value will be sent instead of your callback, if you want to use it as a callback, you need to only pass the function name like:
loadTags(15, highlightAndSearchTags);
If you need to pass your searchParam and searchStr parameters, add them as parameters:
loadTags(15, highlightAndSearchTags, searchParam, searchStr);
When your tags are loaded, you can directly call your callback with the searchParam and searchStr parameters you added to your loadTags function:
function loadTags(showedTagsLength, callback, searchParam, searchStr) {
var xhr = new XMLHttpRequest();
xhr.open('GET', apiUrl + "tags/", true);
xhr.withCredentials = true;
xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if (xhr.status != 200) {
console.log(xhr.responseText);
}
else {
tagList = JSON.parse(xhr.responseText);
tagList = tagList.results;
for (var i = 0; i < showedTagsLength; i++) {
$("#my_favorite_latin_words").append("<span id=\'" + tagList[i].tag_pk + "\'>" + tagList[i].name + "</span>");
}
}
callback(searchParam,searchStr);
}
};
xhr.send();
}
Another approach could also be to wrap your callback in an self-executing anonymous function. This will prevent the highlightAndSearchTags to be executed immediately so you can call it later when your tags are loaded:
loadTags(15, function() { highlightAndSearchTags(searchParam, searchStr); });

Simple XMLHttpRequest function to return a JSON object

I'm trying to return a json object following an XMLHttpRequest get request, and I come up short. I think that might be because it is asynchronous, but I really can't put my finger on how to make it work. What am I doing wrong?
$(document).ready(function() {
var apiEndpoint = 'http://someapiendpoint.com/'
//Helpers
function sendRequest(_path) {
var results = {}
req = new XMLHttpRequest()
req.open('GET', apiEndpoint+_path)
req.onreadystatechange = function() {
if (this.readyState === 4) {
results = JSON.parse(this.response)
}
}
req.send()
return results
}
// Action
console.log(sendRequest('client1/'))
}); // end document ready
You should use this construction
function sendRequest(_path, cb) {
req = new XMLHttpRequest()
req.open('GET', apiEndpoint+_path);
req.onreadystatechange = function() {
if (this.readyState === 4) {
cb(JSON.parse(this.response));
}
else{
cb(null);
}
}
req.send();
}
// Action
sendRequest('client1/', function(result){
console.log(result);
})
For asynchronous calls you need to use call backs
Since you are already using jQuery you can do the following:
$(document).ready(function() {
var apiEndpoint = 'http://someapiendpoint.com/';
function sendRequest(path, callback){
$.get(apiEndpoint+path, function(response){
callback(JSON.parse(response));
}, json).fail(function(){
console.log('Failed');
});
}
sendRequest('client1/', function(json){
if(json){
console.log(json);
}
});
});

Multiple xmlhttprequest on a page

I am having an error when using new XMLHttpRequest() for the second time in JavaScript code called from textbox event on page.
My JavaScript finds suggestions for text entry from the SQL to do that I use xmlhttprequest, it does fine when it is the first time but when I keep typing in the text box I receive:
"typeerror: xmlhttprequest not a costructor"
(this error happens only in Firefox)
This is my code:
function fnNull() { };
function changeofstate(){
if (XMLHttpRequest.readyState == 4)
{
whatever ;
}
XMLHttpRequest.onreadystatechange = fnNull();
}
function whentextchange(){
var WebURL = "the url here ";
XMLHttpRequest = CreateXmlHttpObject(changeOfState);
XMLHttpRequest.open("GET", WebURL, true);
XMLHttpRequest.send(null);
XMLHttpRequestt.abort();
}
}
function CreateXmlHttpObject(handler) {
var objXmlHttpReq = null;
var Req = null;
if (navigator.userAgent.indexOf("Opera")>=0)
{
return ;
}
if (navigator.userAgent.indexOf("MSIE")>=0)
{
var strName="Msxml2.XMLHTTP";
if (navigator.appVersion.indexOf("MSIE 5.5")>=0)
{
strName="Microsoft.XMLHTTP";
}
try
{
objXmlHttpReq=new ActiveXObject(strName);
objXmlHttpReq.onreadystatechange = handler;
return objXmlHttpReq;
}
catch(e)
{
return ;
}
}
if (navigator.userAgent.indexOf("Mozilla") >= 0) {
try
{
if (Req == null) {
Req = new XMLHttpRequest();
}
Req.onload = handler;
Req.onerror = handler;
return Req;
}
catch (e) {
alert(e);
alert(Req.responseText)
alert(e);
return;
}
}
}
You should name your request object something else than XMLHttpRequest. It might override the XMLHttpRequest object in the browser. Thus giving you the error.
XMLHttpRequest = CreateXmlHttpObject(changeOfState);
Assigning XMLHttpRequest variable like this is actually using global scope. You should use var and another variable name
var req = CreateXmlHttpObject(changeOfState);
Hope this clarifies.

Categories