13

I want to move focus from an input field to another when a user has entered the maxLength amount of characters into the first input field. So in my example below, when a user has entered 2 characters into the day input, the focus would move to the month input.

This is my code so far:

<input formControlName="day" maxlength="2" placeholder="DD" type="text" (keyup)="keytab($event)" />
<input formControlName="month" maxlength="2" placeholder="MM" type="text" (keyup)="keytab($event)" />
<input formControlName="year" maxlength="4" placeholder="YYYY" type="text" />

And in my TS file:

 keytab(event){
    let nextInput = event.srcElement.nextElementSibling; // get the sibling element

    var target = event.target || event.srcElement;
    var id = target.id
    console.log(id.maxlength); // prints undefined

    if(nextInput == null)  // check the maxLength from here
        return;
    else
        nextInput.focus();   // focus if not null
}

I know the code in my TS file is wrong, but I was trying to find a way of getting the maxLength property and then shifting the focus. Right now the focus will move as soon as there is a keyup in the input field.

Can anyone tell me how I can access the inputs maxLength property from the keytab function? Thanks.

I'm using Angular 4.

Edit - I'm trying to get the maxLength value and then compare to the input value length. If the input value is more, then move focus to the input field.

4
  • 2
    Possible duplicate of How can I set focus to another input? Commented Nov 29, 2017 at 9:41
  • No its not - I said that I have the shifting focus part working - I don't know how to access the maxLength. But thanks for the link. Interesting information there. Commented Nov 29, 2017 at 9:43
  • Just use <input type="date"> Commented Mar 20, 2018 at 19:13
  • why are you using input type text instead of number? Commented Sep 16, 2020 at 10:41

5 Answers 5

8

here is a generic (Directive) solution to move to next similar control type when reaches the maximum length

1- Create the Directive

import { Directive, HostListener } from '@angular/core';

@Directive({
  selector: 'input[moveNextByMaxLength], textarea[moveNextByMaxLength]',
})
export class MoveNextByMaxLengthDirective {
  @HostListener('keyup', ['$event']) onKeyDown(keyboardEvent: KeyboardEvent) {
    const target = keyboardEvent.target as
      | HTMLInputElement
      | HTMLTextAreaElement
      | null;

    if (!target || target.maxLength !== target.value.length) return;

    keyboardEvent.preventDefault();

    const { type } = target;
    let { nextElementSibling } = target;

    while (nextElementSibling) {
      if (
        (nextElementSibling as HTMLInputElement | HTMLTextAreaElement).type ===
        type
      ) {
        (nextElementSibling as HTMLInputElement | HTMLTextAreaElement).focus();
        return;
      }

      nextElementSibling = nextElementSibling.nextElementSibling;
    }
  }
}

2- Declare the Directive in the module

@NgModule({
  imports: [ BrowserModule ],
  declarations: [
    AppComponent,
    MoveNextByMaxLengthDirective 
  ],
  bootstrap: [ AppComponent ]
})

3- Use the Directive in the component

<input formControlName="day" maxlength="2" moveNextByMaxLength placeholder="DD" type="text" (keyup)="keytab($event)" />
<input formControlName="month" maxlength="2" moveNextByMaxLength placeholder="MM" type="text" (keyup)="keytab($event)" />
<input formControlName="year" maxlength="4" placeholder="YYYY" type="text" />
Sign up to request clarification or add additional context in comments.

2 Comments

its working fine , but problem is here that if you want to remove data by backspace .. its not going to previous input .... i am using this solution for 4 OTP input boxes
It is not working using with div what are the changes required. <div><input formControlName="day" maxlength="2" moveNextByMaxLength placeholder="DD" type="text" (keyup)="keytab($event)" /> </div> <div><input formControlName="month" maxlength="2" moveNextByMaxLength placeholder="MM" type="text" (keyup)="keytab($event)" /> </div> <div><input formControlName="year" maxlength="4" placeholder="YYYY" type="text" /> </div>
4

Use a different approach. Angular does not select elements and read attributes from the existing DOM, as jQuery does, because Angular generates the DOM from data. So it's difficult, if possible at all, to read the input's maxlength attribute, and anyway it would be clumsy an "non-Angulary".

