If I have a script tag like this:
<script
id = "myscript"
src = "http://www.example.com/script.js"
type = "text/javascript">
</script>
I would like to get the content of the "script.js" file. I'm thinking about something like document.getElementById("myscript").text but it doesn't work in this case.
tl;dr script tags are not subject to CORS and same-origin-policy and therefore javascript/DOM cannot offer access to the text content of the resource loaded via a <script> tag, or it would break same-origin-policy.
long version:
Most of the other answers (and the accepted answer) indicate correctly that the "correct" way to get the text content of a javascript file inserted via a <script> loaded into the page, is using an XMLHttpRequest to perform another seperate additional request for the resource indicated in the scripts src property, something which the short javascript code below will demonstrate. I however found that the other answers did not address the point why to get the javascript files text content, which is that allowing to access content of the file included via the <script src=[url]></script> would break the CORS policies, e.g. modern browsers prevent the XHR of resources that do not provide the Access-Control-Allow-Origin header, hence browsers do not allow any other way than those subject to CORS, to get the content.
With the following code (as mentioned in the other questions "use XHR/AJAX") it is possible to do another request for all not inline script tags in the document.
function printScriptTextContent(script)
{
var xhr = new XMLHttpRequest();
xhr.open("GET",script.src)
xhr.onreadystatechange = function () {
if(xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
console.log("the script text content is",xhr.responseText);
}
};
xhr.send();
}
Array.prototype.slice.call(document.querySelectorAll("script[src]")).forEach(printScriptTextContent);
and so I will not repeat that, but instead would like to add via this answer upon the aspect why itthat
Do you want to get the contents of the file http://www.example.com/script.js? If so, you could turn to AJAX methods to fetch its content, assuming it resides on the same server as the page itself.
Update: HTML Imports are now deprecated (alternatives).
---
I know it's a little late but some browsers support the tag LINK rel="import" property.
http://www.html5rocks.com/en/tutorials/webcomponents/imports/
<link rel="import" href="/path/to/imports/stuff.html">
For the rest, ajax is still the preferred way.
I don't think the contents will be available via the DOM. You could get the value of the src attribute and use AJAX to request the file from the server.
yes, Ajax is the way to do it, as in accepted answer. If you get down to the details, there are many pitfalls. If you use jQuery.load(...), the wrong content type is assumed (html instead of application/javascript), which can mess things up by putting unwanted <br> into your (scriptNode).innerText, and things like that. Then, if you use jQuery.getScript(...), the downloaded script is immediately executed, which might not be what you want (might screw up the order in which you want to load the files, in case you have several of those.)
I found it best to use jQuery.ajax with dataType: "text"
I used this Ajax technique in a project with a frameset, where the frameset and/or several frames need the same JavaScript, in order to avoid having the server send that JavaScript multiple times.
Here is code, tested and working:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">
<html>
<head>
<script id="scriptData">
var scriptData = [
{ name: "foo" , url: "path/to/foo" },
{ name: "bar" , url: "path/to/bar" }
];
</script>
<script id="scriptLoader">
var LOADER = {
loadedCount: 0,
toBeLoadedCount: 0,
load_jQuery: function (){
var jqNode = document.createElement("script");
jqNode.setAttribute("src", "/path/to/jquery");
jqNode.setAttribute("onload", "LOADER.loadScripts();");
jqNode.setAttribute("id", "jquery");
document.head.appendChild(jqNode);
},
loadScripts: function (){
var scriptDataLookup = this.scriptDataLookup = {};
var scriptNodes = this.scriptNodes = {};
var scriptNodesArr = this.scriptNodesArr = [];
for (var j=0; j<scriptData.length; j++){
var theEntry = scriptData[j];
scriptDataLookup[theEntry.name] = theEntry;
}
//console.log(JSON.stringify(scriptDataLookup, null, 4));
for (var i=0; i<scriptData.length; i++){
var entry = scriptData[i];
var name = entry.name;
var theURL = entry.url;
this.toBeLoadedCount++;
var node = document.createElement("script");
node.setAttribute("id", name);
scriptNodes[name] = node;
scriptNodesArr.push(node);
jQuery.ajax({
method : "GET",
url : theURL,
dataType : "text"
}).done(this.makeHandler(name, node)).fail(this.makeFailHandler(name, node));
}
},
makeFailHandler: function(name, node){
var THIS = this;
return function(xhr, errorName, errorMessage){
console.log(name, "FAIL");
console.log(xhr);
console.log(errorName);
console.log(errorMessage);
debugger;
}
},
makeHandler: function(name, node){
var THIS = this;
return function (fileContents, status, xhr){
THIS.loadedCount++;
//console.log("loaded", name, "content length", fileContents.length, "status", status);
//console.log("loaded:", THIS.loadedCount, "/", THIS.toBeLoadedCount);
THIS.scriptDataLookup[name].fileContents = fileContents;
if (THIS.loadedCount >= THIS.toBeLoadedCount){
THIS.allScriptsLoaded();
}
}
},
allScriptsLoaded: function(){
for (var i=0; i<this.scriptNodesArr.length; i++){
var scriptNode = this.scriptNodesArr[i];
var name = scriptNode.id;
var data = this.scriptDataLookup[name];
var fileContents = data.fileContents;
var textNode = document.createTextNode(fileContents);
scriptNode.appendChild(textNode);
document.head.appendChild(scriptNode); // execution is here
//console.log(scriptNode);
}
// call code to make the frames here
}
};
</script>
</head>
<frameset rows="200pixels,*" onload="LOADER.load_jQuery();">
<frame src="about:blank"></frame>
<frame src="about:blank"></frame>
</frameset>
</html>
related question
.text did get you contents of the tag, it's just that you have nothing between your open tag and your end tag. You can get the src attribute of the element using .src, and then if you want to get the javascript file you would follow the link and make an ajax request for it.
In a comment to my previous answer:
I want to store the content of the script so that I can cache it and use it directly some time later without having to fetch it from the external web server (not on the same server as the page)
In that case you're better off using a server side script to fetch and cache the script file. Depending on your server setup you could just wget the file (periodically via cron if you expect it to change) or do something similar with a small script inthe language of your choice.
if you want the contents of the src attribute, you would have to do an ajax request and look at the responsetext. If you where to have the js between and you could access it through innerHTML.
This might be of interest: http://ejohn.org/blog/degrading-script-tags/
I had a same issue, so i solve it this way:
The js file contains something like
window.someVarForReturn = `content for return`
On html
<script src="file.js"></script>
<script>console.log(someVarForReturn)</script>
In my case the content was html template. So i did something like this:
On js file
window.someVarForReturn = `<did>My template</div>`
On html
<script src="file.js"></script>
<script>
new DOMParser().parseFromString(someVarForReturn, 'text/html').body.children[0]
</script>
You cannot directly get what browser loaded as the content of your specific script tag (security hazard);
But
you can request the same resource (src) again ( which will succeed immediately due to cache ) and read it's text:
const scriptSrc = document.querySelector('script#yours').src;
// re-request the same location
const scriptContent = await fetch(scriptSrc).then((res) => res.text());
If you're looking to access the attributes of the <script> tag rather than the contents of script.js, then XPath may well be what you're after.
It will allow you to get each of the script attributes.
If it's the example.js file contents you're after, then you can fire off an AJAX request to fetch it.
It's funny but we can't, we have to fetch them again over the internet.
Likely the browser will read his cache, but a ping is still sent to verify the content-length.
[...document.scripts].forEach((script) => {
fetch(script.src)
.then((response) => response.text() )
.then((source) => console.log(source) )
})
Using 2008-style DOM-binding it would rather be:
document.getElementById('myscript').getAttribute("src");
document.getElementById('myscript').getAttribute("type");
You want to use the innerHTML property to get the contents of the script tag:
document.getElementById("myscript").innerHTML
But as #olle said in another answer you probably want to have a read of:
http://ejohn.org/blog/degrading-script-tags/
If a src attribute is provided, user agents are required to ignore the content of the element, if you need to access it from the external script, then you are probably doing something wrong.
Update: I see you've added a comment to the effect that you want to cache the script and use it later. To what end? Assuming your HTTP is cache friendly, then your caching needs are likely taken care of by the browser already.
I'd suggest the answer to this question is using the "innerHTML" property of the DOM element. Certainly, if the script has loaded, you do not need to make an Ajax call to get it.
So Sugendran should be correct (not sure why he was voted down without explanation).
var scriptContent = document.getElementById("myscript").innerHTML;
The innerHTML property of the script element should give you the scripts content as a string provided the script element is:
an inline script, or
that the script has loaded (if using the src attribute)
olle also gives the answer, but I think it got 'muddled' by his suggesting it needs to be loaded through ajax first, and i think he meant "inline" instead of between.
if you where to have the js between and you could access it through innerHTML.
Regarding the usefulness of this technique:
I've looked to use this technique for client side error logging (of javascript exceptions) after getting "undefined variables" which aren't contained within my own scripts (such as badly injected scripts from toolbars or extensions) - so I don't think it's such a way out idea.
Not sure why you would need to do this?
Another way round would be to hold the script in a hidden element somewhere and use Eval to run it. You could then query the objects innerHtml property.
Suppose I'm embedding a javascript in HTML page:
<script type="text/javascript" src="www.mydomain.com/script.js?var1=abc&var2=def"></script>
Is there a way I can get the src url inside the script and extract the params?
Given that you are using a regular script element in the HTML source, you can just get the last script element in the document. Since script elements are (in the absence of attributes that you aren't using in your example) blocking, no more will be added to the document until this one has been executed.
var scripts = document.getElementsByTagName('script');
var last_script = scripts[scripts.length - 1];
var url = script.src;
This won't work if you dynamically add a script element before the last script using DOM.
this little hack uses error handling to find the location of external scripts from within:
(function(){ // script filename setter, leaves window.__filename set with active script URL.
if(self.attachEvent){
function fn(e,u){self.__filename=u;}
attachEvent("onerror",fn);
setTimeout(function(){detachEvent("onerror", fn)},20);
eval("gehjkrgh3489c()");
}else{
Object.defineProperty( window, "__filename", { configurable: true, get:function __filename(){
try{document.s0m3741ng()}catch(y){
return "http://" +
String(y.fileName || y.file || y.stack || y + '')
.split(/:\d+:\d+/)[0].split("http://")[1];
}
}})//end __filename
}//end if old IE?
}());
it sets a global "__filename" property when run, so atop an external script, the __filename is in effect for the execution of the whole script.
i strongly prefer to sniff url parts from scr attributes, but this works in most browsers and without knowing the URL ahead of time.
I don't think there is a property already inside the script that points to this url.
From the script, you can read the DOM. So you can lookup the script tag and inspect its src attribute, but if you got multiple scripts (or the DOM was modified), you cannot really know for sure which one it is.
I assume it is for checking input. So to solve this, you can eiter:
Render the script through a server side script (PHP), and let it output variables. Disadvantage: eats more server resources and makes caching a bitch.
Just get parameter from all the scripts loading from your domain. Maybe it doesn't matter much, or you have only one script anyway. Disadvantage: In this case this is possible, but not very reliable and resistant to changes.
My preferred: Add the variables to the script tag (actually, to another script tag) to make them available directly in Javascript, rather than parsing the script url.
Like this:
<script type="text/javascript">
var1 = 'abc';
var2 = 'def';
</script>
<script type="text/javascript" src="www.mydomain.com/script.js"></script>
Here are two other solutions that will work no matter how the script is loaded (even if they are loaded dynamically or with async or defer attributes):
Put an id on the script tag.
<script id="myscript" type="text/javascript" src="www.mydomain.com/script.js?var1=abc&var2=def"></script>
Then, you can find it with the id:
$("#myscript").attr("src")
Or second, if you know the filename, you can search for any script tag that contains that filename:
function findScriptTagByFilename(fname) {
$("script").each(function() {
if (this.src.indexOf(fname) !== -1) {
return this.src;
}
});
}
var url = findScriptTagByFilename("/script.js");
My HTML block as follows,
<html>
<title>Example</title>
<head>
</head>
<body>
<h2>Profile Photo</h2>
<div id="photo-container">Photo will load here</div>
<script type='text/javascript' src='http://example.com/js/coverphoto.js?name=johndoe'></script>
</body>
</html>
and I have saved this file as test.html. In JavaScript source the name will be dynamic.
I want to collect the name in coverphoto.js. Tried in coverphoto.js as,
window.location.href.slice(window.location.href.indexOf('?') + 1).split('&')
but it is getting the html file name (test.html) only. How can I retrieve the name key from http://example.com/js/coverphoto.js?name=johndoe in coverphoto.js?
To get the URL of the current JavaScript file you can use the fact that it will be the last <script> element currently on the page.
var scripts = document.getElementsByTagName('script');
var script = scripts[scripts.length - 1];
var scriptURL = script.src;
Please note that this code will only work if it executes directly within the JS file, i.e. not inside a document-ready callback or anything else that's called asynchronously. Then you can use any "get querystring parameter" JS (but make sure to replace any location.search references in there) to extract the argument.
I'd suggest you to put the value in a data-name argument though - that way you can simply use e.g. script.getAttribute('data-name') to get the value.
You can use the stack trace technique.
This technique will detect the source of the JS file the script is running from and it doesn't depend on the way you have injected the script. It can be dynamically injected (ajax) or in whatever method you can think of.
Just use the following code in your JS file:
const STACK_TRACE_SPLIT_PATTERN = /(?:Error)?\n(?:\s*at\s+)?/;
const STACK_TRACE_ROW_PATTERN1 = /^.+?\s\((.+?):\d+:\d+\)$/;
const STACK_TRACE_ROW_PATTERN2 = /^(?:.*?#)?(.*?):\d+(?::\d+)?$/;
const getFileParams = () => {
const stack = new Error().stack;
const row = stack.split(STACK_TRACE_SPLIT_PATTERN, 2)[1];
const [, url] = row.match(STACK_TRACE_ROW_PATTERN1) || row.match(STACK_TRACE_ROW_PATTERN2) || [];
if (!url) {
console.warn("Something went wrong. This probably means that the browser you are using is non-modern. You should debug it!");
return;
}
try {
const urlObj = new URL(url);
return urlObj.searchParams;
} catch (e) {
console.warn(`The URL '${url}' is not valid.`);
}
};
const params = getFileParams();
if ( params ) {
console.log(params.get('name'));
}
Note:
The params.searchParams will not work for IE browser, instead you can use params.search. But, for the sake of you nerves don't. Whoever is still using IE in 2020, just let him suffer.
I want to include a script tag, while providing a parameter to it. This is what I came up with so far
Provide a parameter to script URL (cons: generate multiple JS files)
<script src="http://example.com/something.js?P=123" type="text/javascript"></script>
Hide parameter in script tag (cons: same as #1)
<script src="http://example.com/scripts/123/something.js" type="text/javascript"></script>
Google Analytics way (cons: ugly, complicated, global variables)
<script type="text/javascript" charset="utf-8">
var _something = _something || 123;
(function() {
var s = document.createElement('script');
s.type = 'text/javascript';
s.src = 'http://example.com/something.js';
var ss = document.getElementsByTagName('script')[0];
ss.parentNode.insertBefore(s, ss);
})();
</script>
The best thing is to define things (functions &c) in the external script but execute nothing. Then have an inline script that calls functions/methods defined in the external script.
<script src="http://example.com/something.js" type="text/javascript"></script>
<script type="text/javascript">
something(123);
</script>
If the way the script is executed, depends on how it's called, you can add params like your option 1.
Other ways are:
<script params='{"abc": 123}' src="script.js"></script><!-- params is a non standard, non official attr that the script will read -->
or
<script>var _abc = 123;</script>
<script src="script.js"></script>
or even
<script src="script.js#abc=123"></script>
I have to agree with #outis though: load the same thing for everybody, always, and execute it like you/the client want(s) afterwards.
I do this for a cross-sub-domain XHR handler that I have. I call it as:
<script type="text/javascript" src="xd.js#subdomain"></script>
and then in the script, parse it as such (using jQuery):
$('script').each(function(){
if((src = this.src).indexOf('xd.js') < 0){ return; }
xds = src.substr(src.indexOf('#') + 1).split(',');
// do stuff with xds
});
Your first example does not need to generate multiple files. It can be used by JavaScript alone, by detecting window.location.href and parsing it (you might find the likes of http://phpjs.org/functions/parse_url:485 and http://phpjs.org/functions/parse_str:484 helpful in doing this: var queryString = parse_str(parse_url(window.location.href).query); ).
However, if you use something like #P=123 instead of ?P=123, you won't cause another download of the file by your users, so I'd recommend that instead (in which case change "query" in the above code sample to "fragment").
Another possibility is using the HTML5-reserved data-* attributes, and detecting their values within your script:
<script src="http://example.com/something.js" data-myOwnAttribute="someValue" data-anotherCustomAttribute="anotherValue"></script>
The script would then detect along these lines:
(function () {
function getScriptParam (attr) {
var scripts = document.getElementsByTagName('script'),
currentScript = scripts[scripts.length-1];
return currentScript.getAttribute('data-' + attr); // in future, could just use the HTML5 standard dataset attribute instead: currentScript.dataset[attr]
}
var myOwnAttribute = getScriptParam('myOwnAttribute');
// ... do stuff here ...
}());
The real advantage of Google's ugly API is that it allows developers to drop in that code in the <head> of the document (considered proper form), while still acting asynchronously in a cross-browser way. I think they could indeed avoid the global had they combined their dynamic script-tag technique with either of the above approaches.
I was wondering if eval (or some variant of jQuery's getScript) can be used to position external javascript in places other than the end of the DOM or at the head. I've tried:
var head = document.getElementById("fig");
instead of
var head = document.getElementsById("head")[0];
with
var script = document.createElement("script");
script.text = $(".code").val();
head.appendChild(script);
But I can't seem to get it to work regardless. (The code does work, but Firebug shows the code being replaced both at #fig and at the end of the page, right before the </body> tag.
Basically the JavaScript toolkit I'm using renders things based on where the script tag is located, and I'm trying to dynamically modify the javascript based on user input (hence I can't really refer to a new external JS file - I'd rather run an eval, which isn't ideal).
I guess the worst case scenario would be to save the user input into a "new" file using PHP or something and use getScript pointing to that new PHP file, but it seems exceedingly hacky.
Thank you once again!
Does the "JavaScript toolkit" you refer to use document.write or document.writeln to insert output into the page? If so, you could override that function to append the script output into the correct location:
document.write = function(s) {
$('#fig').append(s);
};
document.writeln = function(s) {
$('#fig').append(s + '\n');
};
and then load and execute the script using $.getScript.
Edit: A more robust way of doing it, depending on how the code is added:
var output = '';
document.write = function(s) {
output += s;
};
document.writeln = function(s) {
output += s + '\n';
};
$.getScript('URL of script here', function() {
$('#fig').append(output);
});