Đặt tiêu điểm vào phần tử <input>


101

Tôi đang làm việc với một ứng dụng front-end với Angular 5 và tôi cần ẩn hộp tìm kiếm, nhưng khi nhấp vào một nút, hộp tìm kiếm sẽ được hiển thị và tập trung.

Tôi đã thử một số cách được tìm thấy trên StackOverflow với chỉ thị hoặc tương tự, nhưng không thể thành công.

Đây là mã mẫu:

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

Hộp tìm kiếm không được đặt thành tiêu điểm.

Làm thế nào tôi có thể làm điều đó?

Câu trả lời:


120

Sửa đổi phương pháp tìm kiếm chương trình như thế này

showSearch(){
  this.show = !this.show;  
  setTimeout(()=>{ // this will make the execution after the above boolean has changed
    this.searchElement.nativeElement.focus();
  },0);  
}

14
Tại sao chúng ta cần sử dụng setTimeout? Không phải là sự thay đổi của boolean đồng bộ?
Andrei Rosu

5
điều đó không gây rối với zonejs?
lawphotog

1
@AndreiRosu, nếu không có nó, tôi đã gặp lỗi vì các thay đổi không được hiển thị
Islam Q.

11
Điều này hoạt động, nhưng không có lời giải thích rõ ràng, đó là ma thuật. Phép thuật thường xuyên phá vỡ.
John White

1
Tôi đang cố gắng tập trung phần tử của mình vào bên trong bảng điều khiển khi bảng điều khiển được mở, vì vậy thời gian chờ 0 không hoạt động với tôi, nhưng điều này đã làm được:setTimeout(() => { this.searchElement.nativeElement.focus() }, 100)
tclark333

42

Bạn nên sử dụng tự động lấy nét html cho việc này:

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

Lưu ý: nếu thành phần của bạn được duy trì và sử dụng lại, nó sẽ chỉ tự động lấy nét vào lần đầu tiên phân mảnh được gắn vào. Điều này có thể được khắc phục bằng cách có một trình nghe dom toàn cầu kiểm tra thuộc tính tự động lấy nét bên trong một phân đoạn dom khi nó được đính kèm và sau đó áp dụng lại nó hoặc lấy nét qua javascript.


42
Điều này sẽ chỉ hoạt động một lần mỗi lần làm mới trang, không phải nhiều lần.
moritzg

22

Chỉ thị này sẽ ngay lập tức tập trung và chọn bất kỳ văn bản nào trong phần tử ngay khi nó được hiển thị. Điều này có thể yêu cầu setTimeout đối với một số trường hợp, nó chưa được thử nghiệm nhiều.

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

Và trong HTML:

 <mat-form-field>
     <input matInput type="text" appPrefixFocusAndSelect [value]="'etc'">
 </mat-form-field>

cảm ơn bạn, đây là cách giải quyết duy nhất phù hợp với tôi trong trường hợp chờ đợi theo yêu cầu http
Abdelhalim FELLAGUE CHEBRA

Tôi thấy giải pháp này thích hợp hơn câu trả lời được chấp nhận. Nó gọn gàng hơn trong trường hợp sử dụng lại mã và tập trung vào Angular hơn.
derekbaker783

9

Tôi sẽ cân nhắc về vấn đề này (Giải pháp Angular 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);
    }
  }
}

1
Điều này dường như khá giống với câu trả lời được chấp nhận
Liam

8

Điều này đang hoạt động i Angular 8 mà không có 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();
  }
}

Giải thích: Ok vì vậy điều này hoạt động vì: Phát hiện thay đổi. Đó cũng là lý do setTimout hoạt động, nhưng khi chạy setTimeout trong Angular, nó sẽ bỏ qua Zone.js và chạy lại tất cả các kiểm tra, và nó hoạt động vì khi setTimeout hoàn tất, tất cả các thay đổi đã hoàn thành. Với móc vòng đời chính xác (AfterContentChecked), kết quả tương tự có thể đạt được, nhưng với lợi thế là chu kỳ bổ sung sẽ không được chạy. Hàm sẽ kích hoạt khi tất cả các thay đổi được kiểm tra và thông qua, đồng thời chạy sau các hook AfterContentInit và DoCheck. Nếu tôi sai ở đây xin vui lòng sửa cho tôi.

