For debugging, I'm using Google Chrome. I don't know what I'm doing wrong in my code... all I'm doing is looking for the ready state to change. It's throwing the following error:
TypeError: Property 'onreadystatechange' of object # is not a function
Here's the code:
function startup() {
// For fetching without refresh
// Define new connection
var connect_to = new XMLHttpRequest();
// Open it with the necessary details
connect_to.open('GET', 'light.php?function_id=2');
// Send it
connect_to.send();
// Now, when it comes back, evaluate it for it's ready state and status (readyState equalling 4 and status equalling 200)
connect_to.onreadystatechange(function () {
if (connect_to.readyState == 4 && connect_to.status == 200) {
// Declare where this is going to be put
var plant_into = document.getElementById('intendedContent').innerHTML;
// Put the recieved text into it
plant_into = connect_to.responseText;
}
})
}
It is event so you have to assign function to it, it can have multiple functions attached:
connect_to.onreadystatechange = function (){body};
This () is operator for calling functions, if you put it after something it will try to run function with such name. If you do Foo() then it will try to find Foo and if it will not find it then it will be error. So your usage of ready state changed look like you want to call method passing function to it as a parameter.
Related
I can console.log(arg) but not arg.response. Curious what is the reason behind // edit: when I console log arg I can clearly see response property with data
function doAjax(param, lambda) {
let _xhttp = new XMLHttpRequest();
_xhttp.open("GET", param, true);
_xhttp.send();
_xhttp.onload = lambda(_xhttp);
}
doAjax("ALL_FRAMES", function(arg) {
console.log(arg.response); --> prints blank field in console
//allFrames = JSON.parse(arg.response);
});
_xhttp.onload = lambda(_xhttp);
This means "call lambda(_xhttp) immediately, and then assign its return value to _xhttp.onload". You instead want to assign a function to onload, and that function will call lambda later on:
_xhtttp.onload = () => lambda(_xhttp);
// edit: when I console log arg I can clearly see response property with data
That is just a quirk of the way the dev tools handle logging objects. When logging an object, the object is not evaluated until you click on the object in your dev tools. So at the time the console.log ran it didn't have the response yet, but by the time you click, it does have it.
I am refactoring javascript and have a lot of similar POST calls to the same PHP url.
It would be nice to pass the postdata and callback function (defined in JiraChangeStatus), to a common function (SendPost).
I'm new to javascript, and was under the impression that it was possible to use a pointer / reference if the variable was the property of an object.
Have also tried using a variable for "xhr" instead of an object property, declaring "xhr" within JiraChangeStatus instead of SendPost, and even declaring it globally as a sanity check.
function JiraChangeStatus(index) {
var postdata = "&status="+document.getElementById("jiraT"+(index).toString()).value;
SendPost("changestatus.php",postdata, function(obj) {
alert("readyState: "+obj.xhr.readyState+"\r\nstatus: "+obj.xhr.status);
});
}
function SendPost(module,postdata,fn)
{
var obj = {xhr: new XMLHttpRequest()}
var issuekey = GetJson(document.getElementById("issue").value,'key');
obj.xhr.open("POST", "modules/jira/"+module, true);
obj.xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
obj.xhr.onreadystatechange = fn(obj);
obj.xhr.send("username=user&issuekey="+issuekey+postdata);
}
When the callback function is executed I always see readystate 1 and status 0. I expect to see 4 and 200.
It appears javascript is passing a copy of the xhr object to the callback rather than the actual object.
These functions work when merged. Unless the properties of "xhr" are set within the scope of the callback function for "xhr", the callback doesn't get the value.
Please let me know what I'm doing wrong.
Thanks to Pointy and Bergi.
When it comes to solving my user story, there were 2 problems with the code.
The first was that when I used obj.xhr.onreadystatechange = fn(obj), it instantly evaluated the fn. That instant evaluation caused "this" to have a mouse click as the event trigger, rather than onreadystatechange.
The second was redundancy. There was no need to pass xhr as a parameter when "this" references xhr.
This code doesn't work (irrelevant lines omitted):
function JiraChangeStatus(index) {
SendPost("changestatus.php",postdata, function(pass) {
console.log("This:\r\n"+this); //This: [object Window]
});
}
function SendPost(module,postdata,fn) {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = fn();
}
This code works fine (irrelevant lines omitted):
function JiraChangeStatus(index) {
SendPost("changestatus.php",postdata, function(pass) {
console.log("This:\r\n"+this); //This: [object XMLHttpRequest]
});
}
function SendPost(module,postdata,fn) {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = fn; //no parentheses here fixed my use case
}
Therefore I would accept Pointy and Bergi's comments as they solved my use case.
However, the question I posted was about passing a reference into a callback function and I want to give useful info to people who find it in a search.
The answer to my question of how to pass reference to a callback when parentheses cause immediate evaluation was here: How can I pass a parameter to a function without it running right away?
To validate that it "worked" for my use case I wrote some really ugly and unnecessary code which shows that you can pass a parameter into a callback function with parenthesis by simply having its immediate evaluation return a function.
Since JavaScript allows objects to be assigned by reference, and "xhr" is an object, as Bergi said I would not have needed an object wrapper (irrelevant lines omitted):
function SendPost(module,postdata,fn)
{
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = fn(xhr); //immediately evaluates fn
}
function JiraChangeStatus(index) {
SendPost("changestatus.php",postdata, function(pass) {
//the result of immediate evaluation is a function
//with the parameter "pass" in its scope
return function() {
console.log("This:\r\n"+this); //This: [object XMLHttpRequest]
console.log(this.responseText); //returns the expected response text
console.log("Passed Reference:\r\n"+pass); //Passed Parameter: [object XMLHttpRequest]
console.log(pass.responseText);
//Both of these stop future callbacks
//this.abort();
//pass.abort();
}
});
}
So I want to call a function Local.getThis onload with this code:
class Local {
getThis() {
let that;
if (localStorage.getItem('that') === null) {
that = [];
console.log(that);
localStorage.setItem('that', that);
} else {
that=JSON.parse(localStorage.getItem('that'));
console.log(that);
}
}
// DOM Load Event
document.addEventListener('DOMContentLoaded', Local.getThis)
But nothing is happening, no error nothing. But when I change "getThis" to STATIC it works (Output: []). Does it need to be STATIC ??
P.S.
After setting that = []; I get an error
'Uncaught SyntaxError: Unexpected end of JSON input
at JSON.parse (<anonymous>)
at HTMLDocument.getThis'
on the next reload but that is probably a whole other problem I guess.
EDIT:
For the record the error was related to localStorage.setItem('that', that);, it should be ofcourse localStorage.setItem('that', JSON.stringify(that));
part 1: this is a reserved word in JavaScript, change parameter's name.
part 2: Local is a class, so to access a function directly from it that function must be static. Otherwise an instance needs to be initiated first. and the function can then be used from that instance.
But nothing is happening, no error nothing. But when I change
"getThis" to STATIC it works (Output: []). Does it need to be STATIC
??
To call your method as Local.getThis need to be static.
Otherwise you need to create an instance of Local.
var local = new Local();
document.addEventListener('DOMContentLoaded', function() {
local.getThis();
})
You should create an instance of Local class first to have this
const local = new Local();
document.addEventListener('DOMContentLoaded', local.getThis.bind(local))
//or
document.addEventListener('DOMContentLoaded', function() {
local.getThis();
})
Update
Also you cannot use variable name this as it is reserved word.
If you don't need a context (this) you can create a method as a static method:
class Local{}
Local.getThis = function() {
// code without this
}
so then you can use as you've written:
document.addEventListener('DOMContentLoaded', local.getThis);
I have a javascript function that uses an ajax function to post to a php script and a variable used to determine if the result comes back as true or false. In php, I would normally assign the function to a variable such as this:
$insert_item = $process->insert_item($item_array);
If ($insert_item->error)
{
//error detected, do something
}
I have not been able to accomplish this with javascript. Instead, I get a [object object] return if I assign a function to a variable. As a cheap alternative, I am trying to use a global variable to write any errors:
var error = false;
function update_db(formInput) {
$.post(action.php, formInput, function(data) {
if (data != 0) {
error = true
}
});
return error;
}
var updateDb = update_db(form_data);
if (updateDb) {
alert("error detected");
In this example, 'error' comes back as false despite the ajax function updating it to true. I have read all about javascript hoisting, but have yet to find a solution. Is there anyway around this? My problem stems completely from the ajax function which I have also tried accessing directly to return any vars (like I easily do in PHP) but I have had no luck!
As a side note, I find it interesting that I can access 'error' within the ajax function (returns as false) but not able to change it.
I have a script with the following structure:
Test = {
CONSTANTS : {},
VARIABLES : {},
MARKUP : {},
FUNCTIONS : {
init : function () {
// Access variable from different namespace
var all_constants = DifferentNamespace.CONSTANTS; // WORKS
var tester = DifferentNamespace.CONSTANTS.chunk_of_markup; // SAYS UNDEFINED
}
},
init : function () {
// Call real init() function
$(document).ready(function () {
Test.FUNCTIONS.init();
});
}
};
$(document).ready(function () {
Test.init();
});
If I remove either of the $(document).ready(..) function calls, when I try to access a constant from a different namespace it is undefined; with both is works well.
As you can see I'm using two init() functions, one it just to neaten up the call to init because I have wrapped functions inside an additional object.
If I remove the function that is on the same level as CONSTANTS, VARIABLES etc and try to call the init() within Test.FUNCTIONS it still does not work.
Edit:
If i console.log(all_constants) I get the full object (with .chunk_of_markup) but if I console.log(tester) is get undefined. If i wrap tester i get []
I should also note that the other namespace gets the markup from a seperate file.
Any ideas why?
Having two document ready doesn't make a difference here. You could have one document.ready and/or call Test.FUNCTIONS.init directly and all should work, and the fact that they are in different namespaces doesn't matter as well.
As for why you're getting undefined, I think it is probably because your chunk_of_markup variable is actually undefined at that point. My guess is that you're getting the value for it through AJAX and so the call is done asynchronously which means the DOM will be ready before it actually returns a value. When you use the Debugger then the value is evaluated at the point of time where you run the command so by then, the async call already returns successfully (it's a race condition, if you're fast enough and your AJAX is slow then you can still get undefined, and it's also why 2 ready functions happen to make it slow enough for the AJAX call to return but it's still unreliable).
In all cases, if my theory is correct, then you need to hook to the callback of the AJAX request rather that DOM ready event, this is the only place where you can guarantee that your variable is defined.
Why not call the function init() in the document Handler itself.. I don't think that will lead to the same problems.. You can remove the Test.init() completely as it does not seem to do anything in here
Test = {
CONSTANTS : {},
VARIABLES : {},
MARKUP : {},
FUNCTIONS : {
init : function () {
// Access variable from different namespace
var all_constants = DifferentNamespace.CONSTANTS; // WORKS
var tester = DifferentNamespace.CONSTANTS.chunk_of_markup; // SAYS UNDEFINED
}
}
};
$(document).ready(function () {
Test.FUNCTIONS.init();
});