You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 13:12:02