Controlling tab order via nativeElement.focus() from within an Angular component

2.2k Views Asked by At

I need to cycle through an expanded overlay control. Once the overlay is expanded I want to cycle through the input elements until the overlay has been collapsed. I have the code working to a point, i.e once the overlay is visible my focus is set to the first input element and cycles down to the last 'button' element.

My issue is that the code I have added so I can cycle around the expanded overlay is not working as expected.

This code snippet initially sets focus to the first input element

@ViewChild('focusShortCode', { static: true }) nameField: ElementRef;
public setFocus(isSet: boolean) {
        if (isSet) {
          this.nameField.nativeElement.focus();
        }
      }

This code snippet is supposed to set the focus back to the first input element again

 public onBlur(isSet: boolean) {
    if (isSet) {
      this.nameField.nativeElement.focus();
    }
  }

The issue is when 'onBlur' is invoked my focus jumps the the second input element within my expanded overlay

onblur is setting focus to the 'name' element not the 'shortCode' element

<div class="container">
<div class="layout-box" igxLayout igxLayoutJustify="end">
  <div class="layout-box__el" >
    <igx-input-group type="border" style="width: 200px;">
      <input igxInput #focusShortCode name="shortCode" type="text" [value]="shortCode" />
      <label igxLabel  for="shortCode">{{shortCodePlaceHolder}}</label>
    </igx-input-group>
  </div>
  <div class="layout-box__el">
    <igx-input-group type="border" style="width: 200px;">
      <input igxInput name="name" type="text" [value]="name" />
      <label igxLabel for="name">{{namePlaceHolder}}</label>
    </igx-input-group>
  </div>
</div>
<div class="layout-box" igxLayout igxLayoutJustify="end" style="margin-top: -15px;">
  <div class="layout-box__el">
    <igx-input-group type="border" style="width: 200px;">
      <input igxInput name="street1" type="text" [value]="street1" />
      <label igxLabel for="street1">{{street1PlaceHolder}}</label>
    </igx-input-group>
  </div>
  <div class="layout-box__el">
    <igx-input-group type="border" style="width: 200px;">
    <input igxInput name="street2" type="text" [value]="street2"/>
      <label igxLabel for="street2">{{street2PlaceHolder}}</label>
    </igx-input-group>
  </div>
</div>

<div class="layout-box" igxLayout igxLayoutJustify="end" style="margin-top: -15px;">
  <div class="layout-box__el">
    <igx-input-group type="border" style="width: 200px;">
      <input igxInput name="city" type="text" [value]="city"/>
      <label igxLabel for="city">{{cityPlaceHolder}}</label>
    </igx-input-group>
  </div>
  <div class="layout-box__el">
    <igx-input-group type="border" style="width: 200px;">
      <input igxInput name="country" type="text" [value]="county"/>
      <label igxLabel for="country">{{countyPlaceHolder}}</label>
    </igx-input-group>
  </div>
</div>

<div class="layout-box" igxLayout igxLayoutJustify="end" style="margin-top: -15px;">
  <div class="layout-box__el">
    <igx-input-group type="border" style="width: 200px;">
      <input igxInput name="postCode" type="text" [value]="postCode"/>
      <label igxLabel for="postCode">{{postCodePlaceHolder}}</label>
    </igx-input-group>
  </div>
  <div class="layout-box__el">
    <igx-input-group type="border" style="float: right; width: 100px;" >
      <input igxInput name="country" type="text" [value]="country" />
      <label igxLabel for="country">{{countryCodePlaceHolder}}</label>
    </igx-input-group>
  </div>
  <div>
    <button class="igx-button--raised" #buttonElement igxButton (keydown.Tab)="onBlur(true)">Close</button>
  </div>

</div>
</div>
1

There are 1 best solutions below

0
On

When overwriting the logic applied on key press, button click, etc. you need to ensure that the default behavior is prevented. Since the keydown is cancelable event you could pass the event argument to the handler and cancel it using event.preventDefault(). Before that the event.stopPropagation() method could be called in order to prevent event bubbling:

document.getElementById("buttonElement")
        .addEventListener("keydown", (event) => {
            if (event.key === "Tab") {
                //focus the first element
                this.firstElem.focus();
                //stop the default behaviour
                event.stopPropagation();
                event.preventDefault();
            }
        });

A small sample illustrating my suggestion using igxInputGroup and IgxOverlayService is available here: https://stackblitz.com/edit/cycle-through-elems-in-overlay