Get script path
In CSS, any image path is relative to the CSS file location开发者_高级运维.
f.ex if I put the CSS file in /media/css/mystyles.css
and use something like
.background:url(../images/myimage.jpg);
The browser will look for the image in /media/images/myimage.jpg
which makes sense.
Is it possible to do the same thing in javascript?
F.ex if I include /media/js/myscript.js
and put this code in there:
var img = new Image();
img.src = '../images/myimage.jpg';
Th image is not found, since browser is using the HTML file as a starting point, instead of the script location. I would like to be able to use the script location as a starting point, just like CSS does. Is this possible?
Searching the DOM for your own <script>
tag as above is the usual method, yes.
However, you usually needn't search too hard: when you're in the body of the script — run at include-time — you know very well which <script>
element you are: the last one. The rest of them can't have been parsed yet.
var scripts= document.getElementsByTagName('script');
var path= scripts[scripts.length-1].src.split('?')[0]; // remove any ?query
var mydir= path.split('/').slice(0, -1).join('/')+'/'; // remove last filename part of path
function doSomething() {
img.src= mydir+'../images/myimage.jpeg';
}
This doesn't hold true if your script has been linked with <script defer>
(or, in HTML5, <script async>
). However, this is currently rarely used.
On more recent browsers, you can use the document.currentScript
property to obtain the HTMLScript
element corresponding to that script, then query its src
property.
Can I use indicates support by 70% of the web’s users, at the time of this writing. Apparently Internet Explorer doesn’t support it, but Edge does. MDC lists support as Chrome 29+, Edge, Firefox 4.0+, Gecko 2.0+, Opera 16+ and Safari 8+. The comment by @mjhm already pointed out this feature, but back then it sounded very experimental.
Inspired by bobince answer above, I wrote a jQuery version. Here is the code in one line:
var scriptpath = $("script[src]").last().attr("src").split('?')[0].split('/').slice(0, -1).join('/')+'/';
Edit: Filter the script
tag by the src
attribute, so that we get a src to work on.
As other posters mentioned you need to compute your base url for the script first, you can the script below to find it.
// find the base path of a script
var settings = {};
settings.basePath = null;
if (!settings.basePath) {
(function (name) {
var scripts = document.getElementsByTagName('script');
for (var i = scripts.length - 1; i >= 0; --i) {
var src = scripts[i].src;
var l = src.length;
var length = name.length;
if (src.substr(l - length) == name) {
// set a global propery here
settings.basePath = src.substr(0, l - length);
}
}
})('myfile.js');
}
log(settings.basePath);
For asynchronous scripts, script tag walking won't do. So if: - performance.timing exists (like in new non-Safari browsers) & You have not reached its max, or have pushed-up its max before loading & Your script was the most recent thing loaded:
performance.getEntries().slice(-1)[0].name
To push-up the max, do something like this:
performance.webkitSetResourceTimingBufferSize(10000)
(If base
[Rubens's answer] doesn't work for you. Edit: Apparently he removed it, but I thought it was relevant; see base
on the W3C site.)
It's possible, but it's a pain. :-) You have to search the DOM for the script
tag that imported your script, and then grab its src
value. That's how script.aculo.us
does its module auto-loading; you can refer to the scriptaculous.js file for an example.
I wrote a class to find get the path of scripts that works with delayed loading and async script tags. It's based on inspecting the stack trace and there are a couple of functions to format the results but it's intended to be basic. If nothing else use it as a reference.
function ScriptPath() {
var scriptPath = '';
try {
//Throw an error to generate a stack trace
throw new Error();
}
catch(e) {
//Split the stack trace into each line
var stackLines = e.stack.split('\n');
var callerIndex = 0;
//Now walk though each line until we find a path reference
for(var i in stackLines){
if(!stackLines[i].match(/http[s]?:\/\//)) continue;
//We skipped all the lines with out an http so we now have a script reference
//This one is the class constructor, the next is the getScriptPath() call
//The one after that is the user code requesting the path info (so offset by 2)
callerIndex = Number(i) + 2;
break;
}
//Now parse the string for each section we want to return
pathParts = stackLines[callerIndex].match(/((http[s]?:\/\/.+\/)([^\/]+\.js)):/);
}
this.fullPath = function() {
return pathParts[1];
};
this.path = function() {
return pathParts[2];
};
this.file = function() {
return pathParts[3];
};
this.fileNoExt = function() {
var parts = this.file().split('.');
parts.length = parts.length != 1 ? parts.length - 1 : 1;
return parts.join('.');
};
}
Full source
One line "Pathfinder", when you know your script file name (here include.js
):
// Script file name
var file = 'include.js';
// jQuery version
var path = (src = jQuery('script[src$="' + file + '"]').attr('src')).substring(0, src.lastIndexOf("/") + 1);
// jQuery with error handling
var path = (src = jQuery('script[src$="' + file + '"]').attr('src') ? src : 'Path/Not/Found/').substring(0, src.lastIndexOf("/") + 1);
// Plain JS version
var path = (src = document.querySelector('script[src$="' + file + '"]').src).substring(0, src.lastIndexOf("/") + 1);
// Plain JS with error handling
var path = (src = (script = document.querySelector('script[src$="' + file + '"]')) ? script.src : 'Path/Not/Found/').substring(0, src.lastIndexOf("/") + 1);
Es6 gives now an easy way to do this.
const currentScriptPath=function(level=0){
let line=(new Error()).stack.split('\n')[level+1].split('@').pop();
return line.substr(0,line.lastIndexOf('/')+1);
};
You can flip through <head>
's childNodes, find your <script>
tag, get the url of said script, and compute the url of your image.
One way is to put the path in the script itself:
var scriptPath = <?PHP echo json_encode(dirname($_SERVER['SCRIPT_NAME'])); ?>;
NOTE that all the samples on this question take LAST tag occurrence, and as pointed out not supporting async loading. so if you want to do it perfect manner, you should be matching YOUR script name ADDITIONALLY. in fact scriptaculous does that.
just it's a problem that the "incomplete" code is highlighted and projects like http://areaaperta.com/nicescroll/ use that version of the code
In my case it isn't scripts.length-1
.
Example:
In debug mode js file location: //server/js/app.js
In prod mode js file location: //server/js-build/app.js
The file name is known; the single way to find path by it's name:
getExecutionLocationFolder() {
var fileName = "app.js";
var scriptList = $("script[src]");
var jsFileObject = $.grep(scriptList, function(el) {
var currentElement = el.src.contains(fileName);
return currentElement;
});
var jsFilePath = jsFileObject[0].src;
var jsFileDirectory = jsFilePath.substring(0, jsFilePath.lastIndexOf("/") + 1);
return jsFileDirectory;
};
精彩评论