Angular:我想通过箭头键将 div 的焦点从一个元素移动到另一个元素

7
我正在进行Angular项目,遇到这样一种情况:我有一系列并排的div,当用户点击其中任何一个div时,应该能够使用箭头键移动到另一个div。
请帮忙。
我尝试使用keypress事件,但它没有帮助我解决问题。我试着在stackoverflow上找到类似的问题,但所有的答案都涉及JQuery,而我需要用TypeScript实现。

moveCell(e){
  console.log(e);
}
.container{
width: 100%;
}

.cell{
width: 100px;
float:left;
}

.cell:hover,
.cell:focus{
background: red;
}
<div class="container">
<div class="cell" (keypress)="moveCell($event)">cell 1</div>
  <div class="cell" (keypress)="moveCell($event)">cell 2</div>
  <div class="cell" (keypress)="moveCell($event)">cell 3</div>
  <div class="cell" (keypress)="moveCell($event)">cell 4</div>
  <div class="cell" (keypress)="moveCell($event)">cell 5</div>
  <div class="cell" (keypress)="moveCell($event)">cell 6</div>
  <div class="cell" (keypress)="moveCell($event)">cell 7</div>
  <div class="cell" (keypress)="moveCell($event)">cell 8</div>
  <div class="cell" (keypress)="moveCell($event)">cell 9</div>
</div>

在我的代码中,如果用户点击单元格2,则焦点应自动转到单元格2。之后,如果用户使用键盘并按箭头键,它应将焦点移动到下一个/上一个单元格。

2个回答

5

keypress不能检测到箭头键,请改用keydown。为了接收焦点并监听按键事件,请在 div 元素中添加属性 tabindex

对于右箭头键,您需要检查当前活动元素是否不是最后一个元素,并将焦点更改为下一个元素。

对于左箭头键,请先检查当前活动元素是否不是第一个元素,然后将焦点更改为上一个元素。

--HTML--

<div class="container">
  <div tabindex="0" class="cell" (keydown)="moveCell($event)">cell 2</div>
  <div tabindex="1" class="cell" (keydown)="moveCell($event)">cell 3</div>
  <div tabindex="2" class="cell" (keydown)="moveCell($event)">cell 4</div>
  <div tabindex="3" class="cell" (keydown)="moveCell($event)">cell 5</div>
  <div tabindex="4" class="cell" (keydown)="moveCell($event)">cell 6</div>
  <div tabindex="5" class="cell" (keydown)="moveCell($event)">cell 7</div>
  <div tabindex="6" class="cell" (keydown)="moveCell($event)">cell 8</div>
</div>

--组件代码--
  length: 0;
  domEles;
  moveCell(e){
    const activeEle = document.activeElement;
    const activeEleIndex = Array.prototype.indexOf.call(this.domEles, activeEle);
    if(e.key == "ArrowRight" && activeEleIndex < this.length - 1 ) {
        activeEle.nextElementSibling.focus();
    } 

    if(e.key == "ArrowLeft" && activeEleIndex > 0) {
       activeEle.previousElementSibling.focus();
    }
  }

  ngOnInit() {
    this.domEles = document.querySelectorAll('.container > *');
    this.length = this.domEles.length;
  }

工作代码 - https://stackblitz.com/edit/angular-5qxicw


属性'focus'在类型'Element'上不存在。 - Kenil Barvaliya

5
还有一种使用指令的方法。嗯,思路是我们在app.component中使用ViewChildren获取所有具有我们指令的div,然后我们带有指令的div发送一个事件并调用app.component的一个函数。因此,app.component变得如下所示:
<div arrow-div (event)="handler($event)>my div</div>
<div arrow-div (event)="handler($event)>my div</div>
...

但是我们可以使用一个“服务”使事情更加“透明”。

想象一种类似的服务

@Injectable({
  providedIn: 'root',
})
export class KeyBoardService {
  keyBoard:Subject<any>=new Subject<any>();
  sendMessage(message:any)
  {
    this.keyBoard.next(message)
  }
}

我们的指令可以在按下箭头键时调用服务"sendMessage",在我们的app.component中订阅这个服务。然后我们的app.component类似于以下内容
<div arrow-div >my div</div>
<div arrow-div >my div</div>
<br/>
<div arrow-div >my div</div>
<div arrow-div >my div</div>

我们避免在我们的div中使用这个“丑陋”(event)="handler($event)"!

嗯,这个指令很简单,使用@Hostlistener来监听键盘事件,并且使用renderer2来添加属性"tabindex"(为了使一个div可以被聚焦,我们需要添加tabIndex)。所以

@Directive({
  selector: '[arrow-div]',
})
export class ArrowDivDirective {
  constructor(private keyboardService: KeyBoardService, public element: ElementRef, private render: Renderer2) {
    this.render.setAttribute(this.element.nativeElement, "tabindex", "0")
  }


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

    switch (e.keyCode) {
      case 38:
        this.keyboardService.sendMessage({ element: this.element, action: 'UP' })
        break;
      case 37:
        this.keyboardService.sendMessage({ element: this.element, action: 'LEFT' })
        break;
      case 40:
        this.keyboardService.sendMessage({ element: this.element, action: 'DOWN' })
        break;
      case 39:
        this.keyboardService.sendMessage({ element: this.element, action: 'RIGHT' })
        break;
    }
  }
}

我们的 app.component.ts 文件。
export class AppComponent implements OnInit {
  columns:number=2;
  @ViewChildren(ArrowDivDirective) inputs:QueryList<ArrowDivDirective>

  constructor(private keyboardService:KeyBoardService){}
  ngOnInit()
  {
    this.keyboardService.keyBoard.subscribe(res=>{
      this.move(res)
    })
  }
  move(object)
  {
    const inputToArray=this.inputs.toArray()
    let index=inputToArray.findIndex(x=>x.element==object.element);
    switch (object.action)
    {
      case "UP":
        index-=this.columns;
        break;
      case "DOWN":
        index+=this.columns;
        break;
      case "LEFT":
        index--;
        break;
      case "RIGHT":
        index++;
        break;
      case "RIGHT":
        index++;
        break;
    }

    if (index>=0 && index<this.inputs.length)
      inputToArray[index].element.nativeElement.focus();
  }
}

看到我使用了一个变量"column",如果我们正在制作一个带有列和行的"网格",并使用上下箭头在行之间移动。发送"element"可以避免我们必须存储"div focused"。
你可以在stackblitz中看到一个例子。

你让我的一天变得美好!这是非常聪明的解决方案!最适合Angular项目。谢谢你分享,Eliseo :) - sandip

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接