I'm doing a cross domain request for IE using the XDomainRequest in this way:
<div id="result"></div>
<script type="text/javascript">
var urlToOpen;
var openxUrl = "http://DOMAIN.com/www/delivery/apu.php";
if ($j.browser.msie && window.XDomainRequest) {
// Use Microsoft XDR
var xdr = new XDomainRequest();
xdr.open("get", openxUrl);
xdr.onload = function() {
urlToOpen = xdr.responseText;
};
xdr.send();
}
$j('#result').html(urlToOpen)
</script>
The code return the correct value, but I want to use the value of the Ajax return in other functions (not only inside the function of xdr.onload), so I need that what is returned with xdr.responseText can be declared as global or something like that.
Example: The last line $j('#result').html(urlToOpen) pretend to assign the value of "urlToOpen" but this does not work. How can I achieve this ?
The simple answer here is to use remove the var from xdr and then it becomes a property of the global object. You then will be able to access it anywhere.
I would also namespace it so it doesn't conflict, so name it something like myApp_xdr.
I hope this helps.
Solution: Since "xdr.onload" run as an asynchronous way, the problem was that I tried to use the variable "urlToOpen" in the code $j('#result').html(urlToOpen) when this variable was empty due that "xdr.onload" had not finnished of load, so I place the code $j('#result').html(urlToOpen) inside the "xdr.onload" function. It is not the best but worked.
Related
I'd like to load this code (1) via XMLHttpRequest in another page
<input type="text" onkeypress="info(event)"/>
<script>
function info(e) { console.log(e.keyCode); }
</script>
This (2) is the way I tried...
<div id="x"></div>
<script>
var xhr = new XMLHttpRequest();
xhr.onload = handleResponse.bind(xhr);
xhr.open("POST","test.php",true);
xhr.send();
function handleResponse() {
var x = document.getElementById("x");
x.innerHTML = this.responseText;
var js = x.getElementsByTagName("script")[0];
eval(js.innerText);
}
</script>
...and this is the error I got on keypress:
Uncaught ReferenceError: info is not defined
Why the info() is not found since its definition was eval'd? How to make the info fire on keypress if (2) should be unaware of (1)? No jQuery.
Following Mike's recommendation, I created this question to solve separated problem that leads to explanation of this problem.
(edited for es202020, because JS has changed drastically since 2013 and the old answer is now an anti-pattern. Don't use eval)
Scope. You didn't eval it on window, you added it to the scope of that function handleResponse so it won't exist outside of handleResponse. Also, in this case you don't really need eval at all, just create a script element and add that:
function handleResponse(evt) {
const s = document.createElement(`script`);
s.textContent = evt.responseText;
document.head.appendChild(s);
}
The browser already knows how to interpret scripts, but you need to add elements to the head if you want them to execute instead of be inserted as inactive DOM nodes after the page has already loaded.
Don't use eval().
<script type="text/javascript>
(function($, win) {
function mySettingsInitJS () {
var self = this;
var opts = {
'params' : "userId=" + userId;
};
self.init = function() {
self.initUnlinkAction();
};
self.initbtnAction = function() {
$('#Btn').click(self.btnAction);
};
self.btnAction = function(e) {
if(e) { e.preventDefault(); }
jQuery.post(
'http://abc/rest/PostRequest',
opts.params, function(data) {
alert('This worked!');
}, "json");
};
}
function win.MyFilesSettingsInitJS = new MyFilesSettingsInitJS();
}(jQuery, window));
</script>
I have this this script in a velocity template page and a Btn also in it. Which is basically a fancy Link. I am also sure the rest API on the server side is working fine. But somehow my java script function is not called. Could some one please help me ?
Seems like you forgot quotes here:
'http://abc/rest/PostRequest'
It becomes pretty obvious with good syntax highlighting. Plus if you use JSLint or similar it will find most problems, as well as the console when debugging your code.
Your URL value needs to be a string...add quotes to it
You should use some developer tools available in the browser. Most browsers have at least an error console that would display any JS errors. For example, this code has several syntax errors:
In var opts = { 'params' : "userId=" + userId; }; it is illegal to end a line with ; when you're defining an object literal (a.k.a. map or dictionary).
Again at this line, where is userId defined? It is a bad practice to have global variables, so if userId is a global variable defined in another script, you should refactor the code
function win.MyFilesSettingsInitJS = new MyFilesSettingsInitJS(); is illegal, since you're adding a property to an object, you're not declaring a function or a variable. Just use win.MyFilesSettingsInitJS = new MyFilesSettingsInitJS();
Actually, that might be wrong as well, since above you defined mySettingsInitJS, not MyFilesSettingsInitJS (but that function could actually be defined somewhere else, I don't have the whole code)
I try to load some external .js files, and have some irresolvable namespace conflicts.
I had the idea of loading some of the files in their own context somehow, replacing the "this" from pointing at the window object to some custom namespace.
example:
first.js:
name = "first";
second.js:
name = "second";
It seems to me that this kind of trick can be very useful. Is it possible at all?
EDIT
seems that replacing "this" does not begin to solve the problem, as it is not the default context for identifier resolution in javascript. this is my test code:
var first = {};
var second = {};
(function(){name = "first";}).call(first);
(function(){name = "second";}).call(second);
document.write('name= '+name+' <br/>\n'); //prints "second"
document.write('first.name= '+first.name+' <br/>\n'); //prints "undefined"
document.write('second.name= '+second.name+' <br/>\n'); //prints "undefined
any ideas?
RESOLUTION
It is not possible. I ended up wiser than I was this morning, and I gave it up.
I recommend these enlightening reading materials for anyone with a similar problem that might want to take a crack at it:
http://jibbering.com/faq/notes/closures/
http://softwareas.com/cross-domain-communication-with-iframes
One idea I've had for doing it without needing modifications to your external JavaScript file is getting the contents of the JavaScript file in an AJAXy way (up to you how you do that) and then put it all in a function using the new Function(code) way, then initialise that with new:
surrogateWindow = new new Function(jsCode)();
Then surrogateWindow is the this of that code. I think that that idea should work.
I'm not clear on your reason for doing this; what are you using this for, exactly?
Wrapping the contents of your second.js in an anonymous function will prevent variables in that file from conflicting with global variables. If you really must have a this set to a particular object that isn't the global object, you could do something like
var differentThis = {};
(function() {
// Contents of second.js go here
}).call(differentThis);
UPDATE
You can't do what you want. You seem to want to access the Variable object, which is the object to which a property is added when you declare a variable in JavaScript. In global code, the Variable object is the global object, so you can access it; within a function this is a property of the execution context that there is no way to access directly.
Even though this is an old question, this answer may still be relevant for some:
When a js file is loaded it automatically gets the window's context. That is not possible to change.
However, if you are trying to avoid conflicts between libraries that you are loading, and you don't have control over those libs, and they don't have a built-in "no-conflict" mechanism, then there is a nice trick -
you can load those into a source-less iframe.
This will make their context to be the window of the iframe, and you will still be able to access the iframe since there is no cross-domain issue here.
You can see this library as an example for use of this technique.
You can load your file in an iframe, the file is not a .js but an HTML file, like:
<html>
<body>
<script>
var $ = parent.$, // you can share objects with the parent, eg: jQuery
localObject = { // your local object definition
name: 'first',
showName: function(){
$('div.name').html( this.name );
}
};
//assign the local object to the custom namespace
parent.customNamespace.object1 = localObject;
</script>
</body>
</html>
The trick is to use parent. to get the javascript objects available in the parent page.
For the code you've written, I think you're misunderstanding some of the way classes work in JavaScript. In Java you can drop the this., but in JavaScript you can't. You'll always need to have this. there. So then your code becomes:
var first = {};
var second = {};
(function(){this.name = "first";}).call(first);
(function(){this.name = "second";}).call(second);
document.write('name= '+name+' <br/>\n'); //prints "undefined"
document.write('first.name= '+first.name+' <br/>\n'); //prints "first"
document.write('second.name= '+second.name+' <br/>\n'); //prints "second"
It would also be good to do it in a more normal class way. I'm not sure exactly what your situation is as I can't see all your code so you might be already doing it this way.
function Something(name) {
this.name = name;
}
var first = new Something("first");
var second = new Something("second");
document.write('name= '+name+' <br/>\n'); //prints "undefined"
document.write('first.name= '+first.name+' <br/>\n'); //prints "first"
document.write('second.name= '+second.name+' <br/>\n'); //prints "second"
Well you could wrap the contents of the js files with something like this:
var externalInterfaceForYourObject = (function(){
//code that defines your object
//this should refer to the current anonymous function, and not the window object
return this;
})();
I'm not a Javascript person normally, but I've been diving in, reading Douglas Crockford's book, and writing some trivial, useful tidbits as Chrome extensions and Node.js (note that this question isn't about either of them).
Right now, I'm trying to figure out how to retain a reference to an object that's initiating an AJAX request, that is: once I set the onload event handler (this is from inside a Chrome extension, so I'm using the base XMLHttpRequest object), is there any way that I can refer back to MyObject in the following example:
MyObject.prototype = {
PerformAction: function() {
this.Request = new XMLHttpRequest();
this.Request.open("GET", this.ConstructUrl(), true);
// From within ActionResultHandler, 'this' will always be the XMLHttpRequest
this.Request.onload = this.ActionResultHandler,
this.Request.send(null);
}
}
Doing this exactly is going to assign this to be the request object itself, and if I simply introduce a wrapper:
this.Request.onload = function() { ActionResultHandler() };
well, that just isn't going to do anything, because the ActionResultHandler is now out of scope. The reason I'm asking here is because I've only found trivial cases of caller manipulation (e.g. manipulating what this refers to from inside a function), but given that OO-ified Javascript and AJAX are literally everywhere, this has to have to be a known, simple issue, but my Google-fu is failing me here. In C#, events are invoked in the context of whoever attaches to them, not the object firing the event, so this doesn't come up on a daily basis. Perhaps there's a much better JS pattern that avoids this issue entirely?
It's not really clear to me which variable you want to hold a reference to. Here's how you would retain a reference to MyObject in your onload handler:
MyObject.prototype = {
PerformAction: function() {
var MyObjectRef = MyObject,
ActionResultHandler = this.ActionResultHandler;
this.Request = new XMLHttpRequest();
this.Request.open("GET", this.ConstructUrl(), true);
// From within ActionResultHandler, 'this' will always be the XMLHttpRequest
this.Request.onload = function () {
ActionResultHandler.apply(MyObjectRef, arguments);
};
this.Request.send(null);
}
}
Edited
Ok, I reread your question again and it seems that you want to execute ActionResultHandler in the context of MyObject, so I tweaked my code to do this.
Have you tried...
this.Request.onload = this.ActionResultHandler.apply(this);
I'm thinking that's what you're looking for (sorry if it's not). Using .apply(this) will point ActionResultHandler to Object.
Check out this article on binding while you're at it! It helped me out a lot.
I have such JS class that have to be tested:
SomeClass = function {
// some stuff that uses initRequest
this.initRequest = function() {
if (window.XMLHttpRequest) {
return new XMLHttpRequest();
} else if (window.ActiveXObject) {
return new ActiveXObject("Microsoft.XMLHTTP");
}
}
}
I want to override method initRequest for testing purposes. I tried to do something like that
var request = new MockXmlHttpRequest();
var instance = new SomeClass();
instance.initRequest = function() {
return request;
};
// some calls of the SomeClass methods that use initRequest
// some test code with assertions for the request
Still calling of the initRequest method calls actually the original code, but not the function that I tried to pass to instance.initRequest.
Any ideas what's wrong?
Check out YUI's extend functionality. It makes all this really clean, PLUS, you can still access the method of the super class if you really want to. Their augment object might also be of interest to you.
Here is the link http://developer.yahoo.com/yui/examples/yahoo/yahoo_extend.html
In your first example, initRequest returns a new object after each call; in the second example, all calls to initRequest return a reference to the one and only (mock) object.
The request object is not changed in the code example you provide. instance.initRequest simple returns a reference to request. Why do expect it to have changed?
I'm having a little trouble understanding your question because of your English, but I'll try to answer anyway (and if I answer the wrong question just let me know).
I think what you want to do is:
SomeClass = function {
// some stuff that uses initRequest
this.initRequest = function() {
return request;
}
}
In other words, you want to overwrite the original SomeClass object with a new function. Unless you do that new SomeClass objects won't use your test method, they'll just use the original method.
However, if you only want to override that method for a specific object, not for the whole class, what you have there should work. If that's the case, could you please clarify what exactly isn't working about it?
SomeClass.prototype.initRequest = function()
{
return request;
}
First of all, this code sample is not the best way of OOP in JavaScript - use prototype instead. Another remark, your sample throws error in my browser (invalid declarartion of SomeClass function, maybe it's only typing mistake). And at last, instance.initRequest actually returns (I've run it at Google Chrome) object you have expected - check please my sample.