如何在 keyCode 导航的输入中设置自动焦点?

问题描述

我写了一个表格,其中都包含输入字段。对于这个模板,我开发了一个 keyCode 导航。可以向上、向下、向左和向右导航。我必须在代码添加什么才能使光标在导航时直接聚焦?

我的代码

// Snippet from HTML
...
<tbody formArrayName="rows">
  <tr *ngFor="let rowControl of rows.controls; let rowIndex = index">
   <td [formGroupName]="rowIndex" appArrowKeyNav *ngFor="let column of displayedColumns; let columnIndex = index;">
   <div>
        <span>
               <label>
                   <input [formControl]="rowControl.get(column.attribute)">
                </label>
         </span>
     </div>
   </td>
 </tr>
</tbody>
// TS
public move(object: any) {
    const inputToArray = this.inputs.toArray();
    const cols = this.displayedColumns.length;
    let index = inputToArray.findindex((x) => x.element === object.element);
    // console.log('Index found:',index);
    switch (object.action) {
      case 'UP':
        index -= cols;
        break;
      case 'DOWN':
        index += cols;
        break;
      case 'LEFT':
        index -= 1;
        break;
      case 'RIGHT':
        index += 1;
        break;
    }
    if (index >= 0 && index < this.inputs.length) {
      console.log('Navigating to index:',index);
      inputToArray[index].element.nativeElement.focus();
    }
  }
// Directive
  @HostListener('keydown',['$event']) onKeyUp(event: KeyboardEvent) {
    switch (event.key) {
      case 'ArrowUp':
        this.keyboardService.sendMessage({ element: this.element,action: 'UP' });
        break;
      case 'ArrowLeft':
        this.keyboardService.sendMessage({ element: this.element,action: 'LEFT' });
        break;
      case 'ArrowDown':
        this.keyboardService.sendMessage({ element: this.element,action: 'DOWN' });
        break;
      case 'ArrowRight':
        this.keyboardService.sendMessage({ element: this.element,action: 'RIGHT' });
        break;
      case 'Enter':
        this.keyboardService.sendMessage({ element: this.element,action: 'ENTER' });
        break;
    }
}

这是我的Stackblitz:https://stackblitz.com/edit/angular-wmfjhh-zfkyyx?file=app%2Ftable-basic-example.html

解决方法

拥有可以使用箭头键导航的单元格的表格的一种可能方法

使用 id 属性通过 *ngFor 索引存储行和列信息

    <tr *ngFor="let rowControl of rows.controls; let i = index">

      <ng-container [formGroupName]="i">

        <td>
          <input [id]="'row-' + i + '-col-0'" formControlName="name" (focus)="onFocus($event)">
        </td>
        <td>
          <input [id]="'row-' + i + '-col-1'" formControlName="age" (focus)="onFocus($event)">
        </td>
        <td>
          <input [id]="'row-' + i + '-col-2'" formControlName="color" (focus)="onFocus($event)">
        </td>

      </ng-container>

    </tr>

在这种情况下,第一行的 id 将是 row-0-col-0row-0-col-1 等。 第二行 row-1-col-0

还有一个 onFocus 事件处理程序,它将设置当前单元格为焦点

一旦keyUp被触发

 @HostListener('keydown',['$event'])
  onKeyUp(event: KeyboardEvent) {

    console.log(event.key);

    if (this.currentInputInFocus) {
      const id = this.currentInputInFocus.id;

      if (id && id.includes('row-')) {
        const arr = id.split('-');
        let row: number = Number(arr[1]);
        let col: number = Number(arr[3]);

        switch (event.key) {
          case 'ArrowUp':
            --row;
            break;

          case 'ArrowLeft':
            --col;
            break;

          case 'ArrowDown':
            ++row;
            break;

          case 'ArrowRight':
            ++col;
            break;

          case 'Enter':
            // do nothing
            break;
        }
        this.setFocus(row,col);
      }
    }
  }

获取当前聚焦的元素 id,例如。 'row-0-col-0' 并根据按键更改行或列值,然后尝试聚焦新元素

  private setFocus(row: number,col: number) {
    const newElementToFocusOn = document.getElementById(`row-${row}-col-${col}`);
    if (newElementToFocusOn) {
      this.currentInputInFocus = newElementToFocusOn;
      this.currentInputInFocus.focus();
    }
  }

ngAfterViewInit 初始聚焦的单元格中可以设置:

  ngAfterViewInit() {
    const row = 1;
    const col = 0;
    this.setFocus(row,col);
  }

Working demo

更新

使用你的 Stackblitz 作为上下文很少有 mods 使它工作:

  • 设置正确的 id 属性 [id]="'row-' + rowIndex + '-col-' + columnIndex"
  • 跟踪当前聚焦的输入:currentInputInFocus!: HTMLElement;
  • rest 与原始演示中的相同

Forked and updated your Stackblitz

第二次更新

HTMLElement.focus() 方法将焦点设置在指定的元素上,如果 可以集中注意力。

Docs

一种解决方法是根据此 answer

添加 tabindex="0" 属性

回到演示。正如@Aviad P. 提到的,ng-template 上的指令将不起作用。相反:

  <ng-template #otherColumns>
    <div tabindex="0" [id]="'row-' + rowIndex + '-col-' + columnIndex" arrow-div>
      Here is a Number
    </div>
  </ng-template>

Demo

第三次更新

要继续导航,您可以在 case 'RIGTH':

上尝试此操作
++col; // try to move to next column

if (col >= this.columns.length) { // check if moved past last column
  col = 0; // go to column at zero index  (1st column)
  ++row; // go to next row
}

Demo

,

您有一个错误(好吧,至少有一个),您已将 arrow-div 附加到您的 <ng-template>,这导致 Comment 类型的节点被添加到您的 {{1} } inputs - 这些节点没有 QueryList 函数,因此您的代码会失败。

这是问题区域:

focus()

除此之外,当您按下键盘箭头时,您的索引会跳入错误的间隙,但是一旦您修复了之前的错误,它就会毫无错误地跳来跳去。