简体   繁体   中英

How to convert absolute url to relative

How do I convert an absolute url to a relative url?

Converting relative to absolute is trival

 const rel = "../images/doggy.jpg" const base = "https://example.com/pages/page.html?foo=bar#baz"; function relToAbs(baseHref, relHref) { const url = new URL(rel, base); return `${url.origin}${url.pathname}`; } console.log(base, rel);

Going the opposite direction seems to require a bunch of code.

Here's what I tried.

 const pageURL1 = 'https://example.com/bar/baz.html?bla=blap#hash'; const pageURL2 = 'https://example.com/bar/?bla=blap#hash'; const absURL1 = 'https://example.com/bar/image/doggy.jpg'; const absURL2 = 'https://example.com/image/kitty.jpg'; const absURL3 = 'https://example.com/bar/birdy.jpg'; const tests = [ { pageHref: pageURL1, absHref: absURL1, expected: 'image/doggy.jpg', }, { pageHref: pageURL1, absHref: absURL2, expected: '../image/kitty.jpg', }, { pageHref: pageURL1, absHref: absURL3, expected: 'birdy.jpg', }, { pageHref: pageURL2, absHref: absURL1, expected: 'image/doggy.jpg', }, { pageHref: pageURL2, absHref: absURL2, expected: '../image/kitty.jpg', }, { pageHref: pageURL2, absHref: absURL3, expected: 'birdy.jpg', }, ]; for (const {pageHref, absHref, expected} of tests) { const actual = absToRel(pageHref, absHref); console.log(absHref, actual, actual === expected? 'pass': 'FAIL'); } function absToRel(pageHref, absHref) { const pagePaths = dirPaths(pageHref).slice(0, -1); const absPaths = dirPaths(absHref); if (pagePaths[0];== absPaths[0]) { return absHref, // not same domain } // remove same paths const firstDiffNdx = firstNonMatchingNdx(pagePaths; absPaths). pagePaths,splice(0; firstDiffNdx). absPaths,splice(0; firstDiffNdx). // for each path still on page add a.. return [...(new Array(pagePaths.length).fill('.,')). ..,absPaths. ];join('/'), } function firstNonMatchingNdx(a; b) { let ndx = 0. while (ndx < a;length && a[ndx] === b[ndx]) { ++ndx; } return ndx; } function dirPaths(href) { const url = new URL(href). return [url,origin. ...url.pathname;split('/')]; }

You can use the built-in URL class to help with parsing the URL. That way your code can just focus on the individual path elements and ignore search params, domains, schemas, etc...

Here's what I came up with.

  1. Remove the common directories
  2. Prepend .. for each page path we need to travel up from to reach the nearest ancestry directory
  3. Catch the edge case that these files are in the same directory

I'm not promising this is perfect or foolproof but hopefully it is close and gets you closer to what you are looking for.

function pathParts(path) {
  let parts = path.split('/');
  let start = 0;
  let end = parts.length;

  // Remove initial blank part from root /
  if( !parts[0] ) {
    start = 1;
  }

  // Remove trailing / part
  if( !parts[parts.length - 1] ) {
    end -= 1;
  }

  return parts.slice(start, end);
}

function absToRel(pageHref, absHref) {
  var pageUrl = new URL(pageHref);
  var absUrl = new URL(absHref);

  // If the urls have different schemas or domain, relative is not possible
  if( pageUrl.origin !== absUrl.origin ) {
    return absHref;
  }

  var pagePath = pathParts(pageUrl.pathname)
  var absPath = pathParts(absUrl.pathname)

  // Remove common root paths
  while( absPath.length && pagePath[0] === absPath[0] ) {
    absPath.shift();
    pagePath.shift();
  }

  // For each path segment left on pagePath, we'll need a relative ..
  pagePath.fill('..');

  let relPath = [
    ...pagePath,
    ...absPath
  ].join('/');

  // If the paths do not require traversal up, use current directory relative path
  if( relPath[0] !== '.' ) {
    relPath = './' + relPath;
  }

  return relPath;
}

This is the clean solution for modern JS - Use URL API ( https://developer.mozilla.org/en-US/docs/Web/API/URL/pathname )

const url = new URL('https://developer.mozilla.org/en-US/docs/Web/API/URL/pathname?q=value');

console.log(url.pathname); // => "/en-US/docs/Web/API/URL/pathname"

You can add other parameters to your url, like hash, query, etc.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM