eval JS in AJAX loaded contents - javascript

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().

Related

Inject an opened window with script

This question asks for a way to open a new window using window.open and then inject it with a script. It was not possible because of cross-domain security issues.
However, my problem is that I want to do the exact same thing, except from the same domain to the same domain. Is this possible?
Note that .write does not solve this problem because it wipes all the html from the page first.
You can do something like this:
var theWindow = window.open('http://stackoverflow.com'),
theDoc = theWindow.document,
theScript = document.createElement('script');
function injectThis() {
// The code you want to inject goes here
alert(document.body.innerHTML);
}
theScript.innerHTML = 'window.onload = ' + injectThis.toString() + ';';
theDoc.body.appendChild(theScript);
This also seems to work:
var theWindow = window.open('http://stackoverflow.com'),
theScript = document.createElement('script');
function injectThis() {
// The code you want to inject goes here
alert(document.body.innerHTML);
}
// Self executing function
theScript.innerHTML = '(' + injectThis.toString() + '());';
theWindow.onload = function () {
// Append the script to the new window's body.
// Only seems to work with `this`
this.document.body.appendChild(theScript);
};
And if for some reason you want to use eval:
var theWindow = window.open('http://stackoverflow.com'),
theScript;
function injectThis() {
// The code you want to inject goes here
alert(document.body.innerHTML);
}
// Self executing function
theScript = '(' + injectThis.toString() + '());';
theWindow.onload = function () {
this.eval(theScript);
};
What this does (Explanation for the first bit of code. All examples are quite similar):
Opens the new window
Gets a reference to the new window's document
Creates a script element
Places all the code you want to 'inject' into a function
Changes the script's innerHTML to load said function when the window
loads, with the window.onload event (you can also use addEventListener). I used toString() for convenience, so you don't have to concatenate a bunch of strings. toString basically returns the whole injectThis function as a string.
Appends the script to the new window's document.body, it won't actually append it to the document that is loaded, it appends it before it loads (to an empty body), and that's why you have to use window.onload, so that your script can manipulate the new document.
It's probably a good idea to use window.addEventListener('load', injectThis.toString()); instead of window.onload, in case you already have a script within your new page that uses the window.onload event (it'd overwrite the injection script).
Note that you can do anything inside of the injectThis function: append DIVs, do DOM queries, add even more scripts, etc...
Also note that you can manipulate the new window's DOM inside of the theWindow.onload event, using this.
Yes...
var w = window.open(<your local url>);
w.document.write('<html><head>...</head><body>...</body></html>');
Here's a trick I use, it uses query strings, and is client side. Not perfect but it works:
On the sending page, do:
var javascriptToSend = encodeURIComponent("alert('Hi!');");
window.open('mypage.html?javascript=' + javascriptToSend);
Replace mypage.html with your page. Now on the receiving page, add:
(location.href.match(/(?:javascript)=([^&]+)/)[1])&&eval(decodeURIComponent(location.href.match(/(?:javascript)=([^&]+)/)[1]));
You'll have to do some back-and forth to make sure this works.
If you HAVE PHP you can use this more reliable solution on the receiving page:
eval(decodeURIComponent(<?=$_GET['javascript'] ?>));

inline javascript function not detecting script loaded via code

