【问题标题】:Angular: I want to move focus of div from to another on arrow key buttonsAngular:我想将 div 的焦点从箭头键按钮移到另一个
【发布时间】:2019-06-16 23:01:07
【问题描述】:

我正在做 angular 项目,遇到了这样一种情况,我连续有一系列 div,当用户单击任何 div 时,他应该能够使用箭头键从一个 div 移动到另一个 div。

请帮忙。

我尝试使用按键事件,但它没有帮助我。尝试在 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。之后如果用户使用键盘并按箭头键,它应该将焦点移动到下一个/上一个单元格。

【问题讨论】:

    标签: javascript angular typescript


    【解决方案1】:

    keypress 没有检测到arrow keys,而是使用keydown。为了接收焦点并监听关键事件,将属性tabindex 添加到 div。

    对于右箭头键,你需要检查当前活动元素是否不是最后一个元素,并将焦点转移到下一个元素。

    对于左箭头键,检查当前活动元素是否不是第一个元素,然后将焦点转移到前一个元素。

    --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;
      }
    

    Working Code - https://stackblitz.com/edit/angular-5qxicw.

    【讨论】:

    • “元素”类型上不存在属性“焦点”。
    【解决方案2】:

    还有另一种使用指令的方法。好吧,我们的想法是,我们使用 ViewChildren 在 app.component 中获取所有包含指令的 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 监听key,使用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: 'RIGTH' })
            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 "RIGTH":
            index++;
            break;
          case "RIGTH":
            index++;
            break;
        }
    
        if (index>=0 && index<this.inputs.length)
          inputToArray[index].element.nativeElement.focus();
      }
    }
    

    如果我们使用列和行创建“网格”并使用向上和向下键在行之间移动,请注意我使用了变量“列”。发送“元素”避免我们必须存储“div 聚焦”

    你可以在stackblitz看到一个例子

    【讨论】:

    • 你成就了我的一天!这是非常聪明的解决方案!最适合 Angular 项目。感谢您分享 Eliseo :)
    猜你喜欢
    • 2021-10-12
    • 2018-05-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多