href="javascript:;" 与 href="#"、void(0) 有什么区别

作者:小圆点时间:2026-10-30 17:58:59浏览:502

详细对比 href="javascript:;"、href="#" 和 href="javascript:void(0)" 三种阻止链接默认跳转的写法,分析各自的行为差异、适用场景与潜在问题,帮助开发者做出合理选择。

在前端开发中,当 <a> 标签不需要真正跳转时,最常见的三种写法是 href="#"、href="javascript:void(0)" 和 href="javascript:;"。它们都能阻止页面导航,但在滚动行为、历史记录、返回值处理和语义表达上各有不同。本文逐一分析这三种写法的运行机制,再给出实际场景下的选择依据。

三种写法的基本行为

先看一段最简单的对比代码:

<a href="#">使用锚点</a>
<a href="javascript:void(0)">使用 void(0)</a>
<a href="javascript:;">使用 javascript:;</a>

点击这三个链接,页面都不会跳到新地址,但浏览器内部的处理过程完全不同。

href="#" 指向当前页面的一个空锚点。浏览器会把它当成常规的锚点导航:地址栏末尾加上 #,同时向浏览历史(history)中推入一条新记录。如果页面当前不在顶部,浏览器还会把视口滚动到页面最顶端。这是它最容易引起意外的地方——用户在长页面的某个位置点击后,页面突然跳回顶部,体验很差。

href="javascript:void(0)" 使用了 javascript: 伪协议。浏览器会把 void(0) 当作 JavaScript 表达式执行。void 运算符对任何表达式都返回 undefined,而当 javascript: 协议返回 undefined 时,浏览器不会用返回值替换当前文档。于是页面既不滚动,也不产生新的历史记录。

href="javascript:;" 也基于 javascript: 伪协议。分号是一条空语句,执行结果同样是 undefined。在浏览器看来,它和 void(0) 的效果完全一致:不滚动、不改变 URL、不写入历史。两者的区别仅仅是表达方式——一个显式用 void 返回 undefined,另一个用空语句隐式得到 undefined。

href="javascript:;" 与 href="#"、void(0) 有什么区别
图1:三种 href 写法在浏览器中的处理流程与结果差异

href="#" 的副作用

href="#" 看起来最简短,但引入的副作用最多。以下三个问题在实际项目中尤其常见:

1. 页面滚动 空锚点等同于指向文档开头。在内容较长的页面上,用户点击一个按钮式的链接后被强制拉到顶部,不得不重新找到刚才阅读的位置。虽然可以在 JavaScript 中用 e.preventDefault() 阻止,但如果遗漏了这一行,问题就会暴露。

2. 浏览历史污染 每次点击都会向 history 里推一条记录。用户按"后退"键,期望回到上一个页面,实际却在反复回退同一页面的 # 状态。对单页应用(SPA)来说,这还可能干扰路由逻辑。

3. hashchange 事件触发 如果当前 URL 末尾没有 #,点击后 URL 变化会触发 hashchange 事件。使用了 hash 路由的项目可能因此产生意料之外的路由匹配和组件渲染。

要消除这些副作用,需要在事件处理函数的第一行就调用 e.preventDefault():

document.querySelector('a.btn').addEventListener('click', function(e) {
    e.preventDefault();
    // 实际逻辑
});

这能解决问题,但也意味着用 href="#" 时必须确保对应的 JavaScript 永远正确加载。如果脚本因任何原因没加载,点击链接依然会跳到顶部。

javascript:void(0) 与 javascript:; 的机制

这两种写法都利用 javascript: 伪协议,让浏览器把冒号后面的内容当作 JavaScript 表达式执行。关键在于执行结果:

  • void(0) 显式返回 undefined。void 是一元运算符,对括号内的表达式求值后丢弃结果,返回 undefined。
  • ;(空语句)执行结果也是 undefined,因为没有任何有值的表达式。

当 javascript: 协议的表达式返回 undefined 以外的值时,浏览器会尝试把返回值作为新文档内容渲染。例如 href="javascript:'hello'" 会让页面变成只显示 hello 的纯文本。而返回 undefined 则不触发任何页面替换。

这就是 void(0) 存在的意义:在早期浏览器中,为了保证绝对安全,开发者通过 void 显式声明"这个表达式的结果是 undefined,不要做任何事"。javascript:; 能达到同样效果,只是写法更短,意图不如 void(0) 那么一目了然。

href="javascript:;" 与 href="#"、void(0) 有什么区别
图2:javascript: 伪协议的执行路径——返回值决定页面是否被替换

三种写法的详细对比

