How To Join Relative URLs in JavaScript

32,322

Solution 1

The following function decomposes the URL then resolves it.

function concatAndResolveUrl(url, concat) {
  var url1 = url.split('/');
  var url2 = concat.split('/');
  var url3 = [ ];
  for (var i = 0, l = url1.length; i < l; i ++) {
    if (url1[i] == '..') {
      url3.pop();
    } else if (url1[i] == '.') {
      continue;
    } else {
      url3.push(url1[i]);
    }
  }
  for (var i = 0, l = url2.length; i < l; i ++) {
    if (url2[i] == '..') {
      url3.pop();
    } else if (url2[i] == '.') {
      continue;
    } else {
      url3.push(url2[i]);
    }
  }
  return url3.join('/');
}

Solution 2

8 years later, many browsers (except for Internet Explorer) support the URL constructor (URL(url [, base])).

> new URL('../address', 'http://www.adress.com/more/evenmore/').href
"http://www.adress.com/more/address"
> new URL('../../address', 'http://www.adress.com/more/evenmore/').href
"http://www.adress.com/address"

Solution 3

Using URI.js (urijs - npm): absoluteTo():

function joinUrl(baseUrl, url) {
    var theUrl = new URI(url);
    if (theUrl.is("relative")) {
        theUrl = theUrl.absoluteTo(baseUrl);
    }
    return theUrl.toString();
}

Solution 4

The ECMAScript URL Web API mentioned by @ning is a good place to start: especially as it is available in vanilla JS implementations (Node, etc.) and doesn't require you to use a library that does something the implementation nowq already accomplishes. Consulting the MDN documentation, more specifically the examples, is a great place to start.

Borrowing (somewhat) directly from their documentation:

let m = 'https://developer.mozilla.org';

// ... omitted 

let d = new URL('/en-US/docs', m);

If you were to do the above, then console.log the .toString of the constructed URL object, your output would be: 'https://developer.mozilla.org/en-US/docs'.

Importantly, if you consult the Syntax section of the documentation, you will note that the second argument is optional and the second argument (as seen in the above example) represents the base URL (though only in the case of two arguments being supplied).

If both argument values are absolute URLs, Web API honors the first and discards the second.

If you are working with Node.js, I would encourage you to look at the native path module for doing work on relative paths, again over using a library. The main motivation here is that spinning up your own algorithm (probably just a call to path here and there) is potentially better than introducing a library that will pull in several other dependencies that may introduce vulnerabilities and unnecessary bloat to your application (or just be too heavy weight for what you need).

However, if you are working on the front end, you won't have path available to you and - as mentioned in @cburgmer's answer comments - Web API's URL doesn't support the relative path case mentioned. In this case, you may need to look for a library to accomplish this for you; however, again, given the other answers, I'd consider trying out a home-spun approach.

To their credit, URI.js currently only integrates one non-dev. dependency and doesn't have a huge footprint.

Share:
32,322

Related videos on Youtube

heffaklump
Author by

heffaklump

Updated on August 25, 2020

Comments

  • heffaklump
    heffaklump over 3 years

    I want to join two strings each representing a relative URL in Javascript.

    I want to join the base URL http://www.adress.com/more/evenmore with the following examples:

    1. ../../adress (with the expected output: http://www.adress.com/adress)
    2. ../adress (with the expected output http://www.adress.com/more/adress)

    What would be the best way? I was thinking of using regexp and checking
    how many ../ preceed the relative URL, then subtracting that amount from the baseurl and adding them to what is left.

    • julealgon
      julealgon over 2 years
      Please consider changing the accepted answer on this.
  • Jay
    Jay over 11 years
    I modified this where your testing for '.' to also test for empty string url1[i] == '.' || url1[i] == '' so if the passed in URLs have slashes it still composes a valid URL. +1
  • Gordon Truslove
    Gordon Truslove almost 5 years
    this doesn't seem to work as expected. /a + ./b = /a/b and it should be /b
  • Thomas
    Thomas almost 4 years
    These days there's no real need to use a library.
  • cburgmer
    cburgmer almost 4 years
    This library option works if you have to relative URLs to combine. Because ECMAScript's URL throws an error: new URL("../the_relative_url", "rel/path/") TypeError: URL constructor: rel/path/ is not a valid URL.
  • Thomas
    Thomas almost 4 years
    I'd almost want to add that to your answer. That's a great justification for avoiding URL, especially on the front end where access to Node.js native modules isn't supported.
  • netAction
    netAction over 2 years
    @GordonTruslove: Maybe this solves your issue, it gets rid of the a. url1[ url1.length-1 ] = ''