现代浏览器事件:监听器生命周期、冒泡与安全表格
本文目录
事件监听器不只是“点击后执行函数”:还要考虑传播阶段、默认动作、重复注册和清理。现代浏览器使用 addEventListener/removeEventListener;attachEvent 属于已淘汰的旧 IE API,不应继续加入面向当前浏览器的兼容分支。
注册、移除与生命周期
removeEventListener 需要匹配事件名、同一个函数引用和 capture 配置。匿名函数无法通过重新写一个相同函数体移除。once 可让监听器触发一次后自动清理;passive 表示监听器不会调用 preventDefault,适合滚动等场景;signal 可以统一终止一组监听。
const controller = new AbortController();
const button = document.querySelector('[data-save]');
function handleSave() {
console.log('保存');
}
button?.addEventListener('click', handleSave, { signal: controller.signal });
window.addEventListener('resize', handleResize, { signal: controller.signal });
// 组件销毁或页面流程结束时
controller.abort();
同一回调在不同 capture 配置下是不同的注册关系。若没有 AbortController,也要保留函数引用并在卸载时 removeEventListener。不要在每次 render 中反复注册而不清理。
target、currentTarget 与事件传播
事件通常经过捕获、目标和冒泡阶段。event.target 是最初触发的节点,event.currentTarget 是当前正在执行监听器的节点。委托监听器放在父容器时,两者通常不同。stopPropagation 会阻止继续传播;preventDefault 取消可取消事件的默认行为,例如链接跳转,但不等于阻止冒泡。
container.addEventListener('click', (event) => {
if (!(event.target instanceof Element)) return;
const link = event.target.closest('a[data-external]');
if (!link || !container.contains(link)) return;
if (!link.href.startsWith('https://')) {
event.preventDefault();
}
});
链接安全校验应使用 URL 解析器而不是字符串前缀判断;示例中的前缀仅用于说明事件 API。检查协议、用户名、主机和允许列表后再决定跳转。外链应正确说明新窗口行为,并按需设置 rel=noopener noreferrer。
创建表格不要拼 HTML
按钮添加网站列表时,先构造语义化表格,再用 textContent 放标题,用经过校验的 URL 设置链接属性。用户可控的名称不能进入 innerHTML;外部 URL 不能任意使用 javascript:、data: 或协议相对地址。
function makeSafeLink(label, rawUrl) {
const url = new URL(rawUrl);
if (url.protocol !== 'https:' || !['example.com', 'www.example.com'].includes(url.hostname)) {
throw new TypeError('链接不在允许范围内');
}
const link = document.createElement('a');
link.href = url.href;
link.textContent = label;
link.rel = 'noopener noreferrer';
return link;
}
允许主机应由业务配置决定,不能把示例域名当成实际白名单。即使前端过滤,服务端也要校验保存和跳转目标;开放重定向、DOM XSS 和 SSRF 是不同层次的问题,不可只做一次前缀检查就认为安全。
交互与测试
为按钮选择 button,为导航选择 a;键盘操作和焦点状态应天然可用。分别测试捕获/冒泡、动态子项、重复点击、组件卸载、键盘触发、非法 URL 与默认行为。仅在确实需要改变默认动作时调用 preventDefault,避免破坏复制、打开新标签等浏览器能力。
参考:MDN EventTarget.addEventListener、MDN removeEventListener、MDN Event bubbling、MDN Event.preventDefault、MDN URL API、OWASP DOM XSS Prevention。
Live Notes
留言提交后会先进入审核,审核通过后对其他读者公开。