102

There's a function, which gives me urls like:

./some.css
./extra/some.css
../../lib/slider/slider.css

It's always a relative path.

Let's think we know current path of the page, like http://site.com/stats/2012/, not sure how do I convert these relative paths to real ones?

We should get something like:

./some.css => http://site.com/stats/2012/some.css
./extra/some.css => http://site.com/stats/2012/extra/some.css
../../lib/slider/slider.css => http://site.com/lib/slider/slider.css

No jQuery, only vanilla javascript.

Ryan B
3,39223 silver badges35 bronze badges
asked Feb 8, 2013 at 19:56
1
  • 7
    This is for javascript application, not a regular site. Commented Feb 8, 2013 at 20:01

14 Answers 14

209

The most simple, efficient and correct way to do so it to just use URL api.

new URL("http://www.stackoverflow.com?q=hello").href;
//=> "http://www.stackoverflow.com/?q=hello"
new URL("mypath","http://www.stackoverflow.com").href;
//=> "http://www.stackoverflow.com/mypath"
new URL("../mypath","http://www.stackoverflow.com/search").href
//=> "http://www.stackoverflow.com/mypath"
new URL("../mypath", document.baseURI).href
//=> "https://stackoverflow.com/questions/mypath"

Performance wise, this solution is on par with using string manipulation and twice as fast as creating a tag.

Cid
15.3k4 gold badges33 silver badges49 bronze badges
answered Jun 14, 2017 at 14:40
Sign up to request clarification or add additional context in comments.

4 Comments

Love this solution, however didn't work with React Native due to lack or URL support. Instead I used https://www.npmjs.com/package/url, which worked well.
@hex You're right that using window.location.href has this drawback. I've edited the answer to make it use document.baseURI instead.
This will not be a generalised solution for development environment.
How do I run this solution in ubuntu?
73

Javascript will do it for you. There's no need to create a function.

var link = document.createElement("a");
link.href = "../../lib/slider/slider.css";
alert(link.protocol+"//"+link.host+link.pathname+link.search+link.hash);
// Output will be "http://www.yoursite.com/lib/slider/slider.css"

But if you need it as a function:

var absolutePath = function(href) {
 var link = document.createElement("a");
 link.href = href;
 return (link.protocol+"//"+link.host+link.pathname+link.search+link.hash);
}

Update: Simpler version if you need the full absolute path:

var absolutePath = function(href) {
 var link = document.createElement("a");
 link.href = href;
 return link.href;
}
answered Feb 8, 2013 at 21:30

4 Comments

This doesn't work in IE, not even IE11. See this for more details: stackoverflow.com/questions/470832/…
Works great (in IE too)...I found that simply accessing link.href after being set returned the resolved URL (i.e., no need to manually rebuild the href).
@ChrisBaxter Same here. I'm a bit annoyed that this popular answer refers to protocol, host, etc... whereas href looks simpler and safer. Is there a reason for this? I would like to know...
It's actually a DOM solution
52

This should do it:

function absolute(base, relative) {
 var stack = base.split("/"),
 parts = relative.split("/");
 stack.pop(); // remove current file name (or empty string)
 // (omit if "base" is the current folder without trailing slash)
 for (var i=0; i<parts.length; i++) {
 if (parts[i] == ".")
 continue;
 if (parts[i] == "..")
 stack.pop();
 else
 stack.push(parts[i]);
 }
 return stack.join("/");
}
answered Feb 8, 2013 at 20:04

8 Comments

some sites have internal resources with ~, ~/media/style.css, ran into this the other day
@Daniel_L: ~ typically means something else than base. And it's not exactly a relative path :-) If you need help with paths containing a tilde, please ask a new question
For anyone encountering this later, using document.location.href for base worked for me.
This solution has a problem when the relative url starts with a / for example /some.css. A correct implementation would in such a case remove all of the items in the stack after the domain name.
@Vineet Correct, however a path starting with / is not relative. My function only considers paths, not URIs.
|
7

This from MDN is unbreakable!