This works:
<script src="some.js">
<script>
afunction(); //this function is in some.js
</script>
Then I thought to improve the page speed of the site and load some.js like this
var element = document.createElement("script");
element.src = "some.js;
document.body.appendChild(element);
and now the function doesn't exist and i get an error.
Is there a solution to this?
As others pointed out above, some of the errors you might be encountering are perhaps due to missing " and so on. Assuming that you have every syntax correct, this approach will fail because how HTML parsing happens:
Assuming that you've got this code in the <HEAD> section:
When the parser (in your browser) reads the file stream where you've got this code, it is going to construct the DOM as it is doing so. This means that when it gets to the point in your code where you are telling it to append as a child your script node element to the body, it is going to fail because document.body does not exist yet and this is an error.
Assuming that you've got this code in the <BODY> section:
Then by the mere that you have attached a SCRIPT node element to the DOM does not imply that the file has been loaded by the Javascript engine and processed the content of "some.js". Therefore, when "aFunction" is called, it is undefined.
Finally, i do not think you gain anything peformance-wise by loading your script after your document has loaded.
There are many Syntax Error in your Script
But the answer to the actuall question is window.onload
<script>
window.onload = function () {
afunction();
}
</script>
Syntax Need Correction
1) Close Script Tag :
<script src="some.js"></script>
<script>
afunction(); //this function is in some.js
</script>
2) Missing double quote(")
var element = document.createElement("script");
element.src = "some.js";
document.body.appendChild(element);
The function needs to be written globally like,
$(document).ready(function () {
});
afunction () {
}
in 'some.js' script.
and access tat function using div tag.
<div id = 'a' onClick='afunction()'>
</div>
The problem is caused by calling the function too soon after the node has been appended. All you need is a timeout.
<script type="text/javascript">
var elem = document.createElement("script");
elem.src = "scripts/test.js";
elem.type="text/javascript";
//
function runTest()
{ document.body.appendChild(elem);
setTimeout("alertThis('This is a test')",50);
}
window.onload=runTest;
</script>
The external js for this example says
function alertThis(msg) { alert(msg); }
It runs correctly in all browsers.

Why isn't jQuery.append working?

I am in the middle of creating a small script to 'help' me with my homework. It uses jQuery. The script (so far) is below:
var s = document.createElement('script');
document.body.appendChild(s);
s.src = "http://code.jquery.com/jquery-latest.js"; // Include jQuery
var tmp_1 = document.embeds[0].GetVariable("q1answers"); // Read raw values
var answers_1 = tmp_1.split(","); // Explode into array
answers_1.splice(0,1); // Remove first element (always 0)
var tmp_2 = document.embeds[0].GetVariable("q2answers");
var answers_2 = tmp_2.split(",");
answers_2.splice(0,1);
answers_1.push("LINE_BREAK");
var answers = answers_1.concat(answers_2);
$("body").append("<div id='answers-wrap'></div>");
$("#answers-wrap").css("position", "fixed");
$("#answers-wrap").css("background", "none");
The problem arises when it gets to the 3rd-to-last line. Chrome console claims that Object #<HTMLBodyElement> has no method 'append', however if I extract that line and put it into the console on its own, it works fine. I can use a different method to insert HTML, but I would like to know what isn't working with this one.
Thanks in advance!
Since you're adding the jQuery script dynamically, it's loaded asynchronously, so it's probably not loaded yet when you're trying to use it. Use an onload handler for the dynamic script block:
var s = document.createElement('script');
document.body.appendChild(s);
s.src = "http://code.jquery.com/jquery-latest.js"; // Include jQuery
s.onload = function() {
$("body").append("<div id='answers-wrap'></div>");
$("#answers-wrap").css("position", "fixed");
$("#answers-wrap").css("background", "none");
}
The error message you're getting also indicates that $ exists (another library, maybe?) and is not returning a jQuery object, so you'll probably have to use jQuery in "noConflit" mode, and use jQuery or a user-defined alias instead of $.
Just a guess, but may you are running the script before the browser has finished rendering the DOM?
Try wrapping the code in
window.onload = function(){
// ... your code here
};
in order to execute it onload.
EDIT: changed code to reflect the feedback below, of course one cannot use jQuery's $ before jQuery is loaded, my fault.

To use the response of XDomainRequest outside of the function "load"

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.

How do I pass argument to anonymous Javascript function?

I am writing a simple counter, and I would like to make installation of this counter very simple for users. One of the simplest counter code (for users who install it) I ever see was Google Analytics Code
So I would like to store main code in a file and user who will install my counter will need just to set websiteID like this:
<html><head><title></title></head><body>
<script type="text/javascript" src="http://counterhost.lan/tm.js">
var websiteId = 'XXXXX';
</script>
</body></html>
Here is my code:
<script type="text/javascript" src="http://counterhost.lan/tm.js">
var page = _gat.init('new');
</script>
and this is my JS file:
(function() {
var z = '_gat';
var aa = function init(data) { alert(data); alert(z);};
function na() {
return new z.aa();
}
na();
})();
I tried to understand Google Analytics javascript code but I failed to do this. Can anyone suggest how can I specify variable between tags and then read it in anonymous function which is located in a javascript file ?
Thanks.
In your example, websiteId is a global variable. So it is accessible everywhere including anonymous functions unless there is a local variable with the same name
<script> var websiteId = "something"; </script>
Later in the page or included js file...
(function() {
alert(websiteId); //this should work
})();
Can anyone suggest how can I specify variable between tags and then read it [...]
Not if your tag has both a SRC attribute and JS content.
<script type="text/javascript" src="http:/x.com/x.js"></script>
.. is different from,
<script type="text/javascript">
var x = 1;
</script>
One framework that optionally adds JS variables to SCRIPT tags is Dojo. So if you're using Dojo you can add variables to the global djConfig hash by writing,
<script type="text/javascript" src="mxclientsystem/dojo/dojo.js"
djConfig="
usePlainJson: true,
parseOnLoad: true
">
</script>
Dojo does this by running through the SCRIPT tags and evaluating the custom djConfig attribute.
This does not, however solve your problem.
You do really want two SCRIPT tags. One saying,
<script type="text/javascript">
var websiteId = '123456';
</script>
which will set a global variable websiteId and a second one,
<script type="text/javascript" src="http:/x.com/myreporter.js"></script>
which can load from anywhere and read out the websiteId variable and, I assume, report it back.
You can pass variables to an anonymous function like so:
(function(arg1, arg2, arg3) {
alert(arg1);
alert(arg2);
alert(arg3);
})("let's", "go", "redsox");
// will alert "let's", then "go", then "redsox" :)
I'm not entirely clear about what you're asking, but...
You can tag any HTML element with an id attribute, then use
document.getEntityById() to retrieve that specific element.
You can also give any HTML element user-defined attributes having names of your own choosing, then get and set them for that element within Javascript.
I think you've got a bit confused with how JS objects are called.
z is a String, '_gat'. You can't call aa() on it because a String has no member called aa. aa is a standalone function stored in a local variable. Even if you did call aa(), it doesn't return anything, so using the new operator on its results is meaningless. new can only be called on constructor-functions.
I guess you mean something like:
var _gat= function() {
// Private variable
//
var data= null;
// Object to put in window._gat
//
return {
// Set the private variable
//
init: function(d) {
data= d;
}
};
}();
Then calling _gat.init('foo') as in your second example would set the variable to website ID 'foo'. This works because the _gat object is the return {init: function() {...}} object defined inside the anonymous function, keeping a reference (a ‘closure’) on the hidden data variable.
If you specify a src attribute as part of a script element, any code within the script element tags themselves will not be executed. However, you can add this functionality with the following code. I got this technique from Crockford (I believe it was him), where he uses it in of his talks on the unrelated topic of rendering performance and asynchronously loading scripts into a page to that end.
JavaScript:
(function() {
// Using inner class example from bobince's answer
var _gat = (function() {
var data= null;
return {
init: function(d) {
console.info("Configuration data: ", d);
data = d;
}
}
})();
// Method 1: Extract configuration by ID (SEE FOOT NOTE)
var config = document.getElementById("my-counter-apps-unique-and-long-to-avoid-collision-id").innerHTML;
// Method 2: search all script tags for the script with the expected name
var scripts = document.getElementsByTagName("script");
for ( var i=0, l=scripts.length; i<l; ++i ) {
if ( scripts[i].src = "some-script.js" ) {
config = scripts[i].innerHTML;
break;
}
}
_gat.init( eval("(" +config+ ")") );
})();
HTML:
<script type="text/javascript" src="some-script.js" id="my-counter-apps-unique-and-long-to-avoid-collision-id">
{some: "foo", config: "bar", settings: 123}
</script>
Both methods have their draw backs:
Using a unique and non-colliding ID will make determining the proper script element more precise and faster; however, this is not valid HTML4/XHTML markup. In HTML5, you can define arbitrary attributes, so it wont be an issue at that time
This method is valid HTML markup; however, the simple comparison that I have shown can be easily broken if your url is subject to change (e.g.: http vs https) and a more robust comparison method may be in order
A note on eval
Both methods make use of eval. The typical mantra concerning this feature is that "eval is evil." However, that goes with say that using eval without knowing the dangers of eval is evil.
In this case, AFAIK, the data contained within the script tags is not subject to inject attack since the eval'ing script (the code shown) is executed as soon as that element is reached when parsing the HTML into the DOM. Scripts that may have been defined previously are unable to access the data contained within the counter's script tags as that node does not exist in the DOM tree at the point when they are executed.
It may be the case that a well timed setTimeout executed from a previously included script may be able to run at the time between the counter's script's inclusion and the time of the eval; however, this may or may not be the case, and if possible, may not be so consistently depending on CPU load, etc.
Moral of the story, if you're worried about it, include a non-eval'ing JSON parser and use that instead.

Categories