Atur fokus pada elemen <input>

107

Saya sedang mengerjakan aplikasi front-end dengan Angular 5, dan saya perlu menyembunyikan kotak pencarian, tetapi dengan mengklik tombol, kotak pencarian harus ditampilkan dan difokuskan.

Saya telah mencoba beberapa cara yang ditemukan di StackOverflow dengan direktif atau lebih, tetapi tidak berhasil.

Berikut ini contoh kode:

@Component({
   selector: 'my-app',
   template: `
    <div>
    <h2>Hello</h2>
    </div>
    <button (click) ="showSearch()">Show Search</button>
    <p></p>
    <form>
      <div >
        <input *ngIf="show" #search type="text"  />            
      </div>
    </form>
    `,
  })
  export class App implements AfterViewInit {
  @ViewChild('search') searchElement: ElementRef;

  show: false;
  name:string;
  constructor() {    
  }

  showSearch(){
    this.show = !this.show;    
    this.searchElement.nativeElement.focus();
    alert("focus");
  }

  ngAfterViewInit() {
    this.firstNameElement.nativeElement.focus();
  }

Kotak pencarian tidak diatur ke fokus.

Bagaimana saya bisa melakukan itu?

Bob
sumber

Jawaban:

128

Ubah metode pencarian acara seperti ini

showSearch(){
  this.show = !this.show;  
  setTimeout(()=>{ // this will make the execution after the above boolean has changed
    this.searchElement.nativeElement.focus();
  },0);  
}
Arvind Muthuraman
sumber
15
Mengapa kita perlu menggunakan setTimeout? Bukankah perubahan boolean sinkron?
Andrei Rosu
6
bukankah itu mengacaukan zonejs?
lawphotog
1
@AndreiRosu, tanpa itu saya mendapatkan kesalahan karena perubahan tidak diberikan
Islam Q.
13
Ini berhasil, tetapi tanpa penjelasan yang jelas, ini ajaib. Sihir sering pecah.
John White
1
Saya mencoba untuk memfokuskan elemen saya di dalam panel ketika panel dibuka, jadi batas waktu 0 tidak berhasil untuk saya, tetapi ini berhasil:setTimeout(() => { this.searchElement.nativeElement.focus() }, 100)
tclark333
41

Anda harus menggunakan autofokus html untuk ini:

<input *ngIf="show" #search type="text" autofocus /> 

Catatan: jika komponen Anda dipertahankan dan digunakan kembali, itu hanya akan melakukan fokus otomatis saat pertama kali fragmen dipasang. Hal ini dapat diatasi dengan memiliki pendengar dom global yang memeriksa atribut fokus otomatis di dalam fragmen dom saat dilampirkan, lalu menerapkannya kembali atau memfokuskan melalui javascript.

N-makan
sumber
46
Ini hanya akan berfungsi sekali setiap halaman disegarkan, tidak berkali-kali.
moritzg
23

Arahan ini akan langsung fokus dan memilih teks apa pun di elemen segera setelah ditampilkan. Ini mungkin memerlukan setTimeout untuk beberapa kasus, ini belum banyak diuji.

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

@Directive({
  selector: '[appPrefixFocusAndSelect]',
})
export class FocusOnShowDirective implements OnInit {

  constructor(private el: ElementRef) {
    if (!el.nativeElement['focus']) {
      throw new Error('Element does not accept focus.');
    }
  }

  ngOnInit(): void {
    const input: HTMLInputElement = this.el.nativeElement as HTMLInputElement;
    input.focus();
    input.select();
  }
}

Dan di HTML:

 <mat-form-field>
     <input matInput type="text" appPrefixFocusAndSelect [value]="'etc'">
 </mat-form-field>
ggranum.dll
sumber
terima kasih, ini adalah satu-satunya solusi yang berhasil untuk saya dalam kasus menunggu permintaan http
Abdelhalim FELLAGUE CHEBRA
1
Saya menemukan solusi ini lebih disukai daripada jawaban yang diterima. Ini lebih bersih dalam kasus penggunaan ulang kode, dan lebih Angular-sentris.
derekbaker783
Juga jangan lupa untuk menambahkan direktif di deklarasi modul.
Elijah Dollin
11

Saya akan mempertimbangkan ini (Solusi Sudut 7)

input [appFocus]="focus"....
import {AfterViewInit, Directive, ElementRef, Input,} from '@angular/core';

@Directive({
  selector: 'input[appFocus]',
})
export class FocusDirective implements AfterViewInit {

  @Input('appFocus')
  private focused: boolean = false;

  constructor(public element: ElementRef<HTMLElement>) {
  }

  ngAfterViewInit(): void {
    // ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked.
    if (this.focused) {
      setTimeout(() => this.element.nativeElement.focus(), 0);
    }
  }
}
Ricardo Saracino
sumber
1
Ini tampaknya sangat identik dengan jawaban yang diterima
Liam
9

Ini berfungsi di Angular 8 tanpa setTimeout:

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

@Directive({
  selector: 'input[inputAutoFocus]'
})
export class InputFocusDirective implements AfterContentChecked {
  constructor(private element: ElementRef<HTMLInputElement>) {}

  ngAfterContentChecked(): void {
    this.element.nativeElement.focus();
  }
}

Penjelasan: Ok jadi ini berfungsi karena: Deteksi perubahan. Itu alasan yang sama bahwa setTimout berfungsi, tetapi saat menjalankan setTimeout di Angular, ia akan melewati Zone.js dan menjalankan semua pemeriksaan lagi, dan berfungsi karena saat setTimeout selesai semua perubahan selesai. Dengan hook siklus hidup yang benar (AfterContentChecked), hasil yang sama dapat dicapai, tetapi dengan keuntungan bahwa siklus tambahan tidak akan dijalankan. Fungsi ini akan aktif ketika semua perubahan diperiksa dan diteruskan, dan berjalan setelah hook AfterContentInit dan DoCheck. Jika saya salah di sini tolong perbaiki saya.

Lebih dari satu siklus proses dan deteksi perubahan di https://angular.io/guide/lifecycle-hooks

PEMBARUAN : Saya menemukan cara yang lebih baik untuk melakukan ini jika menggunakan Angular Material CDK, paket a11y. Pertama impor A11yModule di modul menyatakan komponen Anda memiliki masukan-lapangan di Kemudian gunakan. CdkTrapFocus dan cdkTrapFocusAutoCapture arahan dan penggunaan seperti ini di html dan set tabindex pada input:

<div class="dropdown" cdkTrapFocus cdkTrapFocusAutoCapture>
    <input type="text tabIndex="0">
</div>

Kami mengalami beberapa masalah dengan tarik-turun terkait pemosisian dan daya tanggap dan mulai menggunakan OverlayModule dari cdk, dan metode yang menggunakan A11yModule ini bekerja dengan sempurna.

SNDVLL
sumber
Hai ... Saya tidak mencoba cdkTrapFocusAutoCaptureatribut, tetapi saya mengubah contoh pertama Anda menjadi kode saya. Untuk alasan yang tidak diketahui (bagi saya) itu tidak bekerja dengan hook siklus hidup AfterContentChecked, tetapi hanya dengan OnInit. Dalam particolar dengan AfterContentChecked saya tidak dapat mengubah fokus dan memindahkan (dengan mouse atau papan tombol) ke input lain tanpa arahan dalam bentuk yang sama.
timhecker
@timhecker ya kami menghadapi masalah yang sama saat memigrasi komponen kami ke hamparan cdk (ini berfungsi saat hanya menggunakan css alih-alih hamparan untuk pemosisian). Ini berfokus tanpa batas pada input ketika sebuah komponen yang menggunakan direktif terlihat. Satu-satunya solusi yang saya temukan adalah menggunakan arahan cdk yang disebutkan dalam pembaruan.
SNDVLL
7

Di Angular, di dalam HTML itu sendiri, Anda dapat mengatur fokus ke input dengan mengklik tombol.

<button (click)="myInput.focus()">Click Me</button>

<input #myInput></input>
shaheer shukur
sumber
Jawaban ini menunjukkan cara sederhana untuk secara terprogram memilih elemen lain dalam HTML, yang saya cari (semua jawaban "fokus awal" tidak menyelesaikan cara bereaksi terhadap suatu peristiwa dengan mengubah fokus) - sayangnya, saya perlu bereaksi ke mat-select selectionChangedacara yang terjadi sebelum hal-hal UI lainnya, jadi menarik fokus pada saat itu tidak berhasil. Sebaliknya saya harus menulis metode setFocus(el:HTMLElement):void { setTimeout(()=>el.focus(),0); }dan menyebutnya dari event handler: <mat-select (selectionChanged)="setFocus(myInput)">. Tidak sebaik, tapi sederhana dan bekerja dengan baik. Terima kasih!
Guss
Tidak berhasil untuk saya. Saya mendapatkan ERROR TypeError: Tidak dapat membaca 'fokus' properti dari undefined
Reza Taba
@RezaTaba, apakah Anda memanggil fungsi 'myInput.focus ()' atau Anda baru saja menulis 'myInput.focus'? Juga, pastikan, elemen input Anda berada di dalam div yang dirender (* ngIf false dapat menyebabkan kesalahan, misalnya)
shaheer shukur
6

Untuk melakukan eksekusi setelah boolean berubah dan menghindari penggunaan timeout yang bisa Anda lakukan:

import { ChangeDetectorRef } from '@angular/core';

constructor(private cd: ChangeDetectorRef) {}

showSearch(){
  this.show = !this.show;  
  this.cd.detectChanges();
  this.searchElement.nativeElement.focus();
}
Marcin Restel
sumber
5
Saya mencoba ini dengan sudut 7, tidak berhasil, menggunakan batas waktu berfungsi dengan baik
rekiem87
bagi saya juga di sudut 8 tidak berfungsi, buruknya kita harus kembali ke setTimeout
Andrei Chivu
1

Hanya menggunakan Template Angular

<input type="text" #searchText>

<span (click)="searchText.focus()">clear</span>
Sandipan Mitra
sumber
"Jika Anda menggunakan Material ..."
Andrew Koper
ERROR TypeError: Tidak dapat membaca properti 'fokus' dari undefined
Reza Taba
1

html komponen:

<input [cdkTrapFocusAutoCapture]="show" [cdkTrapFocus]="show">

pengontrol komponen:

showSearch() {
  this.show = !this.show;    
}

..dan jangan lupa import A11yModule dari @ angular / cdk / a11y

import { A11yModule } from '@angular/cdk/a11y'
Cichy
sumber
Solusi terbaik. Terima kasih. Solusi lain memberi saya kesalahan.
Reza Taba
1

Saya mengalami skenario yang sama, ini berhasil untuk saya tetapi saya tidak memiliki fitur "sembunyikan / tampilkan" yang Anda miliki. Jadi mungkin Anda bisa terlebih dahulu memeriksa apakah Anda mendapatkan fokus ketika Anda memiliki bidang yang selalu terlihat, dan kemudian mencoba untuk memecahkan mengapa tidak berhasil saat Anda mengubah visibilitas (mungkin itu sebabnya Anda perlu menerapkan tidur atau janji)

Untuk mengatur fokus, ini adalah satu-satunya perubahan yang perlu Anda lakukan:

input mat Html Anda harus:

<input #yourControlName matInput>

di kelas TS Anda, referensi seperti ini di bagian variabel (

export class blabla...
    @ViewChild("yourControlName") yourControl : ElementRef;

Tombol Anda baik-baik saja, menelepon:

  showSearch(){
       ///blabla... then finally:
       this.yourControl.nativeElement.focus();
}

dan hanya itu. Anda dapat memeriksa solusi ini di posting ini yang saya temukan, jadi terima kasih kepada -> https://codeburst.io/focusing-on-form-elements-the-angular-way-e9a78725c04f

Yogurtu
sumber
-1

Cara yang lebih mudah juga dengan melakukan ini.

let elementReference = document.querySelector('<your css, #id selector>');
    if (elementReference instanceof HTMLElement) {
        elementReference.focus();
    }
patz
sumber
3
Anda benar-benar tidak ingin menanyakan dom secara langsung.
Richard.Davenport