Angular: I want to move focus of div from to another on arrow key buttons

北城余情 提交于 2021-02-20 09:06:00

问题


I am working on angular project and came to a situation where i have series of div in a row and when user clicks on any div he should be able to move from one div to another using arrow key.

Please help.

I tried using keypress event but it didn't helped me. Tried finding out similar questions on stackoverflow but all answers where into jquery and i need it in in 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>

In my code If user clicks on cell 2, focus should automatically go to cell 2. After that If user use keyboard and press arrow keys it should move focus to next/prev cell.


回答1:


keypress does not detect the arrow keys, instead use keydown. In order to receive the focus and listen to the key events add attribute tabindex to the divs.

For right arrow key, you need to check if current active element is not a last element, and change the focus to the next element.

For left arrow key, check whether current active element is not a first element and then change the focus to the previous element.

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

--Component code--

  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:


There're another aproach using directive. Well, the idea is that we get all the div that has our directive in our app.component using ViewChildren, then our div with a directive send an event and call a function of our app.component. So the app.component becomes like

<div arrow-div (event)="handler($event)>my div</div>
<div arrow-div (event)="handler($event)>my div</div>
...

But we can use a "service" to make the things more "transparents".

Imagine a service like

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

Our directive can call to the service "sendMessage" when a key arrow is pressed, and in our app.component subscribe to this service. and then our app.component is some like

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

We avoid this "ugly" (event)="handler($event)" in our divs!!

Well, the directive in simple, using @Hostlistener to listen the key, and renderer2 to add the attributte "tabindex" (to make a div focusable, we need add tabIndex). So

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

And our 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();
  }
}

See that I used a variable "column" if we are making a "grid" with cols and rows and use the up and down keys to move between rows. Sending the "element" avoid we must have stored the "div focused"

You can see an example in stackblitz



来源:https://stackoverflow.com/questions/56608536/angular-i-want-to-move-focus-of-div-from-to-another-on-arrow-key-buttons

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!