XMLHttpRequest prototype of onreadystatechange not called - javascript

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/

Related

Send event when module was executed

I'm really stuck on this.. I need to send an event when both Load module and Hide module code was executed, and only then send the event. Ideas on how to achieve this?
// Load module
(
function() {
var s=document.createElement('script');
s.type='text/javascript';
s.async=true;
s.src='https://example.com/bundles.js';
var x = document.getElementsByTagName('script')[0];
x.parentNode.insertBefore(s, x);
}
)();
// Hide module
var inverval = setInterval(hideClass, 100);
function hideClass () {
if ($(".class").hide().length > 0) clearInterval(inverval);
}
// When both happend = Send a event to Google Analytics
DigitalData.push({'event':Module, 'eventLabel':'Page'});
If this is your only option, then perhaps there's something you are going about wrongly. Anyway, let's see ... Only when both events have taken place.
var HandleTwoEvents = function (key1, key2) {
this.count = 0;
this.pack = [];
$self = this;
this.startListening = function(fn) {
fn = fn || function () {}
window.addEventListener(key1, function (ev) {
if ($self.pack.indexOf(key1) < 0) {
$self.pack.push(key1);
$self.count++;
if ($self.count == 2) {
fn();
$self.count = 0;
}
}
console.log(key1, ev);
});
window.addEventListener(key2, function (ev) {
if ($self.pack.indexOf(key2) < 0) {
$self.pack.push(key2);
$self.count++;
if ($self.count == 2) {
fn();
$self.count = 0;
}
}
console.log(key2, ev);
});
}
}
Forgive me, i always use this function to create events
function createEvent(name, obj) {
var evt = document.createEvent("Event");
evt.initEvent(name, true, true);
evt.data = obj;
dispatchEvent(evt);
}
Now, to log both events ...
var both = new HandleTwoEvents("EventKeyOne", "EventKeyTwo");
both.startListening(function () {console.log("This means that both Events have taken place")});
Now, let's test ...
createEvent("EventKeyOne", {});
//key, data are the arguments ... function defined in startListening above does not execute, and upon inspection, both.count is seen to be 1
createEvent("EventKeyTwo", {});
//Now, function executes.
//It also works if "EventKeyTwo" is raised before "EventKeyOne"
Happy Coding!
PS: I'm sure there's a better way to handle the use of the $self variable, with some function binding, i guess. I've never been able to learn it.

