Angular自定义指令多实例仅单实例生效,上下箭头焦点控制异常
问题根因
- 所有
appFocuser指令实例都监听了全局document的keyup事件,每次按下上下箭头时,所有实例的回调函数都会顺序执行,后执行的实例会覆盖前一个实例的焦点切换操作,因此只有最后注册的第二个实例看起来能正常工作 - 没有做触发范围校验:不管当前焦点是否在当前指令对应的容器内,所有实例都会执行焦点切换逻辑,导致不同实例的操作互相冲突
修复方案
核心逻辑增加触发范围校验,只有当当前激活的元素属于当前指令管辖的容器内部时,才响应上下箭头按键事件。如果需要优先保障第一个区域默认获焦,可以增加输入参数控制默认获焦行为,避免多个实例初始化时互相抢夺焦点。
修改后完整指令代码如下:
import { Directive, ElementRef, HostListener, AfterViewInit, Input } from '@angular/core'; @Directive({ selector: '[appFocuser]', }) export class AutoFocusDirective implements AfterViewInit { focusableArray = []; parent = null; count = 0; currentFocus: HTMLElement; // 增加输入参数,控制是否初始化时自动获焦,默认关闭 @Input() autoFocusOnInit = false; constructor(private el: ElementRef) {} @HostListener('document:keyup', ['$event']) onKeyup = ($event: KeyboardEvent) => { // 新增范围校验:当前激活元素不在当前容器内时直接返回,不处理事件 const activeElement = document.activeElement; if (!activeElement || !this.parent.contains(activeElement)) { return; } if ($event.code === 'ArrowDown') { $event.preventDefault(); $event.stopPropagation(); if (this.count >= this.focusableArray.length - 1) { this.count = 0; } else { this.count++; } this.focusableArray[this.count].focus(); return; } if ($event.code === 'ArrowUp') { $event.preventDefault(); $event.stopPropagation(); if (this.count == 0) { this.count = this.focusableArray.length - 1; } else { this.count--; } this.focusableArray[this.count].focus(); return; } }; ngAfterViewInit() { this.parent = this.el.nativeElement; this.focusableArray = Array.from( this.parent.querySelectorAll(`[data-focus=true]`) ) as HTMLElement[]; // 只有配置了autoFocusOnInit为true的实例才会在初始化时自动获焦 if (this.autoFocusOnInit && this.focusableArray.length) { this.currentFocus = this.focusableArray[this.count]; this.currentFocus.focus(); } } }
使用示例
需要优先响应的第一个绿色区域的容器,绑定指令时开启自动获焦即可:
<!-- 第一个容器,开启默认获焦,优先响应 --> <div appFocuser [autoFocusOnInit]="true" class="green-area"> <!-- 内部带data-focus=true的可聚焦元素 --> </div> <!-- 第二个容器,不开启默认获焦 --> <div appFocuser class="other-area"> <!-- 内部带data-focus=true的可聚焦元素 --> </div>
内容的提问来源于stack exchange,提问作者user2024080
相关产品推荐
相关产品推荐

