使用 JavaScript 将相对路径转换为绝对路径

IT技术 javascript html path
2021-02-23 11:34:55

有一个函数,它给了我这样的网址:

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

它始终是一条相对路径。

假设我们知道页面的当前路径,例如http://site.com/stats/2012/,不知道如何将这些相对路径转换为真实路径?

我们应该得到类似的东西:

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

没有 jQuery,只有 vanilla javascript。

6个回答

最简单、有效和正确的方法就是使用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"

在性能方面,此解决方案与使用字符串操作相当,速度是创建a标签的两倍

@hex 你是对的,使用window.location.href有这个缺点。我已经编辑了答案以使用它document.baseURI
2021-04-17 11:34:55
如何在 ubuntu 中运行此解决方案?
2021-04-24 11:34:55
喜欢这个解决方案,但是由于缺乏或 URL 支持,它不适用于 React Native。相反,我使用了https://www.npmjs.com/package/url,效果很好。
2021-05-03 11:34:55
这不是开发环境的通用解决方案。
2021-05-14 11:34:55

Javascript 将为您完成。无需创建函数。

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"

但是如果你需要它作为一个函数:

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

更新:如果您需要完整的绝对路径,请使用更简单的版本:

var absolutePath = function(href) {
    var link = document.createElement("a");
    link.href = href;
    return link.href;
}
它实际上是一个 DOM 解决方案
2021-04-16 11:34:55
这在 IE 中不起作用,甚至在 IE11 中也不起作用。有关更多详细信息,请参阅:stackoverflow.com/questions/470832/...
2021-04-30 11:34:55
@ChrisBaxter 同样在这里。我有点恼火,这个流行的答案是指协议、主机等……而 href 看起来更简单、更安全。是否有一个原因?我想知道...
2021-05-01 11:34:55
效果很好(在 IE 中也是如此)...我发现link.href在设置后简单地访问会返回已解析的 URL(即,无需手动重建 href)。
2021-05-13 11:34:55

这应该这样做:

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("/");
}
@Daniel_L:~通常意味着除base. 而且它不完全是相对路径:-) 如果您需要有关包含波浪号的路径的帮助,请提出一个新问题
2021-04-18 11:34:55
当相对 url/以 example开头时,此解决方案会出现问题/some.css在这种情况下,正确的实现将删除堆栈中域名后的所有项目。
2021-04-26 11:34:55
@Vineet 正确,但是以 开头的路径/不是相对的。我的函数只考虑路径,而不是 URI。
2021-04-27 11:34:55
对于以后遇到此问题的任何人,使用 document.location.href 作为 base 对我有用。
2021-05-05 11:34:55
一些站点有内部资源~, ~/media/style.css, 前几天遇到了这个
2021-05-09 11:34:55

这来自MDN是牢不可破的!

/*\
|*|
|*|  :: 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);
}

示例用法:

/* 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
@Nucleon 该函数期望您提供相对路径。该字符串/?foo=bar是一个错误的相对路径(实际上它是一个绝对路径)。有效的相对路径必须以/^(?:\.\.?\/)*[^\/]/. 例如:/^(?:\.\.?\/)*[^\/]/.test("./hello/world")--> true; /^(?:\.\.?\/)*[^\/]/.test("../hi")--> true; /^(?:\.\.?\/)*[^\/]/.test("../././../foo")--> true; /^(?:\.\.?\/)*[^\/]/.test("/")--> false; /^(?:\.\.?\/)*[^\/]/.test("/?foo=bar")--> false;
2021-04-16 11:34:55
@Nucleon 抱歉,我误解了您的评论。一个路径开始/已经一个绝对路径!因此将其作为该函数的参数是没有意义的!该字符串"/?foo=bar" 已经是绝对路径。
2021-04-21 11:34:55
没错,但是在此页面的 URL 上运行您的函数relPathToAbs("/?foo=bar")它不返回/?foo=bar(因为它已经是绝对的)它返回/questions/14780350/?foo=bar
2021-04-23 11:34:55
它不处理带有前导的相对路径/示例:relPathToAbs("/?foo=bar")应该返回 "/?foo=bar" 而不是它返回/questions/14780350/?foo=bar,这意味着/当将路径发送回根时,它没有正确检测到前导
2021-04-27 11:34:55
@Nucleon路径是包含在协议段 ( http(s)://) 和搜索/哈希段 ( ?/ #)之间的 URL部分函数关心将路径部分与搜索/哈希部分分开,也因为正则表达式可以使它变得非常简单:relPathToAbs("./?foo=bar#someHash".replace(/^[^\?#]*/, "$&"))
2021-05-12 11:34:55

如果您想对来自浏览器中的自定义网页的链接(而不是运行脚本的页面)进行相对到绝对的转换,您可以使用@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('/');
}

null如果有什么问题,它会返回

用法:

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