How to access the values of local variables globally [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 7 years ago.
I returned the rsltCallback function , when i call the googleSearchSuggestions function , i am getting undefined. When i console.log the input parameter inside the rsltCallback function it's printing the output to console.
var googleSearchSuggestions = function(search_Keyword , element) {
var parsed;
var uri = 'http://suggestqueries.google.com/complete/search?output=toolbar&hl=en&q=' + search_Keyword;
var xhr = (typeof XMLHttpRequest !== 'undefined') ? new XMLHttpRequest() : new ActiveXObject(Microsoft.XMLHTTP);
xhr.onreadystatechange = function(){
xhr.responseType = 'xml';
if(xhr.status == 200 && xhr.readyState == 4)
{
var response = xhr.responseXML;
var items = response.getElementsByTagName('toplevel')
for (var i = 0 ; i < items[0].childNodes.length;i++)
{
parsed = items[0].childNodes[i].firstElementChild.getAttribute('data');
rsltcallBack(parsed);
}
}
};
xhr.open('GET', decodeURI(uri), true);
xhr.send();
var rsltcallBack = function(input){
return input;
};
return rsltCallBack();
};
Because you have an asynchronous function call in your method you need to return the parsedValue at a later time. A way to do this are so called promises.
If you want that variable to be globally accessible then you can just add it to window (eg. window.parsedOutput = ...).
This incident as JavaScript Variable hosting issue. for ex:
var value = "a";
function test() {
var value;
alert(value)
}
test();
the global variable return undefined error. same code as works fine as below
var value = 10;
function test() {
var value ="b";
alert(value);
}
test();
online reference :
http://techslides.com/understanding-javascript-closures-and-scope
http://stackoverflow.com/questions/9085839/surprised-that-global-variable-has-undefined-value-in-javascript
An onreadystatechange function is only called after the state has been changed. This is not immediate; there is a split second in which the state has not yet changed. So you cannot access parsedOutput until the onreadystatechange function is called; its value has not yet been set. JavaScript doesn't just stop and wait for this to happen; it allows the request to be made in the background. It continues executing your code, even though the onreadystatechange method has not yet been called.
To solve your problem, simply wait for a response before using parsedOutput. You do this by using the onreadystatechange function to trigger the next step in your code.
BTW, performing a request in the background like this is call an asynchronous request. It is how you make remote requests with JavaScript; it prevents the script from hanging while waiting for a response.
Instead of accessing local variable, just initialize the variable outside the function and assign values locally,
Copy-Paste this,
var parsed ="";
var googleSearchSuggestions = function(search_Keyword , element) {
var uri = 'http://suggestqueries.google.com/complete/search?output=toolbar&hl=en&q=' + search_Keyword;
var xhr = (typeof XMLHttpRequest !== 'undefined') ? new XMLHttpRequest() : new ActiveXObject(Microsoft.XMLHTTP);
xhr.onreadystatechange = function(){
xhr.responseType = 'xml';
xhr.open('GET', decodeURI(uri), true);
xhr.send();
if(xhr.status == 200 && xhr.readyState == 4)
{
var response = xhr.responseXML;
var items = response.getElementsByTagName('toplevel')
for (var i = 0 ; i < items[0].childNodes.length;i++)
{
parsed = items[0].childNodes[i].firstElementChild.getAttribute('data');
rsltcallBack(parsed);
}
}
}
};
function rsltcallBack(input)
{
setTimeout(function()
{
alert(input);
},2000);
}
There are a few ways to declare global variables
var carName;
function myFunction() {
carName = 'Volvo';
}
This example declares the variable outside of the function and then assigns a value to it inside the function scope.
The other is
function myFunction() {
window.carName = 'Volvo';
}
This sets the variable on the window which can be deleted using delete window.carName.

String to jQuery function

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']);

JavaScript "is not a function" error when calling defined method

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

Listen to state and function invocations

Is it possible to listen to any function invocation or state change
I have a object that wrap another
function wrapper(origiObj){
this.origObj = origObj;
}
var obj = wrapper(document);//this is an example
var obj = wrapper(db);//this is an example
now everytime someone tries to invoke obj.innerHTML or obj.query(..)
I would like to listen to that..
Yes, it's possible:
functions are easy, and properties has to be watched
function FlyingObject(obj){
this.obj = obj;
for(var p in obj){
if(typeof obj[p] == 'function'){
console.log(p);
this[p] = function(){
console.log("orig func");
};
}else{
this.watch(p,function(){
console.log("orig property");
});
}
}
}
var obj = {
f:function(a,b){ return a+b},
m:1
};
var fo = new FlyingObject(obj);
fo.m = 5;
fo.f(1,4);
If your browser/node.js doesn't support Object.watch, check this out:
Object.watch() for all browsers?
Yes you can, define a getter/setter for properties and a shadow function for the function like this: http://jsfiddle.net/fHRyU/1/.
function wrapper(origObj){
var type = origObj.innerHTML ? 'doc' : 'db';
if(type === "doc") {
var orig = origObj.innerHTML;
origObj.__defineGetter__('innerHTML',
function() {
// someone got innerHTML
alert('getting innerHTML');
return orig;
});
origObj.__defineSetter__('innerHTML',
function(a) {
// someone set innerHTML
alert('setting innerHTML');
orig = a;
});
} else if(type === "db") {
var orig = origObj.query;
origObj.query = function() {
//someone called query;
alert('calling query');
orig.apply(this, arguments);
};
}
return origObj;
}
var obj = wrapper(document.body);
obj.innerHTML = 'p';
alert(obj.innerHTML);
var db = function() {}
db.query = function() {alert('foo');}
obj = wrapper(db);
obj.query();
edit: "Deleting" answer since it's tagged node.js, leaving it in case it happens to be useful to anyone else:
The general answer is no, it isn't. At least not in every browser, so any solution anyone gives isn't going to work in many cases.
There are a few things that can work, but again there is horrible support for them:
dom modified events (FF only, I believe)
DOMAttrModified
DOMNodeInserted
DOMNodeRemoved
etc
object.watch (FF only)

Categories

Resources