以下是 关于锚元素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 属性的各种用法有所帮助!