翻译:<a>标签 href 属性值的种种可能性

5 阅读4分钟

以下是 关于锚元素href的一些你可能不知道的事情——Jim Nielsen的博客的中文翻译,保留了原文的技术细节和结构:


<a> 标签 href 属性值的种种可能性

我之前写过一篇只用 HTML 重载页面的文章,这让我开始思考:<a> 标签的 href 属性中,我们到底能放哪些值?

我查阅了一些资料,发现了一些我早已知道的内容,比如:

  • 链接协议(Link protocols),如 mailto:tel:sms:javascript:,它们用于触发特定的链接行为。
  • 协议相对链接(Protocol-relative URLs),例如 href="//"
  • 文本片段(Text fragments),用于链接到页面中的特定文本内容,例如 href="#:~:text=foo"

但我也发现了一些以前不知道(或只是模糊了解)的内容,于是我把它们记录下来,方便自己记忆。


href="#"

滚动到页面顶部。这个我知道。

但我想补充一点:如果页面中没有其他元素的 id="top",那么 #top 也会滚动到页面顶部。这一点我之前并不知道。

规范说明:“如果解码后的片段(decodedFragment)与字符串 top 在 ASCII 不区分大小写的条件下匹配,则返回文档顶部。”

更新:Mastodon 上的 HTeuMeuLeu 指出,你还可以使用 #page= 来实现 PDF 文件的深度链接,例如 my-file.pdf#page=42 会跳转到该 PDF 的第 42 页。


href=""

重新加载当前页面,保留查询参数(search string),但移除哈希(hash)部分(如果存在)。

当前 URL解析后结果
/path//path/
/path/#foo/path/
/path/?id=foo/path/?id=foo
/path/?id=foo#bar/path/?id=foo

href="."

加载当前目录的默认索引页,同时移除查询参数和哈希(如果存在)。

注意:如果你打算用 href="." 作为指向当前页面的链接,请确保你的 URL 带有尾部斜杠(trailing slash),否则可能会出现意外的导航行为。因为路径会被解释为一个文件,而 "." 会解析为当前路径的父目录。

当前 URL解析后结果
/path/
/path#foo/
/path?id=foo/
/path//path/
/path/#foo/path/
/path/?id=foo/path/
/path/index.html/path/

2025 年 8 月 15 日更新:正如 Mastodon 上的 @AmeliaBR 所指出,“重新加载当前页面”可能不是最准确的说法。更准确地说,这是“根据 URL 结构加载当前目录的默认索引页”,它可能是重载,也可能不是——具体取决于当前 URL(参见上表和注释)。


href="?"

加载当前页面,移除查询参数和哈希,但保留 ? 字符本身

注意:与 href="." 不同,这里是否带尾部斜杠无关紧要。查询参数会被清除,但路径会原样保留。

当前 URL解析后结果
/path/path?
/path#foo/path?
/path?id=foo/path?
/path?id=foo#bar/path?
/index.html/index.html?

href="data:"

你可以创建指向 data URL 的链接。例如,可读性较强的形式如下:

<a href="data:text/plain,hello world">
  查看纯文本 data URL
</a>

但为了防止意外行为,建议对 data URL 进行编码,例如:

<a href="data:text/plain,hello%20world">
  查看纯文本 data URL
</a>

你可以试试看(注意:某些浏览器可能不支持)。这里有一个纯文本文件和一个 HTML 文件的示例。


href="video.mp4#t=10,20"

媒体片段(Media Fragments) 允许你链接到音视频文件的特定时间段。

例如,video.mp4#t=10,20 表示:从第 10 秒开始播放,到第 20 秒停止。

(截至本文撰写时,浏览器对此特性的支持仍有限。)


亲自验证

我在浏览器中和通过 JavaScript 对这些行为进行了大量测试,我认为上述内容基本都正确。

借助 JavaScript 的 URL 构造函数(并传入 base 参数),我可以程序化地探索这些 href 值是如何解析的。

以下是我编写的一段测试代码。你可以复制粘贴到控制台中运行,所有断言都应该通过:

const assertions = [
  { href: '', location: '/path',               resolves_to: '/path' },
  { href: '', location: '/path/',              resolves_to: '/path/' },
  { href: '', location: '/path/#foo',          resolves_to: '/path/' },
  { href: '', location: '/path/?id=foo',       resolves_to: '/path/?id=foo' },
  { href: '', location: '/path/?id=foo#bar',   resolves_to: '/path/?id=foo' },
  { href: '.', location: '/path',              resolves_to: '/' },
  { href: '.', location: `/path#foo`,          resolves_to: `/` },
  { href: '.', location: `/path?id=foo`,       resolves_to: `/` },
  { href: '.', location: `/path/`,             resolves_to: `/path/` },
  { href: '.', location: `/path/#foo`,         resolves_to: `/path/` },
  { href: '.', location: `/path/?id=foo`,      resolves_to: `/path/` },
  { href: '.', location: `/path/index.html`,   resolves_to: `/path/` },
  { href: '?', location: '/path',              resolves_to: '/path?' },
  { href: '?', location: '/path#foo',          resolves_to: '/path?' },
  { href: '?', location: '/path?id=foo',       resolves_to: '/path?' },
  { href: '?', location: '/path/',             resolves_to: '/path/?' },
  { href: '?', location: '/path/?id=foo#bar',  resolves_to: '/path/?' },
  { href: '?', location: '/index.html#foo',    resolves_to: '/index.html?'}
];

const assertions_evaluated = assertions.map(({ href, location, resolves_to }) => {
  const domain = 'https://example.com';
  const expected = new URL(href, domain + location).toString();
  const received = new URL(domain + resolves_to).toString();
  return {
    href,
    location,
    expected: expected.replace(domain, ''),
    received: received.replace(domain, ''),
    passed: expected === received
  };
});

console.table(assertions_evaluated);

希望这篇总结对你理解 href 属性的各种用法有所帮助!