先记住这个答案
区别在于:addEventListener 回调的返回值会被忽略,return false 不产生任何效果;而 preventDefault() 是 Event 对象的方法,能显式取消可取消事件的默认行为。传统的事件处理器(如 onclick='return false')中,return false 等效于调用 preventDefault(),但不会停止传播,且 addEventListener 不遵循此规则。因此,使用 addEventListener 时,应调用 event.preventDefault()。
- return false 对 addEventListener 无效
- preventDefault 才可取消默认行为
- 仅在传统处理器中 return false 有特殊含义
事件处理器返回值的历史与规范
在早期 DOM0 事件模型中,通过元素属性(如 onclick)注册的处理器,其返回值会被特殊处理。若处理器返回 false,浏览器会执行 preventDefault(),但不会停止事件传播。这是源自旧式事件模型的一种便捷写法。
addEventListener 是 DOM2 引入的标准事件注册方式,规范明确规定回调函数的返回值被忽略。因此,无论返回 true 还是 false,都不会对事件流程产生任何影响。要阻止默认行为,必须调用传入的事件对象上的 preventDefault();要停止传播,则需调用 stopPropagation() 或 stopImmediatePropagation()。
一个链接点击拦截的工程场景
假设在单页应用中,需要拦截所有 <a> 标签的默认跳转,改用路由逻辑处理。代码使用 document.querySelectorAll('a').forEach(a => a.addEventListener('click', handler))。若新手在 handler 中写 return false,点击链接时页面仍会跳转,因为该回调的返回值无效,导致导航发生,应用状态丢失。
正确实现是调用 event.preventDefault(),然后执行路由切换。同时需注意,click 事件是 cancelable 的,但有些事件如 scroll 默认不可取消,需在监听器中检查 event.cancelable 再进行调用,否则对不可取消事件调用无效且可能触发警告。
<a href="https://example.com" id="link">链接</a>
<script>
const link = document.getElementById('link');
// 错误:return false 无效
link.addEventListener('click', function(event) {
return false; // 不会阻止跳转
});
// 正确:调用 preventDefault
link.addEventListener('click', function(event) {
event.preventDefault();
console.log('跳转被阻止');
});
</script>在 HTML 页面中运行,第一个监听器无法阻止跳转,第二个监听器会输出日志并阻止。
失败边界:何时 preventDefault 也无效
preventDefault() 并非万能。它只对 cancelable 属性为 true 的事件有效。例如,通过 dispatchEvent 派发自定义事件时,若未在构造时设置 cancelable: true,则调用 preventDefault() 不会产生任何效果,且该调用不会报错。此外,对于被动监听器(passive: true),浏览器会忽略 preventDefault() 并输出警告。
另一个边界是,return false 在传统处理器中的行为仍可能被一些库或代码风格误用。当代码同时使用 addEventListener 和 HTML 属性时,需明确前者无特殊返回值。若确实需要类似 DOM0 的便捷,可考虑封装一个工具函数来统一处理,但这反而增加了复杂度,不如直接调用 preventDefault()。
容易答错的地方
- 认为 return false 直接等同 preventDefault
- 这是混淆了传统 DOM0 模型与 addEventListener。实际在 addEventListener 中 return false 没有任何效果,必须显式调用 event.preventDefault()。仅当使用 onclick='return false' 时返回值才被解释。
- 忽略事件的 cancelable 属性
- 有些开发者对所有事件都调用 preventDefault(),但非可取消事件(如 scroll 事件或未设置 cancelable 的自定义事件)调用无效。建议先用 if (event.cancelable) 判断,再调用 preventDefault() 以避免无用操作。
面试官还会怎么问?
preventDefault() 会阻止事件传播吗?
不会。preventDefault() 只取消默认行为,事件继续在冒泡或捕获阶段传播。若需停止传播,应使用 stopPropagation()。两者作用独立,可分别调用。
在 passive 监听器中调用 preventDefault 会怎样?
会被静默忽略,并可能在控制台产生警告。设计 passive 是为提升滚动等性能,浏览器承诺不等待 preventDefault,因此该调用无效。必要时应改用非 passive 监听器并承担性能代价。
如何检测一个事件是否可以调用 preventDefault?
检查事件对象的 cancelable 属性,若为 true 则允许取消默认行为。例如:if (event.cancelable) { event.preventDefault(); }。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。