Thêm một vòng đời và phát hiện thay đổi trên https://angular.io/guide/lifecycle-hooks

CẬP NHẬT : Tôi đã tìm thấy một cách thậm chí còn tốt hơn để làm điều này nếu một người đang sử dụng Angular Material CDK, gói a11y. Nhập khẩu đầu tiên A11yModule trong module khai báo các thành phần bạn có đầu vào trường trong Sau đó sử dụng. CdkTrapFocuscdkTrapFocusAutoCapture chỉ thị và sử dụng như thế này trong html và bộ tabIndex vào đầu vào:

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

Chúng tôi đã gặp một số vấn đề với trình đơn thả xuống của chúng tôi liên quan đến định vị và khả năng phản hồi và bắt đầu sử dụng OverlayModule từ cdk và phương pháp sử dụng A11yModule này hoạt động hoàn hảo.


Xin chào ... Tôi không thử cdkTrapFocusAutoCapturethuộc tính, nhưng tôi đã thay đổi ví dụ đầu tiên của bạn thành mã của tôi. Vì những lý do không xác định (đối với tôi), nó không hoạt động với móc vòng đời AfterContentChecked mà chỉ với OnInit. Đặc biệt với AfterContentChecked, tôi không thể thay đổi tiêu điểm và di chuyển (bằng chuột hoặc bàn phím) sang một đầu vào khác mà không có lệnh đó ở cùng một dạng.
timhecker

@timhecker vâng, chúng tôi đã đối mặt với một vấn đề tương tự khi di chuyển các thành phần của chúng tôi sang lớp phủ cdk (nó hoạt động khi chỉ sử dụng css thay vì lớp phủ để định vị). Nó tập trung vô thời hạn vào đầu vào khi một thành phần sử dụng chỉ thị được hiển thị. Giải pháp duy nhất tôi tìm thấy là sử dụng các lệnh cdk được đề cập trong bản cập nhật.
SNDVLL

6

Để thực hiện sau khi boolean đã thay đổi và tránh sử dụng thời gian chờ, bạn có thể làm:

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

constructor(private cd: ChangeDetectorRef) {}

showSearch(){
  this.show = !this.show;  
  this.cd.detectChanges();
  this.searchElement.nativeElement.focus();
}

5
Tôi cố gắng này với góc 7, nó đã không làm việc, sử dụng thời gian chờ làm việc tốt
rekiem87

đối với tôi cũng ở góc 8 không được làm việc, để xấu chúng ta phải quay trở lại setTimeout
Andrei Chivu

6

Trong Angular, trong chính HTML, bạn có thể đặt tiêu điểm để nhập liệu khi nhấp vào nút.

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

<input #myInput></input>

Câu trả lời này cho thấy một cách đơn giản để chọn một phần tử khác trong HTML theo chương trình, đó là những gì tôi đang tìm kiếm (tất cả các câu trả lời "tiêu điểm ban đầu" không giải quyết được cách phản ứng với một sự kiện bằng cách thay đổi tiêu điểm) - thật không may, tôi cần phải phản ứng đến một mat-select selectionChangedsự kiện xảy ra trước các nội dung giao diện người dùng khác, vì vậy việc kéo tiêu điểm vào thời điểm đó không hoạt động. Thay vào đó tôi đã phải viết một phương pháp setFocus(el:HTMLElement):void { setTimeout(()=>el.focus(),0); }và gọi nó là từ xử lý sự kiện: <mat-select (selectionChanged)="setFocus(myInput)">. Không đẹp, nhưng đơn giản và hoạt động tốt. cám ơn!
Guss


1

Chỉ sử dụng Mẫu Angular

<input type="text" #searchText>

<span (click)="searchText.focus()">clear</span>

"Nếu bạn đang sử dụng Material ..."
Andrew Koper

0

html của thành phần:

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

bộ điều khiển của thành phần:

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

..và đừng quên nhập A11yModule từ @ angle / cdk / a11y

import { A11yModule } from '@angular/cdk/a11y'

-1

Cách dễ dàng hơn cũng là làm điều này.

let elementReference = document.querySelector('<your css, #id selector>');
    if (elementReference instanceof HTMLElement) {
        elementReference.focus();
    }

3
Bạn thực sự không muốn truy vấn trực tiếp dom.
Richard.Davenport
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.