Angular自定义autoFocus指令实现blur后返回首个可聚焦元素问题求解
Angular 焦点循环控制指令修复方案
原有代码问题排查
- 拼写错误:事件绑定方法名写错,
addEventListner正确拼写为addEventListener - 多余的属性调用:
querySelectorAll返回的是原生DOM节点集合,不需要额外访问.nativeElement属性,该属性仅Angular封装的ElementRef实例才有 - 拼写不规范:延迟时间变量
dealy应为delay - 缺少边界判断:未判断可聚焦元素列表长度,为空时直接访问索引会抛异常
- 未实现需求逻辑:缺少enter事件监听、触发enter后解绑自动切焦逻辑
- 无事件解绑逻辑:存在内存泄漏风险
修正后完整代码
import { AfterContentInit, Directive, ElementRef, Input, OnDestroy } from '@angular/core'; @Directive({ selector: '[autoFocus]' }) export class AutofocusDirective implements AfterContentInit, OnDestroy { @Input() public autoFocus: boolean; private readonly selector = `[focus="true"]`; private readonly delay = 100; // 存储事件回调引用,用于解绑 private blurHandler: (() => void) | null = null; private enterHandler: ((e: KeyboardEvent) => void) | null = null; public constructor(private readonly el: ElementRef) {} public ngAfterContentInit() { setTimeout(() => { const focusElements = Array.from(this.el.nativeElement.querySelectorAll(this.selector)) as HTMLElement[]; // 边界判断:没有可聚焦元素直接返回 if (focusElements.length < 1) return; const firstEl = focusElements[0]; const lastEl = focusElements[focusElements.length - 1]; // 初始聚焦第一个元素 firstEl.focus(); // 绑定最后一个元素的blur事件 this.blurHandler = () => { firstEl.focus(); }; lastEl.addEventListener('blur', this.blurHandler); // 绑定所有可聚焦元素的enter事件,触发后停止自动切焦 this.enterHandler = (e: KeyboardEvent) => { if (e.key === 'Enter' && this.blurHandler) { lastEl.removeEventListener('blur', this.blurHandler); this.blurHandler = null; // 可选:enter事件触发后也移除enter监听本身 focusElements.forEach(el => el.removeEventListener('keydown', this.enterHandler!)); this.enterHandler = null; } }; focusElements.forEach(el => el.addEventListener('keydown', this.enterHandler)); }, this.delay); } // 组件销毁时解绑所有事件,避免内存泄漏 ngOnDestroy(): void { const focusElements = Array.from(this.el.nativeElement.querySelectorAll(this.selector)) as HTMLElement[]; if (focusElements.length < 1) return; const lastEl = focusElements[focusElements.length - 1]; if (this.blurHandler) { lastEl.removeEventListener('blur', this.blurHandler); } if (this.enterHandler) { focusElements.forEach(el => el.removeEventListener('keydown', this.enterHandler)); } } }
逻辑说明
- 初始化时先判断可聚焦元素列表长度,避免空指针异常
- 存储事件回调的引用,保证后续可以正常解绑
- 所有可聚焦元素监听键盘事件,按下Enter键后立即移除最后一个元素的blur监听,停止自动切回第一个焦点的逻辑
- 实现
OnDestroy生命周期,指令销毁时自动解绑所有绑定的事件,避免内存泄漏
内容的提问来源于stack exchange,提问作者user2024080
相关产品推荐
相关产品推荐

