大家好,我是程序员柏风,AI 最全的 Vibecoding 学习地图 http://coderoadmap.cn 作者。
HTML 链接
这一课你将能做到
- 正确使用 a 标签的 href、target、rel、download 属性,写出符合安全规范的外链
- 实现页内锚点跳转、跨页锚点定位,并处理锚点被 fixed header 遮挡的问题
- 写出触发系统邮件、电话、短信的特殊协议链接,以及强制文件下载的链接
为什么要学
链接是 Web 的「血管」,没有链接的页面是孤岛。但一个 <a> 标签背后藏着不少坑:target="_blank" 不带 rel="noopener" 有安全风险;href="#" 和 href="javascript:..." 是反模式;相对路径写错导致 404;锚点滚动被 fixed 导航栏遮住。这些细节做不好,用户会丢、安全会漏、SEO 会掉。
人话:链接不只是「点一下跳过去」,它还决定新页面从哪打开、搜索引擎给不给权重、用户数据安不安全。
先建立图像
链接的核心三要素:
<a href="目标" target="打开方式" rel="安全关系">显示文本</a>
│ │ │
│ │ └── noopener/nofollow 等安全策略
│ └── _self(默认)/_blank(新标签)
└── URL / #锚点 / mailto: / tel: / javascript:(不推荐)
跟着做
步骤 1:基础链接与 target
新建 links.html:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>链接演示</title>
</head>
<body>
<!-- 当前页跳转(默认) -->
<a href="https://developer.mozilla.org">MDN 文档</a>
<!-- 新标签页打开 -->
<a href="https://github.com" target="_blank">GitHub</a>
<!-- 新标签页 + 安全策略 -->
<a href="https://example.com" target="_blank" rel="noopener noreferrer">
外部链接(安全版)
</a>
</body>
</html>
为什么需要 rel="noopener noreferrer":
noopener:阻止新页面通过window.opener访问原页面(钓鱼攻击常用手段)noreferrer:不发送 Referer 头,保护来源 URL 隐私- 现代浏览器已默认对
target="_blank"启用noopener,但显式写出是防御性编程
步骤 2:锚点链接(页内跳转)
<nav>
<a href="#section1">第一章</a>
<a href="#section2">第二章</a>
<a href="#section3">第三章</a>
</nav>
<section id="section1">
<h2>第一章</h2>
<p>内容……</p>
</section>
<section id="section2">
<h2>第二章</h2>
<p>内容……</p>
</section>
跨页锚点:href="page.html#section2",先加载 page.html,再滚动到 id="section2" 的位置。
步骤 3:返回顶部与空链接的坑
<!-- 推荐:回到页面顶部 -->
<a href="#top">回到顶部</a>
<!-- 配合 body 或 html 的 id="top" -->
<!-- 反模式:不要用 -->
<a href="#">点我</a>
<a href="javascript:void(0)">点我</a>
<a href="javascript:;">点我</a>
href="#" 会让页面跳到顶部并添加历史记录;javascript: 协议已被现代安全策略严格限制。如果只需要一个「按钮式链接」,用 <button type="button"> 配合 CSS 做成链接样式。
步骤 4:下载链接
<!-- 浏览器直接下载,不打开 -->
<a href="/files/report.pdf" download>下载报告</a>
<!-- 指定下载文件名 -->
<a href="/files/report.pdf" download="2026年度总结.pdf">下载报告</a>
<!-- 跨域下载受限:download 属性对跨域资源无效 -->
注意:download 仅对同源文件有效。跨域资源浏览器会忽略 download,直接导航或预览。
步骤 5:特殊协议链接
<!-- 邮件:触发系统默认邮件客户端 -->
<a href="mailto:someone@example.com">发邮件</a>
<a href="mailto:someone@example.com?subject=咨询&body=你好">带主题和正文</a>
<a href="mailto:a@b.com?cc=c@d.com&bcc=e@f.com">带抄送密送</a>
<!-- 电话:移动端直接拨号 -->
<a href="tel:+8613800138000">拨打 13800138000</a>
<!-- 短信:iOS/Android 支持 -->
<a href="sms:13800138000">发短信</a>
<a href="sms:13800138000?body=内容">带内容(Android)</a>
步骤 6:锚点被 fixed header 遮挡
常见问题:页内锚点跳转后,目标标题被顶部固定导航栏遮住。
<style>
/* 方案 1:CSS scroll-padding(推荐,无需改 HTML) */
html {
scroll-padding-top: 80px; /* header 高度 + 间距 */
}
/* 方案 2:给锚点元素加 padding + margin 负值(兼容老浏览器) */
section {
padding-top: 80px;
margin-top: -80px;
}
</style>
讲清楚要点
- href 是 hypertext reference 的缩写:它定义「去哪」,没有 href 的 a 标签不是链接,只是可点击文本(无焦点、无键盘可达)。
- target="_blank" 必须配 rel="noopener":新窗口通过
window.opener能控制原页面,恶意站可篡改原页面地址为钓鱼站。这是真实的安全漏洞。 - 相对路径 vs 绝对路径:
./page.html(同目录)、../page.html(上级)、/page.html(根目录)。用错导致 404,部署到子目录时尤其要小心。 - 锚点本质是 id 定位:URL 的
#fragment部分不发送到服务器,纯客户端行为。改锚点不会刷新页面,但会加历史记录。 - mailto/tel 不是万能的:桌面浏览器没配邮件客户端时 mailto 会失败;tel 在桌面通常打开 Skype/微信,体验不一致。关键操作仍应提供表单。
你可能会懵的地方
-
a 标签里能套 div 吗? HTML5 规范允许 a 包含块级内容(透明内容模型),但建议只包行内内容或一个块级容器。套多层嵌套结构可能导致某些浏览器解析异常。
-
href 和 onclick 同时存在会执行哪个? 都会执行。onclick 先触发,如果返回 false 或调用
event.preventDefault(),href 不跳转。但最佳实践是:行为用 button,导航用 a。 -
target="_blank" 和 window.open() 什么区别?
_blank是声明式,浏览器可拦截弹窗;window.open()是脚本式,更容易被弹窗拦截器拦截。用户体验上 _blank 更可控。 -
锚点 URL 带 # 参数会发给后端吗? 不会。
#后面的部分叫 fragment,浏览器不会将其包含在 HTTP 请求中。Spring Boot 的@RequestParam拿不到 fragment。 -
相对路径
./和不加前缀有区别吗?href="page.html"和href="./page.html"等价,都是相对当前目录。但href="/page.html"是相对于域名根目录,部署到子路径/app/时会跳到错误位置。
错法对照
| 错法 | 问题 | 正解 |
|---|---|---|
<a href="javascript:void(0)"> | 污染协议、CSP 拦截、无键盘焦点 | 用 <button type="button"> 或纯 a + preventDefault |
target="_blank" 无 rel | window.opener 安全风险 | 始终加 rel="noopener noreferrer" |
用 # 做占位链接 | 点击回顶部、加历史记录 | 用 button 或 href="#!" |
| 锚点跳转被 fixed header 挡住 | 用户体验差 | CSS scroll-padding-top |
href="page.html#section" 在 page.html 不存在 | 无反馈的 404 或空白 | 确保目标页面存在且有对应 id |
| 下载链接跨域 | download 属性被忽略 | 同源文件,或后端加 Content-Disposition |
卡点急救
- 锚点跳转后内容被 header 遮住:给 html 加
scroll-padding-top: 80px;(值为 header 高度)。 - target="_blank" 新窗口地址栏显示 about:blank#blocked:被浏览器弹窗拦截。改用 a 标签直接点击,或 window.open 放在用户点击事件里。
- mailto 点击没反应:用户系统没配默认邮件客户端。关键场景提供「复制邮箱地址」按钮作为降级方案。
- 相对路径在本地和服务器表现不一致:本地文件
file:///C:/...和 HTTP 路径解析不同。用本地服务器(npx serve或python -m http.server)测试,别直接双击打开。 - 中文锚点乱码:URL 编码。
href="#第一章"实际发送时是%E7%AC%AC%E4%B8%80%E7%AB%A0,确保目标 id 也是中文且编码一致。
面试常问
-
target="_blank" 有什么安全隐患?怎么解决? 新页面可通过
window.opener.location将原页面重定向到钓鱼站,或读取原页面数据(如果跨域则受限)。解决方案:始终配合rel="noopener noreferrer"。现代浏览器(Chrome 88+)已默认隐式启用 noopener,但显式声明是最佳实践。 -
href="#..." 和 href="javascript:..." 的区别?
#是 fragment 导航,安全、标准;javascript:是伪协议,执行 JS 代码,现代 CSP 默认禁止,且破坏「链接是声明式导航」的语义,属于反模式。 -
相对路径和绝对路径的选择? 内部导航优先相对路径(便于迁移和本地预览);引用外部资源用绝对路径;部署到子目录(如
example.com/app/)时避免以/开头的根路径,否则跳转丢失子目录前缀。 -
download 属性的限制? 仅对同源资源生效;跨域时浏览器忽略 download,直接导航或预览。如需强制下载跨域文件,后端需返回
Content-Disposition: attachment响应头。
自测三问
- 写出「在新标签页打开 GitHub 且安全」的完整 a 标签。
- 为什么
href="#"不推荐作为「点击触发 JS」的占位符? - 页内锚点跳转后被 fixed header 遮住,CSS 怎么解决?
小结一句话
链接是 Web 的经络:href 决定去哪,target 决定怎么开,rel 决定安不安全,锚点决定页内导航——细节处理不好,用户和安全都会付出代价。
延伸
- MDN:创建超链接
- 下一课:嵌入媒体——img、srcset、picture、figure、audio、video 的正确用法。