I have this function:
function parseScript(_source) {
var source = _source;
var scripts = new Array();
while(source.indexOf("<script") > -1 || source.indexOf("</script") > -1) {
var s = source.indexOf("<script");
var s_e = source.indexOf(">", s);
var e = source.indexOf("</script", s);
var e_e = source.indexOf(">", e);
scripts.push(source.substring(s_e+1, e));
source = source.substring(0, s) + source.substring(e_e+1);
}
for(var i=0; i<scripts.length; i++) {
try {
eval(scripts[i]);
}
catch(ex) {
}
}
return source;
}
It parses and execute Javascript wonderfully except the when in <script type='text/javascript' src='scripts/gen_validatorv31.js'></script> the src file never gets executed.
The parser can only evaluate inline scripts in the file you have opened. To evaluate external scripts you would have to find their sources, probably using something like:
var scripts = source.match(/<script[^>]*src=[^>]*>/g);
if (scripts) {
for (var i = 0; i < scripts.length; i++) {
src = scripts[i].match(/src=("([^"]*)"|'([^']*)')/);
src = src[2] || src[3];
if (src) {
addScriptTag(src);
}
}
}
else console.log('no external scripts found');
where addScriptTag is described in this answer. addScriptTag adds the script to the head, if possible. It will need to be adapted if you need to add script to the body.
However... why do this? It is slow and messy to parse an entire HTML/Javascript page to get the scripts; for instance you might end up loading the same scripts twice or loading two scripts that don't work well together. Also the scripts may not work if inserted at a different point in the head or body. With AJAX you should only be loading the particular elements you need. Usually this means loading bits of data or HTML to be added to the page. If you have long scripts that are not needed at the beginning but might be needed later then it might be justified to dynamically add new scripts to the page. But in many cases probably better to load all needed scripts at the beginning. And if you really need to switch pages completely then isn't it better to use the old-fashioned method of linking to another page?
Related
Is there a simple and reliable way to determine the URL of the currently-executing JavaScript file (inside a web page)?
My only thought on this is to scan the DOM for all the script src attributes to find how the current file was referenced and then figure out the absolute URL by applying it to document.location. Anyone have other ideas, is there some super-easy method I completely overlooked?
UPDATE: Script elements accessed via the DOM already have a src property which contains the full URL. I don't know how ubiquitous/standard that is, but alternatively you can use getAttribute("src") which will return whatever raw attribute value is in the [X]HTML.
Put this in the js file that needs to know it's own url.
Fully Qualified (eg http://www.example.com/js/main.js):
var scriptSource = (function(scripts) {
var scripts = document.getElementsByTagName('script'),
script = scripts[scripts.length - 1];
if (script.getAttribute.length !== undefined) {
return script.src
}
return script.getAttribute('src', -1)
}());
Or
As it appears in source (eg /js/main.js):
var scriptSource = (function() {
var scripts = document.getElementsByTagName('script'),
script = scripts[scripts.length - 1];
if (script.getAttribute.length !== undefined) {
return script.getAttribute('src')
}
return script.getAttribute('src', 2)
}());
See http://www.glennjones.net/Post/809/getAttributehrefbug.htm for explanation of the getAttribute parameter being used (it's an IE bug).
For recent browsers, you can use document.currentScript to get this information.
var mySource = document.currentScript.src;
The upside is that it's more reliable for scripts that are loaded asynchronously. The downside is that it's not, as best I know, universally supported. It should work on Chrome >= 29, FireFox >= 4, Opera >= 16. Like many useful things, it doesn't seem to work in IE.
When I need to get a script path, I check to see if document.currentScript is defined, and, if not, use the method described in the accepted answer.
if (document.currentScript) {
mySource = document.currentScript.src;
} else {
// code omitted for brevity
}
https://developer.mozilla.org/en-US/docs/Web/API/document.currentScript
As it appears in source (e.g. /js/main.js), this is cross-browser:
var scriptSource = (function()
{
var scripts = document.getElementsByTagName('script'),
script = scripts[scripts.length - 1];
//No need to perform the same test we do for the Fully Qualified
return script.getAttribute('src', 2); //this works in all browser even in FF/Chrome/Safari
}());
Fully Qualified (e.g. http://www.example.com/js/main.js):
After some tests it seems hard to get the fully qualified one in a cross-browser way. The solution suggested by Crescent Fresh does not work in IE8 to get the fully qualified, even if it works in IE7
This method work with defer, async and lazy loading
Since you know the filename of your script, and if it will be unique
/* see
* http://stackoverflow.com/questions/984510/what-is-my-script-src-url/984656#984656
* http://www.glennjones.net/Post/809/getAttributehrefbug.htm
*
* iterate all script to find script with right filename
* this work with async and defer (but your script MUST have a unique filemane)
* mozilla support document.currentScript and we use it, if is set
*
* this will not work with local script loaded by jQuery.getScript(),
* since there is no script tag added into the dom. the script is only evaluated in global space.
* http://api.jquery.com/jQuery.getScript/
*
* to fix this odd, you can add a reference in meta ( meta[name=srcipt][content=url] )
* when you load the script
*/
var scriptFilename = 'jquery.plugins.template.js'; // don't forget to set the filename
var scriptUrl = (function() {
if (document.currentScript) { // support defer & async (mozilla only)
return document.currentScript.src;
} else {
var ls,s;
var getSrc = function (ls, attr) {
var i, l = ls.length, nf, s;
for (i = 0; i < l; i++) {
s = null;
if (ls[i].getAttribute.length !== undefined) {
s = ls[i].getAttribute(attr, 2);
}
if (!s) continue; // tag with no src
nf = s;
nf = nf.split('?')[0].split('/').pop(); // get script filename
if (nf === scriptFilename) {
return s;
}
}
};
ls = document.getElementsByTagName('script');
s = getSrc(ls, 'src');
if ( !s ) { // search reference of script loaded by jQuery.getScript() in meta[name=srcipt][content=url]
ls = document.getElementsByTagName('meta');
s = getSrc(ls, 'content');
}
if ( s ) return s;
}
return '';
})();
var scriptPath = scriptUrl.substring(0, scriptUrl.lastIndexOf('/'))+"/";
a jquery plugin template with it:
https://github.com/mkdgs/mkdgs-snippet/blob/master/javascript/jquery.plugins.template.js
note: this will not work with local script loaded by jQuery.getScript(), since there is no script tag added into the dom. the script is only evaluated in global space.
http://api.jquery.com/jQuery.getScript/
to fix it you can do something like:
function loadScript(url,callback) {
if ( $('[src="'+url+'"]').length ) return true; // is already loaded
// make a reference of the loaded script
if ( $('meta[content="'+url+'"]', $("head")).length ) return true; // is already loaded
var meta = document.createElement('meta');
meta.content = url;
meta.name = 'script';
$("head").append(meta);
return $.ajax({
cache: true,
url: u,
dataType: 'script',
async: false,
success : function (script) {
try {
if ( typeof callback == 'function' ) callback();
} catch (error) {
//console.log(error);
}
}
});
}
If this is a strictly client solution, yours sounds pretty good.
If you are writing code on the server, you could probably just populate a div/hidden field/(insert your fave HTML element here) with the fully resolved URL to the script, and pick that up with your javascript on the clientside.
You may want to have a look at https://addons.mozilla.org/en-US/firefox/addon/10345 if you're interested in learning which functions (and thus which file) are executing on a page you don't control.
If you're interested in figuring out which of your scripts is executing, then there are a number of ways. With Firebug you could console.log() the information. Even just putting alert statements in your code (while annoying) can help debug in a low-tech way. You could also raise errors and catch them, then process using properties of the error (see: https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Error)
However, why would this be important? If the script is causing errors already then it's easy enough to determine where the error is occurring. If it's not about errors at all, then what's the advantage in knowing which file it comes from?
In my HTML file I have linked to the JS with:
src="myscript.js?config=true"
Can my JS directly read the value of this var like this?
alert (config);
This does not work, and the FireFox Error Console says "config is not defined". How do I read the vars passed via the src attribute in the JS file? Is it this simple?
<script>
var config=true;
</script>
<script src="myscript.js"></script>
You can't pass variables to JS the way you tried. SCRIPT tag does not create a Window object (which has a query string), and it is not server side code.
Yes, you can, but you need to know the exact script file name in the script :
var libFileName = 'myscript.js',
scripts = document.head.getElementsByTagName("script"),
i, j, src, parts, basePath, options = {};
for (i = 0; i < scripts.length; i++) {
src = scripts[i].src;
if (src.indexOf(libFileName) != -1) {
parts = src.split('?');
basePath = parts[0].replace(libFileName, '');
if (parts[1]) {
var opt = parts[1].split('&');
for (j = opt.length-1; j >= 0; --j) {
var pair = opt[j].split('=');
options[pair[0]] = pair[1];
}
}
break;
}
}
You have now an 'options' variable which has the arguments passed. I didn't test it, I changed it a little from http://code.google.com/p/canvas-text/source/browse/trunk/canvas.text.js where it works.
You might have seen this done, but really the JS file is being preprocessed server side using PHP or some other language first. The server side code will print/echo the javascript with the variables set. I've seen a scripted ad service do this before, and it made me look into seeing if it can be done with plain ol' js, but it can't.
You need to use Javascript to find the src attribute of the script and parse the variables after the '?'. Using the Prototype.js framework, it looks something like this:
var js = /myscript\.js(\?.*)?$/; // regex to match .js
var jsfile = $$('head script[src]').findAll(function(s) {
return s.src.match(js);
}).each(function(s) {
var path = s.src.replace(js, ''),
includes = s.src.match(/\?.*([a-z,]*)/);
config = (includes ? includes[1].split('=');
alert(config[1]); // should alert "true" ??
});
My Javascript/RegEx skills are rusty, but that's the general idea. Ripped straight from the scriptaculous.js file!
Your script can however locate its own script node and examine the src attribute and extract whatever information you like.
var scripts = document.getElementsByTagName ('script');
for (var s, i = scripts.length; i && (s = scripts[--i]);) {
if ((s = s.getAttribute ('src')) && (s = s.match (/^(.*)myscript.js(\?\s*(.+))?\s*/))) {
alert ("Parameter string : '" + s[3] + "'");
break;
}
}
Whether or not this SHOULD be done, is a fair question, but if you want to do it, http://feather.elektrum.org/book/src.html really shows how. Assuming your browser blocks when rendering script tags (currently true, but may not be future proof), the script in question is always the last script on the page up to that point.
Then using some framework and plugin like jQuery and http://plugins.jquery.com/project/parseQuery this becomes pretty trivial. Surprised there's not a plugin for it yet.
Somewhat related is John Resig's degrading script tags, but that runs code AFTER the external script, not as part of the initialization: http://ejohn.org/blog/degrading-script-tags/
Credits: Passing parameters to JavaScript files , Passing parameters to JavaScript files
Using global variables is not a so clean or safe solution, instead you can use the data-X attributes, it is cleaner and safer:
<script type="text/javascript" data-parameter_1="value_1" ... src="/js/myfile.js"></script>
From myfile.js you can access the data parameters, for instance with jQuery:
var parameter1 = $('script[src*="myfile.js"]').data('parameter_1');
Obviously "myfile.is" and "parameter_1" have to match in the 2 sources ;)
You can do that with a single line code:
new URL($('script').filter((a, b, c) => b.src.includes('myScript.js'))[0].src).searchParams.get("config")
It's simpler if you pass arguments without names, just like function calls.
In HTML:
<script src="abc.js" data-args="a,b"></script>
Then, in JavaScript:
const args=document.currentScript.dataset.args.split(',');
Now args contains the array ['a','b'].
Is any way to get the file info (file name, path) of a javascript file after the document is ready, without knowing how many scripts or the order how they are loaded?
i.e.
<html>
<head>
<script src="http://www.example.ex/js/js1.js" type="text/javascript">
<script src="http://www.example.ex/javascript/js2.js" type="text/javascript">
<script src="http://www.example.ex/scripts/js3.js" type="text/javascript">
<...>
Where the file js2.js has something like this:
$(document).ready(function() {
// get the filename "js2.js"
}
I could do
var scripts = document.getElementsByTagName("script"),
scriptLocation = scripts[1].src;
but the index "[1]" must be dynamic because i don't know the order of the loaded scripts.
Perhaps a full answer would be more helpful than my comments. If you put this code into js2.js, you'll get what you want. The key is to capture scriptLocation in a piece of code which runs synchronously with the loading the file, which means not in a callback.
var scripts = document.getElementsByTagName("script"),
scriptLocation = scripts[scripts.length - 1].src;
$(document).ready(function() {
// logs the full path corresponding to "js2.js"
console.log(scriptLocation);
}
To get Line Number and filename from Error.stack eg:
console.log((new Error).stack.split("\n"));
See Error.stack
For browser compatibility, see previous SO question
Not entirely sure what you're after, but if you want to get reference to all the scripts loaded in the DOM, you would simply do:
var scripts = document.getElementsByTagName("script");
for(var i = 0; i < scripts.length; i++){
var scriptLocation = scripts[i].src;
}
var scripts = document.getElementsByTagName("script");
for (var i = 0; i < scripts.length; i++) {
if (scripts[i].src.match(/<desired filename>/) {
scriptLocation = scripts[i];
}
}
If I correctly understood your question, that should do the trick.
var scriptName = [].slice.call(document.getElementsByTagName('script')).pop().getAttribute('src');
document.getElementsByTagName('script') return HTMLCollection of page scripts. Last element is current script. HTMLCollection doesn't have method to get last, but after convert colliection by [].slice.call to Array we can call pop to do it. Finally getAttribute return desired filename.
Same code can used to passing arguments to js-script
<script src="file.js" args="arg1;arg2">
...
var args = [].slice.call(document.getElementsByTagName('script')).pop().getAttribute('args').split(';');
I have heard and read a few articles about deferring JavaScript loading and am very interested. It seems to be very promising for web apps that may be useful on Mobile platforms where the amount of JavaScript that can be loaded and executed is limited.
Unfortunately, most of the articles talk about this at an extremely high level. How would one approach this?
EDIT
Normally, all JavaScript is loaded on page load, however, there may be functions that are not necessary until a certain action occurs, at which time, the JavaScript should be loaded. This helps ease the burden of the browser on page load.
Specifically, I have a page that very heavily uses JavaScript. When I load the page on my phone, it won't load properly. As I debugged the page, I eliminated some of the JS functions. Once enough was eliminated, the page suddenly worked.
I want to be able to load the JS as needed. And possibly even eliminate the functions simply used for start up.
The basics are simple - breaking up your JavaScript code into logically separate components and loading only what you need. Depending on what you are building you can use:
Loaders:
Modernizr.load (or yepnope.js by itself)
LABjs
Many, many, many other deferred loading libraries.
Dependency managers (which are also loaders):
Require.js
dojo.require
JavaScript MVC's steal.js
Several other dependency management libraries.
These tools make use of a wide variety of techniques to defer the loading of scripts, the execution of scripts, manage dependencies, etc. What you need depends on what you are building.
You may also want to read through this discussion to learn something more about the pros and cons of using such techniques.
Response to edit:
There isn't really a good way to unload JavaScript that you have already loaded - the closest approximation you can get is to keep all of your loading code namespaced inside your application's namespace and then "clean up" by setting that namespace, and all references to it to null.
I have used a simple script published on line with some modification done by me.
Assume that your COMPRESSED Javascript file is in the cache directory in your webserver and you want to defer the loading of this compressed js file.
Your compressed js file:
80aaad2a95e397a9f6f64ac79c4b452f.js
This is the code html code:
<script type="text/javascript" src="/resources/js/defer.js?cache=80aaad2a95e397a9f6f64ac79c4b452f.js"></script>
This is the defer.js file content:
(function() {
/*
* http://gtmetrix.com/
* In order to load a page, the browser must parse the contents of all <script> tags,
* which adds additional time to the page load. By minimizing the amount of JavaScript needed to render the page,
* and deferring parsing of unneeded JavaScript until it needs to be executed,
* you can reduce the initial load time of your page.
*/
// http://feather.elektrum.org/book/src.html
// Get the script tag from the html
var scripts = document.getElementsByTagName('script');
var myScript = scripts[ scripts.length - 1 ];
// Get the querystring
var queryString = myScript.src.replace(/^[^\?]+\??/,'');
// Parse the parameters
var params = parseQuery( queryString );
var s = document.createElement('script');
s.type = 'text/javascript';
s.async = true;
s.src = '/cache/' + params.cache; // Add the name of the js file
var x = document.getElementsByTagName('script')[0];
x.parentNode.insertBefore(s, x);
function parseQuery ( query ) {
var Params = new Object ();
if ( ! query ) return Params; // return empty object
var Pairs = query.split(/[;&]/);
for ( var i = 0; i < Pairs.length; i++ ) {
var KeyVal = Pairs[i].split('=');
if ( ! KeyVal || KeyVal.length != 2 ) continue;
var key = unescape( KeyVal[0] );
var val = unescape( KeyVal[1] );
val = val.replace(/\+/g, ' ');
Params[key] = val;
}
return Params;
}
})();
I would like to say thanks to http://feather.elektrum.org/book/src.html that helped me to understand how to get the parameters from the script tag.
bye
Deferring loading til when?
The reason typically why JS is loaded last, is so that the entire DOM has been loaded first.
An easy way is to just use
<body onload="doSomething();">
So you could easily have doSomething() function to load all your JS.
You can also add a function to window.onload, like
window.onload = function(){ };
Also, if you are using JS librarys, such as jQuery and Dojo, they each have their own onReady and addOnLoad methods in order to run some JS only after the document has already loaded.
Here's a useful article on the script element's defer and async attributes. Specifying these attributes will get the browser to defer loading in different ways. You can also load in an external script using JavaScript after page load.
It should also be noted that the position of your script elements within your HTML document will determine load and execution order if neither defer nor async have been specified.
Is it possible from a Javascript code to find out "where" it came from?
I needed this to provide scripts that could run folder-agnostic, e.g.:
http://web1/service-a/script.js
http://web2/some-folder/service-b/script.js
And they're linked in:
http://web1/page1.html
http://web2/page2.html
The file script.js is identical in both locations but I would like them to be able to find out where it originates from so it can call the right web service methods (script.js from service-a should call service-a.method while script.js that is served from service-b should call service-b.method)
Is there a way to find out where script.js came from without using any server-side code?
Well, it's kind of a hack, you could grab all the <script> tags in the document, look at which one has the file name of the file, and do the logic from there.
document.getElementsByTagName('script'); is pretty much all you need. The rest is basic JS.
Even more interesting than looping through all of the returned elements (although that's probably safest), is that we can simply only look at the last element returned by the call above, as Javascript guarantees that must be the <script> tag we're in at the time of it being parsed (with the exception of deferred scripts). So this code:
var all_scripts = document.getElementsByTagName('script');
var current_script = scripts[all_scripts.length-1];
alert(current_script.src);
Will alert the source of the script tag used to include the current Javascript file.
You can analyze source of the html where script.js is included for tag and retrieve path of the script.js from there. Include next function in script.js and use it to retrieve the path.
function getPath() {
var path = null;
var scriptTags = document.getElementsByTagName("script");
for (var i = 0; i < scriptTags.length; i++) {
var scriptTagSrc = scriptTags.item(i).src;
if (scriptTagSrc && scriptTagSrc.indexOf("script.js") !== -1) {
path = scriptTagSrc;
break;
}
}
return path;
}