I have the following code in my ajax query:
xhr.onreadystatechange = stateChange;
and the function stateChange is function stateChange(event)
Is it possible to add a second parameter to the function so it doesn't just passes a number as well as the event?
I've tried doing xhr.onreadystatechange = stateChange(event,'123'); with function stateChange(event,num) but it doesn't seem to work.
You can create a closure that allows your event handling function access to those variables.
Instead of
xhr.blahblah;
xhr.onreadystatechange = stateChange;
xhr.blahblah;
This technique creates an anonymous function that gives scope to your '123' variable:
xhr.blahblah;
function (xhrObj, callbackFn, param) {
xhrObj.onreadystatechange = function (event) {
callbackFn(event, param);
};
}(xhr, stateChange, '123');
xhr.blahblah;
Mmmm, sort of:
xhr.onreadystatechange = function(event) { stateChange(event,'123'); };
would do what you say you want, but it isn't clear to me that what you say you want is what you need.
Related
Im learning JS and working with passing variables between functions. I am trying to execute a function on click whilst passing a variable between functions. I can pass the variable by executing the function on page load, but cant do it on a click function.
function getData() {
let xmlhttp = new XMLHttpRequest();
let url = "https://jsonplaceholder.typicode.com/posts";
xmlhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
myArr = JSON.parse(this.responseText);
//myFunction(myArr);
document.getElementById("start2").addEventListener("click", myFunction(myArr));
}
};
xmlhttp.open("GET", url, true);
xmlhttp.send();
}
getData();
function myFunction(arr) {
// var arr = getData();
var out = "";
var i;
for (i = 0; i < arr.length; i++) {
out += '<p>' + arr[i].id + '</p><br>';
}
document.getElementById("answer-holder").innerHTML = out;
}
It does not work, since addEventListener expects a function as its second argument. But you do not provide a function. Instead you provide the result of evaluating myFunction, which is undefined (since myFunction does not return anything).
// when `addEventListener` is called, `myFunction` is also called immediately,
// instead of first being called when the event is triggered
document.getElementById("start2").addEventListener("click", myFunction(myArr));
You can fix the code by instead providing a function, that calls myFunction with your chosen argument.
document.getElementById("start2").addEventListener("click", function() {
myFunction(myArr);
});
The problem is you are calling the function when trying to set the onClick listener:
document.getElementById("start2").addEventListener("click", myFunction(myArr));
As suggested by Thomas, you have to pass a function to addEventListener. However, I would suggest the newer lambda notation:
document.getElementById("start2").addEventListener("click", () => myFunction(myArr));
I am creating a function that handles a bunch of stuff around pagenating and sorting a table. It contains a key function that submits the db query and updates the display table.
I want to be able to access that inner function/method from both inside the function and also from outside on the object created.
testFunction = function() {
keyMethod = function() {
console.log('ya got me');
};
document.getElementById('test').addEventListener('click', function (e) {
keyMethod();
});
keyMethod();
};
myTest = new testFunction();
myTest.keyMethod();
testFunction = function() {
this.keyMethod = function() {
console.log('ya got me');
};
document.getElementById('test').addEventListener('click', function (e) {
// would have to use bind here which then messes up trying to
// find the correct target etc.
keyMethod();
});
this.keyMethod();
};
myTest= new DrawShape();
myTest.keyMethod();
Creating it the first way means that the keyMethod function is available everywhere within the testFunction but I cant call it from outside.
Creating it the second way means I can do myTest.keyMethod but I then cant call it from within an inner function without using bind everywhere.
Is there a better way..?
You could replace the function provided as callback with an arrow function or use bind the function first like you already said.
testFunction = function() {
this.keyMethod = function() {
console.log('ya got me');
};
// Replace callback by simply providing the function to call.
// This works as long as you don't use the `this` keyword inside the
// provided function.
document.getElementById('test').addEventListener('click', this.keyMethod);
// If your callback method does use the `this` keyword you can either use an
// arrow function or bind the function up front.
document.getElementById('test').addEventListener('click', event => this.keyMethod());
document.getElementById('test').addEventListener('click', this.keyMethod.bind(this));
this.keyMethod();
};
console.log("constructor output:");
myTest = new testFunction();
console.log(".keyMethod() output:");
myTest.keyMethod();
console.log("click event output:");
<button id="test">test</button>
At the moment I am using a similar code visible below to set a specific value for timeout when creating an instance of XMLHttpRequest.
I also set a function for ontimeout which will be executed when event is fired.
I would like to know if it is possible to set a timeout value globally for any XMLHttpRequest and a global function when ontimeout is called.
I am aware I could use a factory for XMLHttpRequest which could set timeout and ontimeout automatically on each instance created but I would like to know if a different approach exists.
Notes: I am looking for a solution based on vanilla JavaScript (no jQuery or other libraries) and I am targeting only Chrome and FireFox latest version.
Any idea is welcome thanks.
var xhr = new XMLHttpRequest();
xhr.open('GET', '/server', true);
xhr.timeout = 2000;
xhr.onload = function () {};
xhr.ontimeout = function (e) {
// do smt here
};
xhr.send(null);
You have at least three options:
Encapsulate your use of XMLHttpRequest in your own function. That lets you apply these sorts of things in that function. The fact your code uses the function makes it perfectly clear what's going on. For instance:
function createXHR() {
var xhr = new XMLHttpRequest();
xhr.timeout = 2000;
xhr.addEventListener("timeout", function(e) {
// ...
});
return xhr;
}
Override XMLHttpRequest with your own version. I do not recommend doing this, but sometimes if you need to integrate with code that uses XMLHttpRequest directly, you may feel you need to do it. Example:
(function() {
var realXMLHttpRequest = XMLHttpRequest;
XMLHttpRequest = function fakeXMLHttpRequest() {
realXMLHttpRequest.apply(this, arguments);
this.timeout = 2000;
this.addEventListener("timeout", function(e) {
// ...
});
};
XMLHttpRequest.prototype = realXMLHttpRequest.prototype;
})();
Again, I do not recommend it, but it's possible.
You've said below that the one above doesn't work for you. If it doesn't, it's because the XMLHttpRequest implementation you're using is making life difficult. This workaround will work:
(function() {
var realXMLHttpRequest = XMLHttpRequest;
XMLHttpRequest = function fakeXMLHttpRequest(arg) {
var xhr;
if (typeof arg !== "undefined") {
// Some implementations allow for a non-standard argument, support them
xhr = new realXMLHttpRequest(arg);
} else {
// Normal case
xhr = new realXMLHttpRequest();
}
xhr.timeout = 2000;
xhr.addEventListener("timeout", function(e) {
// ...
});
return xhr;
};
XMLHttpRequest.prototype = realXMLHttpRequest.prototype;
})();
That works even though you call it with new XMLHttpRequest because the way the new operator works is that it creates an object and calls the function with that object as this, and then the result of new is that object unless the function returns a different non-null object, as the one above does.
Did I mention I do not recommend this? ;-)
Use a library that already does #1 for you.
I was able to monkey path XMLHttpRequest using the following code.
(function (xhr) {
var send = xhr.send;
xhr.send = function (data) {
this.timeout = 5000;
var hasTimeOut = 'ontimeout' in this;
if (hasTimeOut) {
this.ontimeout = function () {
throw ('Error XMLHttpRequest timeout.');
};
}
return send.apply(this, arguments);
};
})(XMLHttpRequest.prototype);
I'm struggling to get my head around how to pass information around in javascript. Can some kind person show me how to take a variable from within a function and use it in another function but without making the variable global. I think i need to use return but I am stuck and not sure what to google to get something I can learn from.
For example, how can I make the "json" variable available so i can use it in the "MasterView" function. Thank you in advance.
function fetchData() {
var xhr = Ti.Network.createHTTPClient({
onload : function(e) {
Ti.App.Properties.setString('cachedJson', this.responseText);
var json = JSON.parse(Ti.App.Properties.getString('cachedJson',''));
},
timeout: 5000
});
xhr.open("GET", site_url + "?get_json=postObjects");
xhr.send();
}
function MasterView() {};
There are numerous ways to go around this without using globals.
Also, what do you mean by "without making the variable global"?
You want to avoid polluting the global namespace or you just don't want your variable to be available in all of your functions?
Here's a way of passing the value from one of your functions to another:
function a(){
var hello = "world";
return hello;
}
function b(){
var result = a();
console.log(result);
}
As you can see function a() returns the variable hello with the value "world".
Now if you call function b() it will store a()'s return value in a variable called result and then log it to the console.
Another option is to use parameters (sometimes called as arguments) in your functions:
function a(){
var hello = "world";
b(hello);
}
function b(arg){
console.log(arg);
}
In this case if you will call function a() it will instantly call function b(), passing the variable hello, so b() will log "world".
Of course the latter aproach is not always a good choice, i.e. you simply don't want your first function to call another one. In that case you can do the following:
function a(){
var hello = "world";
return hello;
}
function b(arg){
console.log(arg);
}
Then call function b() as: b(a());
This way you'll pass function a()'s return value as an argument to function b().
Hopefully this cleared up most of your questions. :)
// Simply add this:
var json;
// and continue...
function fetchData() {
var xhr = Ti.Network.createHTTPClient({
onload : function(e) {
Ti.App.Properties.setString('cachedJson', this.responseText);
json = JSON.parse(Ti.App.Properties.getString('cachedJson',''));
},
timeout: 5000
});
xhr.open("GET", site_url + "?get_json=postObjects");
xhr.send();
}
function MasterView() {
console.log(JSON.stringify(json))
};
With hopes that it might work...Or:
function fetchData() {
var xhr = Ti.Network.createHTTPClient({
onload : function(e) {
Ti.App.Properties.setString('cachedJson', this.responseText);
json = JSON.parse(Ti.App.Properties.getString('cachedJson',''));
},
timeout: 5000
});
xhr.open("GET", site_url + "?get_json=postObjects");
xhr.send();
}
function MasterView() {
var json;
fetchData();
console.log(JSON.stringify(json))
};
Global variables are useful for a lot of things, however if the only variable you need is json, and you only need it in the function Masterview, call the function fetchData from the function MasterView and make fetchData return json.
W3 schools is a great tool for javascript and html development. Try this link for more on functions, specifically functions with a return value.
http://www.w3schools.com/js/js_functions.asp
I am trying to add some event listener to document, but for some reason it looks like the click event is never fired, because the callback is never called:
function NotJquery(element) {
this.element = element;
return this;
}
NotJquery.prototype.eventFired = function(event, callback) {
console.log('in NotJquery prototype');
return function (event, callback) {
element.addEventListener(event, callback, true);
};
};
var $ = function(element) {
return new NotJquery(element);
};
function Test() {
}
Test.prototype.addListener = function() {
console.log('in Test prototype');
$(document).eventFired('click', function() {
console.log('click event fired');
});
};
(function() {
var test= new Test();
test.addListener();
}());
Both the messages: "in Test prototype" and "in NotJquery prototype" are logged in the console, but when I click somewhere in my document the message "click event fired" is not output in the console. I do not see what is wrong with my code. Anybody has an idea to get it working?
http://jsfiddle.net/Nn3tZ/1/
Your client code is expecting something like this:
NotJquery.prototype.eventFired = function(event, callback) {
this.element.addEventListener(event, callback, false);
};
Working demo: http://jsfiddle.net/Nn3tZ/2/
element is not defined within your eventFired function (but that's not the only problem). Here's a minimal update:
NotJquery.prototype.eventFired = function(event, callback) {
var self = this; // <== Change 1 of 2
console.log('in NotJquery prototype');
return function () {
self.element.addEventListener(event, callback, true);
// ^-- Change 2 of 2
};
};
Unlike some other languages (Java, C#), this is not optional when referring to a property on the current object. Separately, the function you're creating within eventFired won't have the right this, so that's why I've stowed it away as self and then used self within the generated function (which is a closure).
Separately, you're passing event and callback into eventFired but then also declaring them on the generated function (it's not at all clear to me why you're generating a function there at all), so the ones you pass into eventFired are never used.
More reading (on my anemic blog):
Mythical Methods
You must remember this
Closures are not complicated