Reference script container element? - javascript

I'm wondering if there is a way to get a handle on the DOM element that contains the script inside it. So if I had:
<script type="text/javascript> var x = ?; </script>
Is there a way that I can assign "x" a reference to the script element that contains "x"?

There isn't a truly safe way.
The closest you can come is to use getElementsByTagName to get all the scripts, get its length, get the last script element, then work from there.
This can fail if the script is deferred or if the script has been dynamically added to the page before another script element.

You could include some marker text in the script element, and then (similar to what David said), you can loop through all the script elements in the DOM (using getElementsByTagName or whatever features your library, if you're using one, may have). That should find the element reliably. That would look something like this (live example):
<body>
<script id='first' type='text/javascript'>
(function() {
var x = "MARKER:first";
})();
</script>
<script id='second' type='text/javascript'>
(function() {
var x = "MARKER:second";
})();
</script>
<script id='third' type='text/javascript'>
(function() {
var x = "MARKER:third";
})();
</script>
<script id='last' type='text/javascript'>
(function() {
var scripts, index, script;
scripts = document.getElementsByTagName("script");
for (index = 0; index < scripts.length; ++index) {
script = scripts[index];
if (script.innerHTML.indexOf("MARKER:second") >= 0
&& script.id !== "last") {
display("Found MARKER:second in script tag #" + script.id);
}
}
function display(msg) {
var p = document.createElement('p');
p.innerHTML = msg;
document.body.appendChild(p);
}
})();
</script>
</body>
Note that, like the script above, if you're looking for a script tag marker from within a different script tag, you'll need to handle that. Above it's handled by checking the ID of the script tag, but you can also just break it up in the one you don't want to find, like this (live example):
if (script.innerHTML.indexOf("MARKER:" + "second") >= 0) {
display("Found MARKER:" + "second in script tag #" + script.id);
}

Related

Use inline javascript to set some values [duplicate]

I'm wondering if there is a way to get a handle on the DOM element that contains the script inside it. So if I had:
<script type="text/javascript> var x = ?; </script>
Is there a way that I can assign "x" a reference to the script element that contains "x"?
There isn't a truly safe way.
The closest you can come is to use getElementsByTagName to get all the scripts, get its length, get the last script element, then work from there.
This can fail if the script is deferred or if the script has been dynamically added to the page before another script element.
You could include some marker text in the script element, and then (similar to what David said), you can loop through all the script elements in the DOM (using getElementsByTagName or whatever features your library, if you're using one, may have). That should find the element reliably. That would look something like this (live example):
<body>
<script id='first' type='text/javascript'>
(function() {
var x = "MARKER:first";
})();
</script>
<script id='second' type='text/javascript'>
(function() {
var x = "MARKER:second";
})();
</script>
<script id='third' type='text/javascript'>
(function() {
var x = "MARKER:third";
})();
</script>
<script id='last' type='text/javascript'>
(function() {
var scripts, index, script;
scripts = document.getElementsByTagName("script");
for (index = 0; index < scripts.length; ++index) {
script = scripts[index];
if (script.innerHTML.indexOf("MARKER:second") >= 0
&& script.id !== "last") {
display("Found MARKER:second in script tag #" + script.id);
}
}
function display(msg) {
var p = document.createElement('p');
p.innerHTML = msg;
document.body.appendChild(p);
}
})();
</script>
</body>
Note that, like the script above, if you're looking for a script tag marker from within a different script tag, you'll need to handle that. Above it's handled by checking the ID of the script tag, but you can also just break it up in the one you don't want to find, like this (live example):
if (script.innerHTML.indexOf("MARKER:" + "second") >= 0) {
display("Found MARKER:" + "second in script tag #" + script.id);
}

is there any way to print a javascript result into the div where the javascript function is being called?

I am wondering if is possible to do this.
I have my javascript function
function doSomething(){
return "Hello World"; //its actually more complicated method but the logic is the same..
}
so, at my html I have this.
<div class="someClass">
<script type="text/javascript">
doSomething();
</script>
</div>
So, basically, I want to is to print that hello world inside the div where is called.
Any idea how to do this?
AFAIK JavaScript cannot easily determine the location of its script declaration. As Marc B suggested in a comment--it works for printing to the document stream, but other applications won't work. Not to mention if you want the same behavior in multiple places, you've violated the "don't repeat yourself" (DRY) principle. You should instead inspect the dom document and find the element you wish to print "hello" in. This is easily accomplished with jQuery:
$(document).ready(function () {
$('.someClass').text('hello');
});
As laugri suggests, you should add an id to your div if you want only the one div changed.
<div id="someId".../>
...
$(document).ready(function () {
$('#someId').text('hello');
});
You can generate a unique div to find wherever you're code is executing
http://jsfiddle.net/190p77wu/
var doSomething = function (id) {
var targ = document.getElementById(id).parentNode;
targ.innerHTML = "This was inserted for id " + id;
}
and your html:
<div>
You shouldn't see this text.
<script>
var uniqueId = "tmp_" + Math.round(Math.random() * 100000000);
document.write('<div id="' + uniqueId + '"></div>');
doSomething(uniqueId);
</script>
</div>
Edit: or even cooler, if you have a properly cached JS file, you can do something like this:
http://jsfiddle.net/190p77wu/1/
var doSomething = function (script) {
var targ = script.parentNode;
targ.innerHTML = "This was inserted dynamically";
}
and this html:
<div>
You shouldn't see this text.
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js" onload="doSomething(this)"></script>
</div>
Try setting an id at script element , calling script.parentElement within doSomething
<div class="someClass">
<script type="text/javascript" id="someClass">
function doSomething() {
var elem = document.getElementById("someClass");
elem.parentElement.appendChild(
document.createTextNode("Hello World")
)
}
doSomething();
</script>
</div>

Loading scripts with document.write in an extension's content script

If a script contains:
document.write("<iframe>ads here</iframe>");
If it's included in the html before the page is requested for load, it might look something like this:
<html>
<!-- stuff !-->
<div><script src="document_write.js" type="text/javascript"></script></div>
<body>
</html>
Loading an html page with the code similar to above will result in the <iframe> being placed in the <div> tag which housed the script. If the document.write() is called after the page load, it will overwrite the whole page.
Chrome extensions' content scripts will also overwrite a page with document.write, or crash it - depending on when in the lifecycle of a page it was called.
Is there a way to insert scripts containing document.write() in Chrome's content scripts?
I had faced the same problem when I was working with some conversion tracking scripts on my ajax site. I ended up overriding document.write, which fixed the problem.
$(document).ready(function() {
document.write = function(str) {
var moz = !window.opera && !/Apple/.test(navigator.vendor);
if (str.match(/^<\//))
return;
if (!window.opera)
str = str.replace(/&(?![#a-z0-9]+;)/g, "&");
str = str.replace(/<([a-z]+)(.*[^\/])>$/, "<$1$2></$1>");
if (!moz)
str = str.replace(/(<[a-z]+)/g, "$1 xmlns='http://www.w3.org/1999/xhtml'");
var div = document.createElementNS("http://www.w3.org/1999/xhtml", "div");
div.innerHTML = str;
var pos;
if (!moz) {
pos = document.getElementsByTagName("*");
pos = pos[pos.length - 1];
} else {
pos = document;
while (pos.lastChild && pos.lastChild.nodeType == 1)
pos = pos.lastChild;
}
var nodes = div.childNodes;
while (nodes.length)
pos.parentNode.appendChild(nodes[0]);
};
});

only add script to head if doesn't exist

I want to add additional scripts and styles to my site when a specific div is loaded.
I start out by defining a path to either a script or stylesheet and then create an element. Hereafter I append the element to the head tag in HTML.
But I would like a way to see if the script or stylesheet already has been append before I append it again. It would be stupid to append an already existing script or stylesheet.
Q: How do I use javascript to check wether or not a script already exists in the head tag, and then append the element if not?
EDIT
I have made a function based on the answer from #KernelPanik. It doesn't work yet but hopefully it will. The function is currently in question: my script appending function doesn't work
If you can use jquery, this code is good
function appendScript(filepath) {
if ($('head script[src="' + filepath + '"]').length > 0)
return;
var ele = document.createElement('script');
ele.setAttribute("type", "text/javascript");
ele.setAttribute("src", filepath);
$('head').append(ele);
}
function appendStyle(filepath) {
if ($('head link[href="' + filepath + '"]').length > 0)
return;
var ele = document.createElement('link');
ele.setAttribute("type", "text/css");
ele.setAttribute("rel", "Stylesheet");
ele.setAttribute("href", filepath);
$('head').append(ele);
}
In your code write
appendScript('/Scripts/myScript.js');
appendStyle('/Content/myStyle.css');
var lib = '/public/js/lib.js';
if (!isLoadedScript(lib)) {
loadScript(lib);
}
// Detect if library loaded
function isLoadedScript(lib) {
return document.querySelectorAll('[src="' + lib + '"]').length > 0
}
// Load library
function loadScript(lib) {
var script = document.createElement('script');
script.setAttribute('src', lib);
document.getElementsByTagName('head')[0].appendChild(script);
return script;
}
You can use the DOM getElementsByTagName("script") to get all of the <script> tags in the document. Then you can check the src urls of each script tag returned, for the url of the script(s) that you have added to the head section. Likewise, you can do something similar for the style sheets by replacing the search of "script" with "style".
For example, if the url of the script appended to the <head> section is header_url.html
var x = document.getElementsByTagName("script");
var header_already_added = false;
for (var i=0; i< x.length; i++){
if (x[i].src == "header_url.html"){
// ... do not add header again
header_already_added = true;
}
}
if (header_already_added == false){
// add header if not already added
}
Likewise, if the url of the style appended to the <head> section is header_style.css
var x = document.getElementsByTagName("style");
var header_already_added = false;
for (var i=0; i< x.length; i++){
if (x[i].src == "header_style.css"){
// ... do not add header again
header_already_added = true;
}
}
if (header_already_added == false){
// add header if not already added
}
A similar question was also asked here: Check if Javascript script exists on page
I used Jack Lee's solution. It was easy to implement and quickly versitile with just about any type file.... I didn't expand on anything ...I actually probably stupefied it a bit... just wanted to list what I did in case it helps someone else...
var lib_jq = '//pathtofile/jquery.js';
var lib_bs = '//pathtofile/bootstrap.min.3.5.js';
var lib_cs = '//pathtofile.css';
///checks files with the SRC attribute
function isLoadedScript(lib) {
return document.querySelectorAll('[src="' + lib + '"]').length > 0
}
///checks files with the HREF attribute
function isLoadedCss(lib) {
return document.querySelectorAll('[href="' + lib + '"]').length > 0
}
///loads the script.js files
function loadScript(link) {
document.write('<scr' + 'ipt type="text/javascript" src="'+link+'"></scr' + 'ipt>');
}
///loads the style.css files
function loadCss(link) {
document.write('<link rel="stylesheet" href="'+link+'">');
}
/// run funtion; if no file is listed, then it runs the function to grab the URL listed. ///Run a seperate one for each file you wish to check.
if (!isLoadedScript(lib_jq)) { loadScript(lib_jq); }
if (!isLoadedScript(lib_bs)) { loadScript(lib_bs); }
if (!isLoadedCss(lib_cs)) { loadCss(lib_cs); }
I know there is always a "better" and more "elegant" solution, but for us beginiers, we got to get it working before we can start to understand it...
Another way with a function helper like below
function isScriptAlreadyPresent(url) {
var scripts = Array.prototype.slice.call(document.scripts);
return scripts.some(function(el) {
return el.src && el.src != undefined && el.src == url;
});
}
isScriptAlreadyPresent('http://your_script_url.tld/your_lib.js');
It uses Array.prototype.some function. You may need a es5-shim if your are in browsers not supporting ES5 (IE7 and IE8...)
maybe headjs can help you.
or maybe you can add onload attribute in the script tag.
my english is a little poor,so maybe i'm misunderstand your question.
if(ie){
js.onreadystatechange = function(){
if(js.readyState == 'complete'){
callback(js);
}
}
else{
js.onload = function(){
callback(js);
}
You can try calling some function, object, variable from that js script file, if it finds it then it exists, if not, you need to insert that js script file.

How do you execute a dynamically loaded JavaScript block?

I'm working on a web page where I'm making an AJAX call that returns a chunk of HTML like:
<div>
<!-- some html -->
<script type="text/javascript">
/** some javascript */
</script>
</div>
I'm inserting the whole thing into the DOM, but the JavaScript isn't being run. Is there a way to run it?
Some details: I can't control what's in the script block (so I can't change it to a function that could be called), I just need the whole block to be executed. I can't call eval on the response because the JavaScript is within a larger block of HTML. I could do some kind of regex to separate out the JavaScript and then call eval on it, but that's pretty yucky. Anyone know a better way?
Script added by setting the innerHTML property of an element doesn't get executed. Try creating a new div, setting its innerHTML, then adding this new div to the DOM. For example:
<html>
<head>
<script type='text/javascript'>
function addScript()
{
var str = "<script>alert('i am here');<\/script>";
var newdiv = document.createElement('div');
newdiv.innerHTML = str;
document.getElementById('target').appendChild(newdiv);
}
</script>
</head>
<body>
<input type="button" value="add script" onclick="addScript()"/>
<div>hello world</div>
<div id="target"></div>
</body>
</html>
You don't have to use regex if you are using the response to fill a div or something. You can use getElementsByTagName.
div.innerHTML = response;
var scripts = div.getElementsByTagName('script');
for (var ix = 0; ix < scripts.length; ix++) {
eval(scripts[ix].text);
}
While the accepted answer from #Ed. does not work on current versions of Firefox, Google Chrome or Safari browsers I managed to adept his example in order to invoke dynamically added scripts.
The necessary changes are only in the way scripts are added to DOM. Instead of adding it as innerHTML the trick was to create a new script element and add the actual script content as innerHTML to the created element and then append the script element to the actual target.
<html>
<head>
<script type='text/javascript'>
function addScript()
{
var newdiv = document.createElement('div');
var p = document.createElement('p');
p.innerHTML = "Dynamically added text";
newdiv.appendChild(p);
var script = document.createElement('script');
script.innerHTML = "alert('i am here');";
newdiv.appendChild(script);
document.getElementById('target').appendChild(newdiv);
}
</script>
</head>
<body>
<input type="button" value="add script" onclick="addScript()"/>
<div>hello world</div>
<div id="target"></div>
</body>
</html>
This works for me on Firefox 42, Google Chrome 48 and Safari 9.0.3
An alternative is to not just dump the return from the Ajax call into the DOM using InnerHTML.
You can insert each node dynamically, and then the script will run.
Otherwise, the browser just assumes you are inserting a text node, and ignores the scripts.
Using Eval is rather evil, because it requires another instance of the Javascript VM to be fired up and JIT the passed string.
The best method would probably be to identify and eval the contents of the script block directly via the DOM.
I would be careful though.. if you are implementing this to overcome a limitation of some off site call you are opening up a security hole.
Whatever you implement could be exploited for XSS.
You can use one of the popular Ajax libraries that do this for you natively. I like Prototype. You can just add evalScripts:true as part of your Ajax call and it happens automagically.
For those who like to live dangerously:
// This is the HTML with script element(s) we want to inject
var newHtml = '<b>After!</b>\r\n<' +
'script>\r\nchangeColorEverySecond();\r\n</' +
'script>';
// Here, we separate the script tags from the non-script HTML
var parts = separateScriptElementsFromHtml(newHtml);
function separateScriptElementsFromHtml(fullHtmlString) {
var inner = [], outer = [], m;
while (m = /<script>([^<]*)<\/script>/gi.exec(fullHtmlString)) {
outer.push(fullHtmlString.substr(0, m.index));
inner.push(m[1]);
fullHtmlString = fullHtmlString.substr(m.index + m[0].length);
}
outer.push(fullHtmlString);
return {
html: outer.join('\r\n'),
js: inner.join('\r\n')
};
}
// In 2 seconds, inject the new HTML, and run the JS
setTimeout(function(){
document.getElementsByTagName('P')[0].innerHTML = parts.html;
eval(parts.js);
}, 2000);
// This is the function inside the script tag
function changeColorEverySecond() {
document.getElementsByTagName('p')[0].style.color = getRandomColor();
setTimeout(changeColorEverySecond, 1000);
}
// Here is a fun fun function copied from:
// https://stackoverflow.com/a/1484514/2413712
function getRandomColor() {
var letters = '0123456789ABCDEF';
var color = '#';
for (var i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
}
<p>Before</p>

Categories