I want to change the behavior of a JavaScript used to display a banner, coming from a central source.
Today I include a script-tag inline in code, like this:
<script type="text/javascript" src="http://banner.com/b?id=1234"></script>
But what that returns is code which uses document.write, like this:
if(condition) {
document.write('<img src="..." />')
}
I want to somehow override this document.write and maybe evaluate the returned code and instead use a JavaScript-framework to bind code to a div-element at DOM ready.
Is there a way to do that? Something like this?:
OnDOMReady() {
BindBanner(1234);
}
BindBanner(bannerId) {
var divTag = document.getElementById('banner_' + bannerId);
divTag.innerHtml = ManipulatedReturenedCode(bannerId);
}
Can JavaScript's prototyping handle something like this?
Edit: It has to be somewhat waterproof cross-platform, cross-browser-compatible, so I don't know if changing document.write is ok.
Yes, you can override document.write. Prototyping is not necessary, you can do it directly on the document object itself. I do this commonly for analysing malware, but it could certainly be used to capture ad script output, as long as the ad script doesn't do any particularly convoluted processing that would turn up the difference between document.write and whatever you replaced it with.
Here's a wrapper that loads an ad onload (slightly later than DOMReady):
<div id="advertgoeshere"></div>
<script type="text/javascript">
function rewrite(w) {
document.getElementById('advertgoeshere').innerHTML+= w;
}
window.onload= function() {
document.write= rewrite;
var script= document.createElement('script');
script.type= 'text/javascript';
script.src= 'http://externalsite/ads.js';
document.body.appendChild(script);
}
</script>
Have you tried overriding document.write? I don't have time to try it right now but give this a go:
var foo = document.write;
var bannerCode = '';
document.write = function(str) { bannerCode += str; };
Then include the script file, then do
document.write = foo;
alert(bannerCode);
Why not use jQuery? It has a DOM ready function:
$.ready(function() {
});
Then you can easily manipulate an element using
$("#div_id").html
Related
I want to load some script tag from the server as a string and to append it to HTML header, but even though I can append it, it doesn't execute. Here is the simplified HTML file to illustrate this situation:
<head>
</head>
<body>
<script>
function htmlStringToElement(htmlString) {
var template = document.createElement('template');
htmlString = htmlString.trim();
template.innerHTML = htmlString;
return template.content.firstChild;
}
//Mocking http request
setTimeout(function() {
var httpResponseMock = '<script>alert("HELLO FROM HTTP RESPONSE!");<\/script>';
var script = htmlStringToElement(httpResponseMock);
document.head.appendChild(script);
}, 1000);
</script>
</body>
I suppose that the reason is that header has already been rendered when the script is added dynamically but is there any other way to achieve this?
With Jquery,
var httpResponseMock = '<script><\/script>';
$('head').append(httpResponseMock);
and with javascript
var httpResponseMock = '<script><\/script>';
document.getElementsByTagName('head')[0].appendChild(httpResponseMock);
don't ever use innerHTML unless you know what you are doing.
if you really want to dynamically inject script into the document just do this or use eval:
const script = document.createElement("script");
script.textContent = "console.log('yay it works!');";
document.head.appendChild(script);
the appendChild is running it.
There is a longer discussion in another question about dynamic loading of JS. The simple answer in this case is to use eval to evaluate the script content. Please note though that using eval is considered mostly a bad practice.
I'm having problems getting this to work. I first tried setting my script tags as strings and then using jquery replaceWith() to add them to the document after page load:
var a = '<script type="text/javascript">some script here</script>';
$('#someelement').replaceWith(a);
But I got string literal errors on that var. I then tried encoding the string like:
var a = '&left;script type="text/javascript">some script here<\/script>';
but sending that to replaceWith() outputs just that string to the browser.
Can someone please let me know how you would go about dynamically adding a <script> tag into the browser after page load, ideally via jQuery?
You can put the script into a separate file, then use $.getScript to load and run it.
Example:
$.getScript("test.js", function(){
alert("Running test.js");
});
Try the following:
<script type="text/javascript">
// Use any event to append the code
$(document).ready(function()
{
var s = document.createElement("script");
s.type = "text/javascript";
s.src = "http://scriptlocation/das.js";
// Use any selector
$("head").append(s);
});
http://api.jquery.com/append
Here's the correct way to do it with modern (2014) JQuery:
$(function () {
$('<script>')
.attr('type', 'text/javascript')
.text('some script here')
.appendTo('head');
})
or if you really want to replace a div you could do:
$(function () {
$('<script>')
.attr('type', 'text/javascript')
.text('some script here')
.replaceAll('#someelement');
});
A simpler way is:
$('head').append('<script type="text/javascript" src="your.js"></script>');
You can also use this form to load css.
This answer is technically similar or equal to what jcoffland answered.
I just added a query to detect if a script is already present or not.
I need this because I work in an intranet website with a couple of modules, of which some are sharing scripts or bring their own, but these scripts do not need to be loaded everytime again. I am using this snippet since more than a year in production environment, it works like a charme. Commenting to myself: Yes I know, it would be more correct to ask if a function exists... :-)
if (!$('head > script[src="js/jquery.searchable.min.js"]').length) {
$('head').append($('<script />').attr('src','js/jquery.searchable.min.js'));
}
Here is a much clearer way — no need for jQuery — which adds a script as the last child of <body>:
document.body.innerHTML +='<script src="mycdn.js"><\/script>'
But if you want to add and load scripts use Rocket Hazmat's method.
Example:
var a = '<script type="text/javascript">some script here</script>';
$('#someelement').replaceWith(a);
It should work. I tried it; same outcome. But when I used this:
var length = 1;
var html = "";
for (var i = 0; i < length; i++) {
html += '<div id="codeSnippet"></div>';
html += '<script type="text/javascript">';
html += 'your script here';
html += '</script>';
}
$('#someElement').replaceWith(a);
This worked for me.
Edit: I forgot the #someelement (btw I might want to use #someElement because of conventions)
The most important thing here is the += so the html is added and not replaced.
Leave a comment if it didn't work. I'd like to help you out!
There is one workaround that sounds more like a hack and I agree it's not the most elegant way of doing it, but works 100%:
Say your AJAX response is something like
<b>some html</b>
<script>alert("and some javscript")
Note that I've skipped the closing tag on purpose. Then in the script that loads the above, do the following:
$.ajax({
url: "path/to/return/the-above-js+html.php",
success: function(newhtml){
newhtml += "<";
newhtml += "/script>";
$("head").append(newhtml);
}
});
Just don't ask me why :-) This is one of those things I've come to as a result of desperate almost random trials and fails.
I have no complete suggestions on how it works, but interestingly enough, it will NOT work if you append the closing tag in one line.
In times like these, I feel like I've successfully divided by zero.
If you are trying to run some dynamically generated JavaScript, you would be slightly better off by using eval. However, JavaScript is such a dynamic language that you really should not have a need for that.
If the script is static, then Rocket's getScript-suggestion is the way to go.
document.body.innerHTML = "<script>alert(11);</script>"
$("body").html("<script>alert(11);</script>")
innerHTML is not executed.
jQuery html() is executed.
Why so?
Without getting into the theoretical questions of why jQuery chose to do this, the jQuery html() behaves differently than the native innerHTML. By default, jQuery will find the script tags within the HTML, and load then asynchronously. If this behavior is undesirable, you can use $.parseHTML to prevent this from happening by setting the third argument to false.
$("body").empty().append($.parseHTML("<script>alert(11);</script>", document, false));
Note that the script tags will not be added to the DOM using this method.
Conversely, if you wish to achieve the same affect as your jQuery statement in vanilla JS, you can do the following.
var script = document.createElement('script');
script.text = 'alert(11);';
document.body.innerHTML = '';
document.body.appendChild(script);
Try this...
var x = document.createElement('script');
var y = document.getElementsByTagName('script')[0];
x.text = "alert(11);"
y.parentNode.insertBefore(x, y);
Using innerHTML will stop the script from executing according to the documentation in simple words without going into details.
<script type="text/javascript">
var script = document.createElement('script');
script.appendChild(document.createTextNode("alert('11')"));
document.body.appendChild(script);
</script>
Using JavaScript, is there a way to detect whether or not an external script (from a third-party vendor) has completely loaded?
The script in question is used to pull in and embed the markup for a list of jobs and, unfortunately, doesn't make use of any variables or functions. It uses document.write to output all of the content that gets embedded in my page.
Ideally, I'd like to display some kind of loading message while I'm waiting for the external script to load, and if it fails to load, display a "We're sorry, check back later..." message.
I'm using jQuery on the site, but this external script is called before I make the jQuery call.
Here's what the document.write stuff from the external script looks like:
document.write('<div class="jt_job_list">');
document.write("
<div class=\"jt_job jt_row2\">
<div class=\"jt_job_position\">
Position Title
</div>
<div class=\"jt_job_location\">City, State</div>
<div class=\"jt_job_company\">Job Company Name</div>
</div>
");
Attach an function to the load event:
<script type="text/javascript" src="whatever.js" onload ="SomeFunction()" />
As far as your loading... problem goes, try displaying a div for loading and then just display:none-ing it in your onload function. Make sure to handle cases where your script fails to load too, though.
Script tags block downloads, so as long as the content dependent on your script is below where your script it loaded, you should be fine. This is true even if the script is in-line in the body of your page.
This website has a great example of how this works.
This obviously does not work if you're loading the scripts asynchronously.
Scripts without async or defer attributes are fetched and executed immediately, before the browser continues to parse the page.
Source: MDN
You could put a script block after it on the page:
<script src="external_script.js"></script>
<script type="text/javascript">
ExternalScriptHasLoaded();
</script>
Thanks for the assistance above, especially ngmiceli for the Steve Souders link!
I decided to take what's probably a "lazy" approach, and also forego the "loading" message:
$(document).ready(function(){
if ($('.jt_job_list').length === 0){
$('#job-board').html("<p>We're sorry, but the Job Board isn't currently available. Please try again in a few minutes.</p>");
};
});
Pretty simple, but I'm looking to see if an element with the .jt_job_list class is in the dom. If it isn't, I display an error message.
This worked for me: it does however, rely on the newer querySelector interface which most modern browsers support. But if you're using really old browsers, you can use getElement... and run a for loop.
function loadJS(file, callback, error, type) {
var _file = file ;
var loaded = document.querySelector('script[src="'+file+'"]') ;
if (loaded) {
loaded.onload = callback ;
loaded.onreadystatechange = callback;
return
}
var script = document.createElement("script");
script.type = (typeof type ==="string" ? type : "application/javascript") ;
script.src = file;
script.async = false ;
script.defer = false ;
script.onload = callback ;
if (error) {
script.onerror = error ;
}
else {
script.onerror = function(e) {
console.error("Script File '" + _file + "' not found :-(");
};
}
script.onreadystatechange = callback;
document.body.appendChild(script);
}
You could give what ever your looking for an ID
and check whether not the ID has been loaded using document.getElementById("ID");
Is that what your looking for not sure I fully understand?
Lets suppose that I have the following markup:
<div id="placeHolder"></div>
and I have a JavaScript variable jsVar that contains some markup and some JavaScript.
By using Mootools 1.1 I can inject the JavaScript content into the placeholder like this:
$('placeHolder').setHTML(jsVar);
This works in Firefox, Opera, and even Safari and the resulting markup looks like this:
<div id="placeHolder">
<strong>I was injected</strong>
<script type="text/javascript">
alert("I was injected too!");
</script>
</div>
However, on IE 8 I get the following:
<div id="placeHolder">
<strong>I was injected</strong>
</div>
Is there any way to inject the JavaScript on IE 8 or does it security model forbid me from doing this at all?
I tried Luca Matteis' suggestion of using
document.getElementById("placeHolder").innerHTML = jsVar;
instead of the MooTools code and I get the same result. This is not a MooTools issue.
This MSDN post specifically addresses how to use innerHTML to insert javascript into a page. You are right: IE does consider this a security issue, so requires you to jump through certain hoops to get the script injected... presumably hackers can read this MSDN post as well as we can, so I'm at a loss as to why MS considers this extra layer of indirection "secure", but I digress.
From the MSDN article:
<HTML>
<SCRIPT>
function insertScript(){
var sHTML="<input type=button onclick=" + "go2()" + " value='Click Me'><BR>";
var sScript="<SCRIPT DEFER>";
sScript = sScript + "function go2(){ alert('Hello from inserted script.') }";
sScript = sScript + "</SCRIPT" + ">";
ScriptDiv.innerHTML = sHTML + sScript;
}
</SCRIPT>
<BODY onload="insertScript();">
<DIV ID="ScriptDiv"></DIV>
</BODY>
</HTML>
If at all possible, you may wish to consider using a document.write injected script loading tag to increase security and reduce cross-browser incompatibility. I understand this may not be possible, but it's worth considering.
This is how we did it on our site about a year ago to get it working in IE. Here are the steps:
add the HTML to an orphan DOM element
search the orphan node for script tags (orphan.getElementsByTagName)
get the code from those script nodes (save for later), and then remove them from the orphan
add the html leftover that is in the orphan and add it to the placeholder (placeholder.innerHTML = orphan.innerHTML)
create a script element and add the stored code to it (scriptElem.text = 'alert("my code");')
then add the script element to the DOM (preferably the head), then remove it
function set_html( id, html ) {
// create orphan element set HTML to
var orphNode = document.createElement('div');
orphNode.innerHTML = html;
// get the script nodes, add them into an arrary, and remove them from orphan node
var scriptNodes = orphNode.getElementsByTagName('script');
var scripts = [];
while(scriptNodes.length) {
// push into script array
var node = scriptNodes[0];
scripts.push(node.text);
// then remove it
node.parentNode.removeChild(node);
}
// add html to place holder element (note: we are adding the html before we execute the scripts)
document.getElementById(id).innerHTML = orphNode.innerHTML;
// execute stored scripts
var head = document.getElementsByTagName('head')[0];
while(scripts.length) {
// create script node
var scriptNode = document.createElement('script');
scriptNode.type = 'text/javascript';
scriptNode.text = scripts.shift(); // add the code to the script node
head.appendChild(scriptNode); // add it to the page
head.removeChild(scriptNode); // then remove it
}
}
set_html('ph', 'this is my html. alert("alert");');
I have encountered the same issues with IE8 (and IE7)
The only way I could dynamically inject a script (with an src) is by using a timer:
source = "bla.js";
setTimeout(function () {
// run main code
var s = document.createElement('script');
s.setAttribute('src', source);
document.getElementsByTagName('body')[0].appendChild(s);
}, 50);
If you have inline code you would like to inject, you can drop the timer and use the "text" method for the script element:
s.text = "alert('hello world');";
I know my answer has come pretty late; however, better late than never :-)
I am not sure about MooTools, but have you tried innerHTML ?
document.getElementById("placeHolder").innerHTML
= jsVar;
You may need to eval the contents of the script tag. This would require parsing to find scripts in your jsVar, and eval(whatsBetweenTheScriptTags).
Since IE refuses to insert the content by default you will have to execute it yourself, but you can at least trick IE into doing the parsing for you.
Simply use string.replace() to swap all the <script> tags for <textarea class="myScript" style="display:none">, preserving the content. Then stick the result into an innerHTML of a div.
After this is done, you can use
div.getElementsByTagName("textarea")
to get all the textareas, loop through them and look for your marker class ("myScript" in this case), and either eval(textarea.value) or (new Function(textarea.value))() the ones you care about.
I never tried it, it just came to my mind... Can you try the following:
var script = document.createElement('script');
script.type = 'text/javascript';
script.innerHTML = '//javascript code here'; // not sure if it works
// OR
script.innerText = '//javascript code here'; // not sure if it works
// OR
script.src = 'my_javascript_file.js';
document.getElementById('placeholder').appendChild(script);
You can use the same technique (DOM) to insert HTML markup.
I am sorry, perhaps I am missing something here--but with this being a mootools 1.11 question, why don't you use assets.js?
// you can also add a json argument with events, etc.
new Asset.javascript("path-to-script.js", {
onload: function() {
callFuncFromScript();
},
id: "myscript"
});
Isn't one of the reasons why we're using a framework not to have to reinvent the wheel all over again...
as far as the 'other' content is concerned, how do you happen to get it? if through the Request class, it can do what you want nicely by using the options:
{
update: $("targetId"),
evalScripts: true,
evalResponse: false
}
When you say it "works" in those other browsers, do you mean you get the alert popup message, or do you just mean the <script> tag makes it into the DOM tree?
If your goal is the former, realize that the behaviour of injecting html with embedded <script> is very browser-dependent. For example in the latest MooTools I can try:
$(element).set('html', '<strong>Foo</strong><script>alert(3)</script>')
and I do not get the popup, not in IE(7), not in FF(3) (however I do get the <script> node into the DOM successfully). To get it to alert in all browsers, you must do as this answer does.
And my comment is really late, but it's also the most accurate one here - the reason you're not seeing the <script> contents running is because you didn't add the defer attribute to the <script> tag. The MSDN article specifically says you need to do that in order for the <script> tag to run.