Instead, use a different approach and pass the maxLength in the keyup function :

<input type="text" (keyup)="keytab($event, 2)" />
<input type="text" (keyup)="keytab($event, 4)" />


keytab(event, maxLength){
   console.log(maxlength); // 2 or 4
   // .......
}

2 Comments

Thanks for your answer. I'm now passing the extra param to the keytab function, and from there I get the input value length and compare them - if (event.target.value.length < maxLength) ...
@Josethehose can you comment the full logic please, thanks
4

Another simple answer for Angular 9+

<input #input1 (keyup)="(input1.value.length == 2) ? input2.focus() : ''" type="text" maxlength="2">
<input #input2 (keyup)="(input2.value.length == 2) ? input3.focus() : ''" type="text" maxlength="2">
<input #input3 (keyup)="(input2.value.length == 2) ? submit.focus() : ''"  type="text" maxlength="2">

<button #submit type="submit">Submit</button>

Comments

2

Just an idea but if you're using reactive forms you could do something like this:

import { FormGroup, FormBuilder, Validators } from "@angular/forms";
import { Component, OnInit, ViewChild, ElementRef } from "@angular/core";
import { filter } "rxjs/operators";

@Component({
  selector: "app-form",
  template: `
    <form [formGroup]="form">
        <input formControlName="day" placeholder="DD" type="text" #day />
        <input formControlName="month" placeholder="MM" type="text" #month />
        <input formControlName="year" placeholder="YYYY" type="text" #year />
    </form>
`
})
export class FormComponent implements OnInit {
  form: FormGroup;

  @ViewChild("day") dayElement: ElementRef;

  @ViewChild("month") monthElement: ElementRef;

  @ViewChild("year") yearElement: ElementRef;

  constructor(private fb: FormBuilder) {}

  ngOnInit() {
    const dayMaxLength = 2;
    const monthMaxLength = 2;
    const yearMaxLength = 4;

    this.form = this.fb.group({
      day: ["", [Validators.required, Validators.maxLength(dayMaxLength)]],
      month: ["", [Validators.required, Validators.maxLength(monthMaxLength)]],
      year: ["", [Validators.required, Validators.maxLength(yearMaxLength)]]
    });

    this.form.get("day").valueChanges
      .pipe(filter((value: string) => value.length === dayMaxLength))
      .subscribe(() => this.monthElement.nativeElement.focus());

    this.form.get("month").valueChanges
      .pipe(filter((value: string) => value.length === monthMaxLength))
      .subscribe(() => this.yearElement.nativeElement.focus());
}

Basically subscribe to the value changes of both day and month form controls, filter each stream so that it only continues when the value is equal to the max length, then set the focus to the next element. Probably worth noting these will need to be unsubscribed from too.

3 Comments

This looks interesting. I'll try it later and let you know. Many thanks!
I have try this solution. this.form.controls['day'].valueChanges works instead of this.form.get("day").valueChanges
This is definitly the most elegant way of doing things in angular. Like @EmonZan said tough, your example does not work with get. A the best way would probably be this.form.controls.day.valueChanges.
1

Pure js approach

let allInputs = document.getElementsByTagName('input');
let index = 0;
for(i=0;i<allInputs.length;i++) {
allInputs[i].onkeydown =trackInputandChangeFocus;
}

function trackInputandChangeFocus() {
let allInputsArr = Array.from(allInputs); 
let presentInput = allInputsArr.indexOf(this)
if(this.value.length == parseInt(this.getAttribute('maxlength'))) {
      let next;
      if(presentInput != 2) next = allInputsArr[presentInput+1]
      else next = allInputsArr[0]
      next.focus();
    }
}
<input formControlName="day" maxlength="2" placeholder="DD" type="text" (keyup)="keytab($event)" />
<input formControlName="month" maxlength="2" placeholder="MM" type="text" (keyup)="keytab($event)" />
<input formControlName="year" maxlength="4" placeholder="YYYY" type="text" />

1 Comment

But what's the point? OP explicitely said that they use Angular 4.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.