/*\
|*|
|*| :: translate relative paths to absolute paths ::
|*|
|*| https://developer.mozilla.org/en-US/docs/Web/API/document.cookie
|*|
|*| The following code is released under the GNU Public License, version 3 or later.
|*| http://www.gnu.org/licenses/gpl-3.0-standalone.html
|*|
\*/
function relPathToAbs (sRelPath) {
 var nUpLn, sDir = "", sPath = location.pathname.replace(/[^\/]*$/, sRelPath.replace(/(\/|^)(?:\.?\/+)+/g, "1ドル"));
 for (var nEnd, nStart = 0; nEnd = sPath.indexOf("/../", nStart), nEnd > -1; nStart = nEnd + nUpLn) {
 nUpLn = /^\/(?:\.\.\/)*/.exec(sPath.slice(nEnd))[0].length;
 sDir = (sDir + sPath.substring(nStart, nEnd)).replace(new RegExp("(?:\\\/+[^\\\/]*){0," + ((nUpLn - 1) / 3) + "}$"), "/");
 }
 return sDir + sPath.substr(nStart);
}

Sample usage:

/* Let us be in /en-US/docs/Web/API/document.cookie */
alert(location.pathname);
// displays: /en-US/docs/Web/API/document.cookie
alert(relPathToAbs("./"));
// displays: /en-US/docs/Web/API/
alert(relPathToAbs("../Guide/API/DOM/Storage"));
// displays: /en-US/docs/Web/Guide/API/DOM/Storage
alert(relPathToAbs("../../Firefox"));
// displays: /en-US/docs/Firefox
alert(relPathToAbs("../Guide/././API/../../../Firefox"));
// displays: /en-US/docs/Firefox
answered Sep 14, 2014 at 13:41

10 Comments

