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.
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.