This is my code:
request_xml: function()
{
http_request = false;
http_request = new XMLHttpRequest();
if (http_request.overrideMimeType)
{
http_request.overrideMimeType('text/xml');
}
if (!http_request)
{
return false;
}
http_request.onreadystatechange = this.response_xml;
http_request.open('GET', realXmlUrl, true);
http_request.send(null);
xmlDoc = http_request.responseXML;
},
response_xml:function ()
{
if (http_request.readyState == 4)
{
if(http_request.status == 404 && countXmlUrl<=3)
{
countXmlUrl++;
realXmlUrl = xmlUrl[countXmlUrl];
this.request_xml();
}
if (http_request.status == 200)
{
xmlDoc = http_request.responseXML;
alert("need to update3");
this.peter_save_data();
}
}
},
peter_save_data:function()
{
// removed function code
},
Strangely, the alert fires without a problem but the function call underneath gives me this error:
Error: this.peter_save_data is not a function
Calling the same damn function from another function elsewhere works fine.
You could do this, right before you call the XML generation.
var that = this;
and later...
that.peter_save_data();
Because this frequently changes when changing scope by using a new function, you can't access the original value by using it. Aliasing it to that allows you still to access the original value of this.
One important piece of the puzzle that is missing is how response_xml is being called. This is important, because it will change what this is (see Jared's comment).
Remember that this can be thought of as (roughly) "the receiver of the method call". If response_xml is passed directly to use as a callback then of course it won't work -- this will likely be window.
Consider these:
var x = {f: function () { return this }}
var g = x.f
x.f() === x // true
g() === x // false
g() === window // true
Happy coding.
The "fix" is likely just to change how response_xml is being called. There are numerous ways to do this (generally with a closure).
Examples:
// Use a closure to keep he object upon which to explicitly invoke the method
// inside response_xml "this" will be "that",
// which was "this" of the current scope
http_request.onreadystatechange = (function (that) {
return function () { return that.response_xml() }
}(this)
// Or, alternatively,
// capture the current "this" as a closed-over variable...
// (assumes this is in a function: var in global context does not create a lexical)
var self = this
http_request.onreadystatechange = function () {
// ...and invoke the method upon it
return self.response_xml()
}
Personally, I would just use jQuery or similar ;-)
If you want a class-like behavior, use the right syntax, The libraries that use that, are using JSON to pass a parameter to a function that makes a class out of it.
function MyClass(CTOR paarams){
var response_xml=function ()
{
if (http_request.readyState == 4)
{
if(http_request.status == 404 && countXmlUrl<=3)
{
countXmlUrl++;
realXmlUrl = xmlUrl[countXmlUrl];
this.request_xml();
}
if (http_request.status == 200)
{
xmlDoc = http_request.responseXML;
alert("need to update3");
this.peter_save_data();
}
}
}
var peter_save_data=function()
{
// removed function code
}
}
var Test = new MyClass(somthing,another_something);
Test.response_xml();
//etc etc.
Or, use the libraries like Mootools where you can do it as JSON:
var T = new Class({
response_xml:function ()
{
if (http_request.readyState == 4)
{
if(http_request.status == 404 && countXmlUrl<=3)
{
countXmlUrl++;
realXmlUrl = xmlUrl[countXmlUrl];
this.request_xml();
}
if (http_request.status == 200)
{
xmlDoc = http_request.responseXML;
alert("need to update3");
this.peter_save_data();
}
}
},
peter_save_data:function()
{
// removed function code
}
});
var X = new T();//etc etc
Related
I have an array of XMLHttpRequest (working fine) and I want to know when they are all finished. I wrote the following array of on readystatechange functions:
xhr[i].open('post', 'PHP/write_and_exec_opt.php');
display_opt[i] = xhr[i].onreadystatechange = function(index) {
if (xhr[index].readyState == 4 && xhr[index].status == 200) {
text = xhr[index].responseText.trim();
n_finished++;
console.log('display_opt', n_finished);
}
xhr[index].send(data);
}(i);
The xhr requests are executed properly. But no output on the console log. Why not?
You're really close, but by executing your anonymous function, you end up assigning its return value to the onreadystatechange property. You never return a value, so you assign undefined. You probably meant to return a function:
xhr[i].open('post', 'PHP/write_and_exec_opt.php');
display_opt[i] = xhr[i].onreadystatechange = function(index) {
return function() {
if (xhr[index].readyState == 4 && xhr[index].status == 200) {
text = xhr[index].responseText.trim();
n_finished++;
console.log('display_opt', n_finished);
}
};
}(i);
xhr[i].send(data);
That said, it gets confusing fast and there's no good reason to create the temporary function repeatedly, which is why I usually break things out:
Somewhere before (or after, but in any case in the same fundamental scope) your i loop:
function createStateChangeHandler(index) {
return function() {
if (xhr[index].readyState == 4 && xhr[index].status == 200) {
text = xhr[index].responseText.trim();
n_finished++;
console.log('display_opt', n_finished);
}
};
}
Then in your i loop:
xhr[i].open('post', 'PHP/write_and_exec_opt.php');
display_opt[i] = xhr[i].onreadystatechange = createStateChangeHandler(i);
xhr[i].send(data);
For what it's worth, I'd suggest looking into using promises for this sort of thing, esp. now that they're a standard part of JavaScript (as of ES2015):
Promise.all(xhrArray.map(function(xhr) {
return new Promise(function(resolve, reject) {
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
resolve(xhr.responseText.trim());
} else {
reject(/*...include some error perhaps...*/);
}
}
};
xhr[index].send(data);
});
})
.then(function(array) {
// `array` contains an array o the results, in the same
// order as `xhrArray`
})
.catch(function() {
// At least one XHR failed
});
You have 2 questions
no, there is no builtin method to catch when you array of XMLHttpRequest finished. you need implement it by yourself
you have no output because you don't assign function to onreadystatechange, you assign return value of execution of your function (undefined in your case).
So this is a part of a simple JavaScript code to handle xmlhttprequest. The part that is generating the error is at the bottom (else if):
httpReq.onreadystatechange = function(){
if (httpReq.readyState == 4) {
if (httpReq.status == 200) {
var strRes = httpReq.responseText;
if(xmlOrig) strRes = (new $Xml(strRes, true)).conteudo(xmlOrig);
if(elemDest) $id(elemDest).innerHTML = strRes;
if(func) {
var dadosArray = new Array(4, strRes, httpReq, 'OK', 'Concluído com sucesso.');
window[func](dadosArray);
}
} else {
if(elemDest) elemDest.innerHTML = 'Erro: '+httpReq.status+' | '+httpReq.statusText;
if(func) {
var dadosArray = new Array(4, false, httpReq, 'erro', 'Erro, conteúdo não carregado!');
window[func](dadosArray);
}
}
} else if(func){
console.log("func? "+typeof(func));
var dadosArray = new Array(httpReq.readyState);
window[func](dadosArray); // <-- HERE IS THE ERROR!
}
}
However, the console.log return the "func" argument as a function, so where is the error?
The Safari console:
func? function
TypeError: 'undefined' is not a function (evaluating 'windowfunc')
Are you sure func is on the window? You are checking for func, which could be inside any scope, and thne you can calling window.func().
You probably meant to do window["func"] instead of window[func].
The latter expression is equivalent to window["function(someParam) { ... }"] (i.e., a whatever the content of func actually is). window probably does not have a property whose name is the entire stringified text content of func.
Okay so we have a project ongoing, and it is to be passed today. But I have a problem.
This is the sample code:
output.js
var MapPrintDialog = Class.create();
MapPrintDialog.prototype = {
// 他の画面からテンプレート情報を更新するために呼ばれる。
comboReload : function() {
var val = tempComb.getValue();
tempComb.store.load({
callback: function(result, o) {
if (this.store.data.keys.indexOf(val) == -1) {
this.setValue(this.store.data.keys[0]);
this.fireEvent("select", this);
}
}.createDelegate(tempComb)
});
},
initialize : function(){
this.define();
},
define : function(){
var DrawPrintAreaFrame = function(mode, noUpdateStatusBarText){
var cs = getCurrentSetting();
if (mode == "init" || mode == "edit"){
PrintController.DrawMapPrintArea(cs.center.x, cs.center.y, cs.scale, cs.result.PrintMaps[0].Width, cs.result.PrintMaps[0].Height, cs.result.PageRowCount, cs.result.PageColumnCount, mode);
}
else if (mode == "delete"){
PrintFrameManager.ClearPrintFrame();
}
if (noUpdateStatusBarText) {
gisapp.noUpdateStatusBarText = true;
}
gisapp.refreshMap();
}
}
Now my problem is, how will I call "DrawPrintAreaFrame" from another js file?
I tried:
MapPrintDialog.prototype.define().DrawPrintAreaFrame("edit");
MapPrintDialog.prototype.define.DrawPrintAreaFrame("edit");
MapPrintDialog.define().DrawPrintAreaFrame("edit");
MapPrintDialog.define.DrawPrintAreaFrame("edit");
MapPrintDialog.DrawPrintAreaFrame("edit");
DrawPrintAreaFrame("edit");
but it gives me an error lol. How will I fix this? Please don't be too harsh, I just started learning javascript but they gave me an advanced project which isn't really "beginner" friendly XD
EDIT ----------------------
Okay now i tried to modify it like this:
var MapPrintDialog = Class.create();
MapPrintDialog.prototype = {
// 他の画面からテンプレート情報を更新するために呼ばれる。
comboReload : function() {
var val = tempComb.getValue();
tempComb.store.load({
callback: function(result, o) {
if (this.store.data.keys.indexOf(val) == -1) {
this.setValue(this.store.data.keys[0]);
this.fireEvent("select", this);
}
}.createDelegate(tempComb)
});
},
initialize : function(){
this.DrawPrintAreaFrame("edit");
}
}
function DrawPrintAreaFrame(mode, noUpdateStatusBarText){
var cs = gisapp.getCurrentView();
if (mode == "init" || mode == "edit"){
PrintController.DrawMapPrintArea(cs.center.x, cs.center.y, cs.scale, cs.result.PrintMaps[0].Width, cs.result.PrintMaps[0].Height, cs.result.PageRowCount, cs.result.PageColumnCount, mode);
}
else if (mode == "delete"){
PrintFrameManager.ClearPrintFrame();
}
if (noUpdateStatusBarText) {
gisapp.noUpdateStatusBarText = true;
}
gisapp.refreshMap();
}
But it gives me: Javascript runtime error: Object doesn't support property or method 'DrawPrintAreaFrame'
You have 2 different way:
1- you have to first change it like this:
define : function(){
var DrawPrintAreaFrame = function(mode, noUpdateStatusBarText){
//You function code
}
this.getDrawPrintAreaFrame = function(){
return DrawPrintAreaFrame;
}
}
then create your object using your class:
var obj = new MapPrintDialog();
obj.define();
obj.getDrawPrintAreaFrame().call(obj, "edit");
2- remove the define method and add your function to prototype:
MapPrintDialog.prototype.DrawPrintAreaFrame = function(){
//your function code
}
create your object and simply call your method like this:
var obj = new MapPrintDialog();
obj.DrawPrintAreaFrame("edit");
It's funny, because I said you have 2 ways, and the third one just came up:
3- as far as you use Prototype framework you can use MapPrintDialog.addMethods, which is there to be used to add new instance methods to your class, remove your define and DrawPrintAreaFrame functions and add this:
MapPrintDialog.addMethods({
DrawPrintAreaFrame: function DrawPrintAreaFrame(){
//your code
}
});
or even without removing your method you can use it like:
define : function(){
var DrawPrintAreaFrame = function(mode, noUpdateStatusBarText){
//You function code
}
MapPrintDialog.addMethods({ DrawPrintAreaFrame: DrawPrintAreaFrame });
}
and create your instance and call the method:
var obj = new MapPrintDialog();
obj.DrawPrintAreaFrame("edit");
4- try this if you need your function like a sort of static method, without needing to create a instance:
MapPrintDialog.DrawPrintAreaFrame = function(){
//You function code
}
and call it like this
MapPrintDialog.DrawPrintAreaFrame("edit");
and if you want to define it in runtime, add the whole definition to your define method like this:
define : function(){
MapPrintDialog.DrawPrintAreaFrame = function(){
//You function code
}
}
I'm trying to detect when any ajax call finishes in my UIWebView. I modified the code in this answer: JavaScript detect an AJAX event to the best of my abilities. Here is my attempt:
var s_ajaxListener = new Object();
s_ajaxListener.tempOnReadyStateChange = XMLHttpRequest.prototype.onreadystatechange;
s_ajaxListener.callback = function () {
window.location='ajaxHandler://' + this.url;
};
XMLHttpRequest.prototype.onreadystatechange = function() {
alert("onreadystatechange called");
s_ajaxListener.tempOnReadyStateChange.apply(this, arguments);
if(s_ajaxListener.readyState == 4 && s_ajaxListener.status == 200) {
s_ajaxListener.callback();
}
}
I'm injecting this into the webView but the alert is never firing. If I place an alert at the beginning or end of the script, it'll fire so I'm fairly certain there are no syntax errors.
I'm not a JS guy so I'm hoping this is a trivial problem.
Putting a generic onreadystatechange in XMLHttpRequest.prototype didn't work for me. However the code you linked to can be easily adapted to invoke a custom function whenever that event occurs:
var s_ajaxListener = {};
s_ajaxListener.tempOpen = XMLHttpRequest.prototype.open;
s_ajaxListener.tempSend = XMLHttpRequest.prototype.send;
// callback will be invoked on readystatechange
s_ajaxListener.callback = function () {
// "this" will be the XHR object
// it will contain status and readystate
console.log(this);
}
XMLHttpRequest.prototype.open = function(a,b) {
if (!a) var a='';
if (!b) var b='';
s_ajaxListener.tempOpen.apply(this, arguments);
s_ajaxListener.method = a;
s_ajaxListener.url = b;
if (a.toLowerCase() == 'get') {
s_ajaxListener.data = b.split('?');
s_ajaxListener.data = s_ajaxListener.data[1];
}
}
XMLHttpRequest.prototype.send = function(a,b) {
if (!a) var a='';
if (!b) var b='';
s_ajaxListener.tempSend.apply(this, arguments);
if(s_ajaxListener.method.toLowerCase() == 'post')s_ajaxListener.data = a;
// assigning callback to onreadystatechange
// instead of calling directly
this.onreadystatechange = s_ajaxListener.callback;
}
http://jsfiddle.net/s6xqu/
Overview
I am trying to find the jQuery function that matches a selection attribute value and run that function on the selection.
Example.
$('[data-store="' + key + '"]').each(function() {
var $callback = $(this).attr('data-filter');
if($callback != null) {
var fn = window['$.fn.nl2br()'];
if(jQuery.isFunction(fn)) {
$(this).fn();
}
}
$(this).setValue(value);
});
Problem 1
I'm not sure how to create a jQuery function call from string.
I know I can call the function like this, $(this)'onclick'; however I have no way to check if it exists before trying to call it.
Normally I could do this:
var strfun = 'onclick';
var fn = body[strfun];
if(typeof fn === 'function') {
fn();
}
This seems to fail:
var fn = window['$.fn.nl2br()'];
if(jQuery.isFunction(fn)) {
$(this).fn();
}
EDIT:
I seem to be having success doing this:
if($callback != null) {
var fn = $(this)[$callback]();
if( typeof fn === 'function') {
$(this)[$callback]();
}
}
Problem 2
Using jQuery.isFunction() how do you check if a methods exists? can you do this with jQuery.isFunction()?
Example
Declare function:
$.fn.nl2br = function() {
return this.each(function() {
$(this).val().replace(/(<br>)|(<br \/>)|(<p>)|(<\/p>)/g, "\r\n");
});
};
Test if function existe, these options fail:
jQuery.isFunction($.fn.nl2br); // = false
jQuery.isFunction($.fn['nl2br']()); //false
Functions in JavaScript are referenced through their name just like any other variables. If you define var window.foobar = function() { ... } you should be able to reference the function through window.foobar and window['foobar']. By adding (), you are executing the function.
In your second example, you should be able to reference the function through $.fn['nl2br']:
$.fn.nl2br = function() {
return this.each(function() {
$(this).val().replace(/(<br>)|(<br \/>)|(<p>)|(<\/p>)/g, "\r\n");
});
};
console.log(jQuery.isFunction($.fn['nl2br']));
See a working example - http://jsfiddle.net/jaredhoyt/hXkZK/1/
var fn = window['$.fn.nl2br']();
and
jQuery.isFunction($.fn['nl2br']);