It doesn't handle relative paths with a leading /. Example: relPathToAbs("/?foo=bar") should return "/?foo=bar" instead it returns /questions/14780350/?foo=bar which means it's not properly detecting a leading / when sends the path back to the root.
@Nucleon The path is the part of the URL included between the protocol segment (http(s)://) and the search / hash segments (?/#). It is not concern of that function to separate the path part from the search / hash parts, also because Regular Expressions can make it very easy: relPathToAbs("./?foo=bar#someHash".replace(/^[^\?#]*/, "$&"))
@Nucleon Sorry, I had misunderstood your comment. A path starting with / is already an absolute path! Therefore it has no sense to put it as argument of that function! The string "/?foo=bar" is already an absolute path.
Exactly, but run your function on this page's URL relPathToAbs("/?foo=bar"). It doesn't return /?foo=bar (because it's already absolute) it returns /questions/14780350/?foo=bar.
@Nucleon The function expects you're giving a relative path. The string /?foo=bar is a wrong relative path (indeed it is an absolute path). A valid relative path must start with /^(?:\.\.?\/)*[^\/]/. E.g.: /^(?:\.\.?\/)*[^\/]/.test("./hello/world") --> true; /^(?:\.\.?\/)*[^\/]/.test("../hi") --> true; /^(?:\.\.?\/)*[^\/]/.test("../././../foo") --> true; /^(?:\.\.?\/)*[^\/]/.test("/") --> false; /^(?:\.\.?\/)*[^\/]/.test("/?foo=bar") --> false;
|
6

If you want to make a relative-to-absolute conversion for a link from a custom webpage in your browser (not for the page that runs your script), you can use a more enhanced version of the function suggested by @Bergi:

var resolveURL=function resolve(url, base){
 if('string'!==typeof url || !url){
 return null; // wrong or empty url
 }
 else if(url.match(/^[a-z]+\:\/\//i)){ 
 return url; // url is absolute already 
 }
 else if(url.match(/^\/\//)){ 
 return 'http:'+url; // url is absolute already 
 }
 else if(url.match(/^[a-z]+\:/i)){ 
 return url; // data URI, mailto:, tel:, etc.
 }
 else if('string'!==typeof base){
 var a=document.createElement('a'); 
 a.href=url; // try to resolve url without base 
 if(!a.pathname){ 
 return null; // url not valid 
 }
 return 'http://'+url;
 }
 else{ 
 base=resolve(base); // check base
 if(base===null){
 return null; // wrong base
 }
 }
 var a=document.createElement('a'); 
 a.href=base;
 if(url[0]==='/'){ 
 base=[]; // rooted path
 }
 else{ 
 base=a.pathname.split('/'); // relative path
 base.pop(); 
 }
 url=url.split('/');
 for(var i=0; i<url.length; ++i){
 if(url[i]==='.'){ // current directory
 continue;
 }
 if(url[i]==='..'){ // parent directory
 if('undefined'===typeof base.pop() || base.length===0){ 
 return null; // wrong url accessing non-existing parent directories
 }
 }
 else{ // child directory
 base.push(url[i]); 
 }
 }
 return a.protocol+'//'+a.hostname+base.join('/');
}

It'll return null if something is wrong.

Usage:

resolveURL('./some.css', 'http://example.com/stats/2012/'); 
// returns http://example.com/stats/2012/some.css
resolveURL('extra/some.css', 'http://example.com/stats/2012/');
// returns http://example.com/stats/2012/extra/some.css
resolveURL('../../lib/slider/slider.css', 'http://example.com/stats/2012/');
// returns http://example.com/lib/slider/slider.css
resolveURL('/rootFolder/some.css', 'https://example.com/stats/2012/');
// returns https://example.com/rootFolder/some.css
resolveURL('localhost');
// returns http://localhost
resolveURL('../non_existing_file', 'example.com')
// returns null
answered Sep 29, 2015 at 11:49

Comments

4
function canonicalize(url) {
 var div = document.createElement('div');
 div.innerHTML = "<a></a>";
 div.firstChild.href = url; // Ensures that the href is properly escaped
 div.innerHTML = div.innerHTML; // Run the current innerHTML back through the parser
 return div.firstChild.href;
}

This works on IE6 too, unlike some other solutions (see Getting an absolute URL from a relative one. (IE6 issue))

answered Dec 30, 2014 at 16:47

2 Comments

Why do you "Run the current innerHTML back through the parser"?
check the blog post linked here: stackoverflow.com/a/22918332/82609
2

The proposed and accepted solution does not support server relative URLs and does not work on absolute URLs. If my relative is /sites/folder1 it won't work for example.

Here is another function that supports full, server relative or relative URLs as well as ../ for one level up. It is not perfect but covers a lot of options. Use this when your base URL is not the current page URL, otherwise there are better alternatives.

 function relativeToAbsolute(base, relative) {
 //make sure base ends with /
 if (base[base.length - 1] != '/')
 base += '/';
 //base: https://server/relative/subfolder/
 //url: https://server
 let url = base.substr(0, base.indexOf('/', base.indexOf('//') + 2));
 //baseServerRelative: /relative/subfolder/
 let baseServerRelative = base.substr(base.indexOf('/', base.indexOf('//') + 2));
 if (relative.indexOf('/') === 0)//relative is server relative
 url += relative;
 else if (relative.indexOf("://") > 0)//relative is a full url, ignore base.
 url = relative;
 else {
 while (relative.indexOf('../') === 0) {
 //remove ../ from relative
 relative = relative.substring(3);
 //remove one part from baseServerRelative. /relative/subfolder/ -> /relative/
 if (baseServerRelative !== '/') {
 let lastPartIndex = baseServerRelative.lastIndexOf('/', baseServerRelative.length - 2);
 baseServerRelative = baseServerRelative.substring(0, lastPartIndex + 1);
 }
 }
 url += baseServerRelative + relative;//relative is a relative to base.
 }
 return url;
}

Hope this helps. It was really frustrating not to have this basic utility available in JavaScript.

answered Apr 4, 2017 at 21:20

2 Comments

As far as I can tell, this wouldn't work with protocol-independent urls? Eg. //www.example.com/page.
No, but it is easy to support if you just change this statement: else if (relative.indexOf("://") > 0) and remove the : but I keep it this way since it can take the protocol from the base, which I send dynamically anyway.
2

I know this is a very old question, but you could do it with: (new URL(relativePath, location)).href.

answered May 12, 2021 at 18:21

Comments

1

The href solution only works once the document is loaded (at least in IE11). This worked for me:

link = link || document.createElement("a");
link.href = document.baseURI + "/../" + href;
return link.href;

See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base

answered Nov 18, 2013 at 20:01

1 Comment

I think you should use document.baseURI instead of window.location.href in order to take into account a possible <base> element.
0

I had to add a fix to the accepted solution because we can have slashes after # in our angularjs navigation.

function getAbsoluteUrl(base, relative) {
 // remove everything after #
 var hashPosition = base.indexOf('#');
 if (hashPosition > 0){
 base = base.slice(0, hashPosition);
 }
 // the rest of the function is taken from http://stackoverflow.com/a/14780463
 // http://stackoverflow.com/a/25833886 - this doesn't work in cordova
 // http://stackoverflow.com/a/14781678 - this doesn't work in cordova
 var stack = base.split("/"),
 parts = relative.split("/");
 stack.pop(); // remove current file name (or empty string)
 // (omit if "base" is the current folder without trailing slash)
 for (var i=0; i<parts.length; i++) {
 if (parts[i] == ".")
 continue;
 if (parts[i] == "..")
 stack.pop();
 else
 stack.push(parts[i]);
 }
 return stack.join("/");
}
answered Dec 23, 2015 at 7:28

1 Comment

I added if (charAt(0) !== ".") return relative; to handle the case where relative is already an absolute path, so I don't have to check if it's relative first, before calling this function.
0

This will work. but only when you open a page with it's file name. it will not work well when you open a link like this stackoverflow.com/page. it will work with stackoverflow.com/page/index.php

function reltoabs(link){
 let absLink = location.href.split("/");
 let relLink = link;
 let slashesNum = link.match(/[.]{2}\//g) ? link.match(/[.]{2}\//g).length : 0;
 for(let i = 0; i < slashesNum + 1; i++){
 relLink = relLink.replace("../", "");
 absLink.pop();
 }
 absLink = absLink.join("/");
 absLink += "/" + relLink;
 return absLink;
}
answered Feb 27, 2019 at 22:24

Comments

0

A current good option is by using URL.parse(path,base) method.

URL.parse('../../lib/slider/slider.css','http://example.com/stats/2012/')
// returns an URL object with 'http://example.com/lib/slider/slider.css'

In case there's something wrong will return null.


In my case I only need to resolve the pathname, so I optimized in this way:

URL.parse('../../lib/slider/slider.css','x:/stats/2012/').pathname
// returns a string = '/lib/slider/slider.css'
answered Nov 15, 2024 at 20:20

Comments

-1

I found a very simple solution to do this while still supporting IE 10 (IE doesn't support the URL-API) by using the History API (IE 10 or higher). This solution works without any string manipulation.

function resolveUrl(relativePath) {
 var originalUrl = document.location.href;
 history.replaceState(history.state, '', relativePath);
 var resolvedUrl = document.location.href;
 history.replaceState(history.state, '', originalUrl);
 return resolvedUrl;
}

history.replaceState() won't trigger browser navigation, but will still modify document.location and supports relative aswell as absolute paths.

The one drawback of this solution is that if you are already using the History-API and have set a custom state with a title, the current state's title is lost.

answered Oct 15, 2018 at 12:18

Comments

-1

Try:

/**
 * Convert relative paths to absolute paths
 * @author HaNdTriX
 * @param {string} html - HTML string
 * @param {string} baseUrl - base url to prepend to relative paths
 * @param {string[]} [attributes] - attributes to convert
 * @returns {string}
 */
function absolutify(
 html,
 baseUrl,
 attributes = [
 "href",
 "src",
 "srcset",
 "cite",
 "background",
 "action",
 "formaction",
 "icon",
 "manifest",
 "code",
 "codebase",
 ]
) {
 // Build the regex to match the attributes.
 const regExp = new RegExp(
 `(?<attribute>${attributes.join(
 "|"
 )})=(?<quote>['"])(?<path>.*?)\\k<quote>`,
 "gi"
 );
 return html.replaceAll(regExp, (...args) => {
 // Get the matched groupes
 const { attribute, quote, path } = args[args.length - 1];
 // srcset may have multiple paths `<url> <descriptor>, <url> <descriptor>`
 if (attribute.toLowerCase() === "srcset") {
 const srcSetParts = path.split(",").map((dirtyPart) => {
 const part = dirtyPart.trim();
 const [path, size] = part.split(" ");
 return `${new URL(path.trim(), baseUrl).toString()} ${size || ""}`;
 });
 return `${attribute}=${quote}${srcSetParts.join(", ")}${quote}`;
 }
 const absoluteURL = new URL(path, baseUrl).href;
 return `${attribute}=${quote}${absoluteURL}${quote}`;
 });
}
console.log(
 absolutify("<img src='./fooo.png'>", "https://example.com")
)

answered Nov 29, 2022 at 12:00

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.