使用 JavaScript 将相对路径转换为绝对路径
Posted
技术标签:
【中文标题】使用 JavaScript 将相对路径转换为绝对路径【英文标题】:Convert relative path to absolute using JavaScript 【发布时间】:2013-01-24 16:16:33 【问题描述】:有一个函数,它给了我这样的网址:
./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,只有原版 javascript。
【问题讨论】:
这是针对 javascript 应用程序,而不是常规站点。 【参考方案1】:最简单、最有效、最正确的方法就是使用URL api。
new URL("http://www.***.com?q=hello").href;
//=> http://www.***.com/?q=hello"
new URL("mypath","http://www.***.com").href;
//=> "http://www.***.com/mypath"
new URL("../mypath","http://www.***.com/search").href
//=> "http://www.***.com/mypath"
new URL("../mypath", document.baseURI).href
//=> "https://***.com/questions/mypath"
在性能方面,此解决方案与使用字符串操作相当,速度是创建 a
标记的两倍。
【讨论】:
喜欢这个解决方案,但是由于缺少 URL 支持,它不能与 React Native 一起使用。相反,我使用了https://www.npmjs.com/package/url,效果很好。 @hex 你说得对,使用window.location.href
有这个缺点。我已经编辑了答案,让它改用document.baseURI
。
这不是开发环境的通用解决方案。
如何在 ubuntu 中运行这个解决方案?【参考方案2】:
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;
【讨论】:
这在 IE 中不起作用,甚至在 IE11 中也不起作用。有关详细信息,请参阅此:***.com/questions/470832/… 效果很好(在 IE 中也是如此)...我发现设置后只需访问link.href
即可返回解析的 URL(即,无需手动重建 href)。
@ChrisBaxter 这里也一样。我有点恼火,这个流行的答案是指协议、主机等……而 href 看起来更简单、更安全。是否有一个原因?我想知道...
其实是DOM方案【参考方案3】:
应该这样做:
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("/");
【讨论】:
有些网站有内部资源,~
,~/media/style.css
,前几天碰到这个
@Daniel_L: ~
通常指的不是base
。而且它不完全是相对路径 :-) 如果您在包含波浪号的路径方面需要帮助,请ask a new question
对于以后遇到这种情况的任何人,使用 document.location.href 作为 base 对我有用。
当相对 url 以 /
开头时,此解决方案会出现问题,例如 /some.css
。在这种情况下,正确的实现会删除堆栈中域名后面的所有项目。
@Vineet 正确,但是以 /
开头的路径不是相对的。我的函数只考虑路径,而不考虑 URI。【参考方案4】:
来自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
【讨论】:
它不处理带有前导/
的相对路径。示例:relPathToAbs("/?foo=bar")
应该返回 "/?foo=bar" 而不是返回 /questions/14780350/?foo=bar
,这意味着当将路径发送回根时,它没有正确检测到前导 /
。
@Nucleon path 是 protocol 段 (http(s)://
) 和 search 之间的 URL 部分i> / hash 段 (?
/#
)。 该函数不需要将 path 部分与 search / hash 部分分开,这也是因为Regular表达式可以让它变得非常简单:relPathToAbs("./?foo=bar#someHash".replace(/^[^\?#]*/, "$&"))
@Nucleon 对不起,我误解了你的评论。以/
开头的路径已经是绝对路径!因此,将其作为该函数的参数是没有意义的!字符串"/?foo=bar"
is 已经是一个absolute 路径。
没错,但是在此页面的 URL relPathToAbs("/?foo=bar")
上运行您的函数。它不返回/?foo=bar
(因为它已经是绝对的)它返回/questions/14780350/?foo=bar
。
@Nucleon 该函数希望您提供相对路径。字符串/?foo=bar
是错误 相对路径(实际上是绝对路径)。有效的相对路径必须以 /^(?:\.\.?\/)*[^\/]/
开头。例如:/^(?:\.\.?\/)*[^\/]/.test("./hello/world")
--> true
; /^(?:\.\.?\/)*[^\/]/.test("../hi")
--> true
; /^(?:\.\.?\/)*[^\/]/.test("../././../foo")
--> true
; /^(?:\.\.?\/)*[^\/]/.test("/")
--> false
; /^(?:\.\.?\/)*[^\/]/.test("/?foo=bar")
--> false
;【参考方案5】:
如果您想对浏览器中自定义网页的链接(而不是运行脚本的页面)进行相对到绝对转换,您可以使用@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
【讨论】:
【参考方案6】:我知道这是一个非常古老的问题,但您可以这样做:
(new URL(relativePath, location)).href
.
【讨论】:
【参考方案7】: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;
这也适用于 IE6,与其他一些解决方案不同(请参阅 Getting an absolute URL from a relative one. (IE6 issue))
【讨论】:
你为什么“通过解析器运行当前的innerHTML”? 查看此处链接的博文:***.com/a/22918332/82609【参考方案8】:建议和接受的解决方案不支持服务器相对 URL,也不适用于绝对 URL。 例如,如果我的亲戚是 /sites/folder1,它将不起作用。
这是另一个功能,它支持完整的、服务器相对或相对 URL 以及 ../ 用于上一级。它并不完美,但涵盖了很多选择。 当您的基本 URL 不是当前页面 URL 时使用此选项,否则会有更好的选择。
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;
希望这会有所帮助。没有在 JavaScript 中提供这个基本实用程序真是令人沮丧。
【讨论】:
据我所知,这不适用于与协议无关的网址?例如。//www.example.com/page
.
不,但如果你只是改变这个语句很容易支持: else if (relative.indexOf("://") > 0) 并删除 : 但我保持这种方式,因为它可以从基础中获取协议,无论如何我都是动态发送的。【参考方案9】:
href 解决方案仅在加载文档后才有效(至少在 IE11 中)。这对我有用:
link = link || document.createElement("a");
link.href = document.baseURI + "/../" + href;
return link.href;
见https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
【讨论】:
我认为您应该使用document.baseURI
而不是window.location.href
以考虑可能的<base>
元素。【参考方案10】:
我必须为已接受的解决方案添加一个修复程序,因为我们可以在 angularjs 导航中的 # 之后添加斜杠。
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://***.com/a/14780463
// http://***.com/a/25833886 - this doesn't work in cordova
// http://***.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("/");
【讨论】:
我添加了if (charAt(0) !== ".") return relative;
来处理relative
已经是绝对路径的情况,所以在调用这个函数之前我不必先检查它是否是相对的。【参考方案11】:
我找到了一个非常简单的解决方案,通过使用History API(IE 10 或更高版本)仍然支持 IE 10(IE 不支持 URL-API)。此解决方案无需任何字符串操作即可工作。
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()
不会触发浏览器导航,但仍会修改document.location
并支持相对路径和绝对路径。
此解决方案的一个缺点是,如果您已经在使用 History-API 并设置了带有标题的自定义状态,则当前状态的标题会丢失。
【讨论】:
【参考方案12】:这会奏效。但仅当您打开带有文件名的页面时。当您打开像 ***.com/page
这样的链接时,它将无法正常工作。它适用于***.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;
【讨论】:
以上是关于使用 JavaScript 将相对路径转换为绝对路径的主要内容,如果未能解决你的问题,请参考以下文章