对比维度href="#"href="javascript:void(0)"href="javascript:;"
页面滚动会滚到顶部(无 preventDefault 时)不滚动不滚动
URL 变化末尾追加 #不变不变
浏览历史推入新记录不影响不影响
hashchange 事件可能触发不触发不触发
JS 未加载时的表现跳到顶部无反应无反应
CSP 安全策略兼容兼容可能被 unsafe-inline 策略阻止可能被 unsafe-inline 策略阻止
可读性简短直观意图明确较隐晦
无障碍(a11y)三者均不理想,详见下文同左同左

CSP 策略对 javascript: 协议的影响

内容安全策略(Content Security Policy)允许网站限制页面中可以执行的 JavaScript 来源。当 CSP 头中没有包含 'unsafe-inline' 时,所有内联 JavaScript(包括 javascript: 伪协议)都会被浏览器拦截。

这意味着在严格的 CSP 环境下,href="javascript:void(0)" 和 href="javascript:;" 都会导致控制台报错,链接点击后没有任何反馈。而 href="#" 因为不涉及脚本执行,不受 CSP 限制。

如果你的项目已经或计划启用严格 CSP,那么 javascript: 协议的两种写法都不是好选择。

无障碍和语义化角度的考量

从 HTML 语义来看,<a> 标签的作用是导航。如果一个元素点击后不导航到任何页面,而是触发某个操作(打开弹窗、切换标签页、提交数据),它的语义更接近按钮,应该使用 <button>。

屏幕阅读器会把 <a> 播报为"链接",用户期望点击后会到达某个位置。如果点击后什么都没发生,或者弹出了一个对话框,用户会感到困惑。使用 <button> 则会被播报为"按钮",符合预期。

当然,实际项目中完全不用 <a> 做交互按钮并不现实,尤其是在已有大量遗留代码的项目中。在不方便重构的情况下,至少应当添加 role="button" 属性,并且确保键盘的 Enter 和 Space 键都能正常触发操作。

href="javascript:;" 与 href="#"、void(0) 有什么区别
图3:根据实际场景选择合适的链接处理方式

实际项目中的选择依据

选择哪种写法不应该只看"哪个更短"或"哪个更流行",而要看项目的具体约束条件:

如果可以修改 HTML 结构,应该把不导航的 <a> 改为 <button>。按钮不需要 href,天然不存在滚动、历史污染和 CSP 冲突的问题。样式可以通过 CSS 重置为链接的外观:

button.link-style {
    background: none;
    border: none;
    color: #1a73e8;
    cursor: pointer;
    padding: 0;
    font: inherit;
    text-decoration: underline;
}

如果项目启用了严格 CSP,排除 javascript: 开头的两种写法。使用 href="#" 并在 JavaScript 中调用 e.preventDefault(),或者改用 <button>。

如果维护遗留代码,短期内无法改结构,href="javascript:void(0)" 比 href="#" 更安全,因为即使 JavaScript 没有加载,它也不会产生滚动和历史副作用。javascript:; 功能上完全等价,选择取决于团队的编码规范——有的团队认为 void(0) 更可读,有的认为空语句更简洁。

如果链接有降级跳转需求,例如 JavaScript 不可用时希望用户能跳到一个真实地址,那么 href 应该填真实 URL,再通过 JavaScript 拦截默认行为。这种情况下三种占位写法都不适用。

一个容易踩的坑:javascript: 中的返回值

有时开发者会在 javascript: 中写比较复杂的表达式,比如:

<!-- 危险写法 -->
<a href="javascript:document.title">显示标题</a>

document.title 返回的是字符串,不是 undefined,浏览器会把当前页面替换为一个只显示标题文本的空白页。这种行为在现代浏览器中依然存在。

同样的问题也会出现在赋值语句中。x = 5 的返回值是 5,如果写成 href="javascript:x=5",页面就会变成只显示数字 5 的文档。要避免这个问题,要么用 void() 包裹整个表达式,要么干脆不在 href 里执行任何逻辑——把逻辑写在事件监听器中才是正确做法。

总结

三种写法的核心差异可以归结为一句话:href="#" 走的是锚点导航逻辑,有滚动和历史副作用;javascript:void(0) 和 javascript:; 走的是脚本执行逻辑,没有副作用,但受 CSP 策略限制。

在理想情况下,不导航的交互元素应当使用 <button> 而非 <a>。当必须使用 <a> 时,javascript:void(0) 在不考虑 CSP 的场景下副作用最小、意图最明确,是三者中较稳妥的选择。如果项目有严格 CSP 要求,则应使用 href="#" 配合 e.preventDefault()。不论选择哪种方式,始终把业务逻辑写在独立的事件处理函数中,不要嵌入 href 属性里。

href="javascript:;" 与 href="#"、void(0) 有什么区别
图4:三种写法在滚动、历史记录、CSP 兼容性等维度的总结对比
作者最新文章
相关文章
更多