<input>要素にフォーカスを設定します


106

Angular 5でフロントエンドアプリケーションを使用しています。検索ボックスを非表示にする必要がありますが、ボタンをクリックすると、検索ボックスが表示され、フォーカスが移動します。

StackOverflowで見つかったいくつかの方法をディレクティブなどで試しましたが、成功しません。

サンプルコードは次のとおりです。

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

検索ボックスがフォーカスするように設定されていません。

どうやってやるの?

回答:


128

このようにshowsearchメソッドを変更します

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

15
なぜsetTimeoutを使用する必要があるのですか?ブール値の変更は同期していませんか?
アンドレイロス

6
それはzonejsを台無しにしませんか?
lawphotog

1
@AndreiRosu、それがないと、変更がレンダリングされなかったためにエラーが発生していました
Islam Q.

13
これは機能しますが、明確な説明がなければ魔法です。魔法は頻繁に壊れます。
ジョンホワイト

1
0タイムアウトが私のために仕事をしませんでしたので、私は、パネルが開かれたパネルの内側に私の要素を集中しようとしていたが、これはやった:setTimeout(() => { this.searchElement.nativeElement.focus() }, 100)
tclark333

42

これにはhtmlオートフォーカスを使用する必要があります。

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

注:コンポーネントが永続化されて再利用される場合、フラグメントが最初にアタッチされたときにのみオートフォーカスが行われます。これは、接続時にdomフラグメント内のオートフォーカス属性をチェックするグローバルdomリスナーを用意し、それを再適用するか、JavaScriptを介してフォーカスすることで克服できます。


45
これは、ページが更新されるたびに1回だけ機能し、複数回は機能しません。
moritzg

23

このディレクティブは、要素が表示されるとすぐに、要素内のテキストにフォーカスして選択します。これには、場合によってはsetTimeoutが必要になることがありますが、あまりテストされていません。

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

そしてHTMLで:

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

ありがとう、これはhttpリクエストを待っている場合に私のために働いた唯一の解決策です
Abdelhalim FELLAGUECHEBRA20年

1
私はこの解決策が受け入れられた答えよりも好ましいと思います。コードの再利用の場合はよりクリーンで、より角度中心です。
derekbaker7 8320

また、モジュールの宣言にディレクティブを追加することを忘れないでください。
ElijahDollin20年

11

これについて検討します(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);
    }
  }
}


9

これは、setTimeoutなしでAngular8で機能しています。

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

説明:わかりました。これは次の理由で機能します:変更検出。setTimoutが機能するのと同じ理由ですが、AngularでsetTimeoutを実行すると、Zone.jsがバイパスされ、すべてのチェックが再度実行されます。setTimeoutが完了すると、すべての変更が完了するため、機能します。正しいライフサイクルフック(AfterContentChecked)を使用すると、同じ結果に到達できますが、余分なサイクルが実行されないという利点があります。この関数は、すべての変更がチェックされて渡されたときに起動し、フックAfterContentInitおよびDoCheckの後に実行されます。私がここで間違っているなら、私を訂正してください。

https://angular.io/guide/lifecycle-hooksで複数のライフサイクルと変更検出

更新:Angular Material CDK、a11y-packageを使用している場合、これを行うためのさらに良い方法を見つけました。最初に、入力フィールドがあるコンポーネントを宣言するモジュールにA11yModuleをインポートします。次に、cdkTrapFocusおよびcdkTrapFocusAutoCaptureディレクティブを使用し、htmlで次のように使用し、入力にtabIndexを設定します。

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

配置と応答性に関するドロップダウンにいくつかの問題があり、代わりにcdkからOverlayModuleを使用し始めましたが、A11yModuleを使用するこのメソッドは問題なく機能します。


こんにちは...私はcdkTrapFocusAutoCapture属性を試しませんが、最初の例を私のコードに変更しました。理由は不明ですが(私にとっては)、AfterContentCheckedライフサイクルフックでは機能しませんでしたが、OnInitでのみ機能しました。特にAfterContentCheckedを使用すると、同じフォームにそのディレクティブがないと、フォーカスを変更して(マウスまたはキーボードを使用して)別の入力に移動できません。
timhecker

@timheckerええ、コンポーネントをcdkオーバーレイに移行するときに同様の問題に直面しました(配置にオーバーレイの代わりにcssだけを使用すると機能しました)。ディレクティブを使用するコンポーネントが表示されている場合、入力に無期限に焦点を合わせました。私が見つけた唯一の解決策は、アップデートで言及されたcdkディレクティブを使用することでした。
SNDVLL

7

Angularでは、HTML自体の中で、ボタンをクリックしたときに入力するようにフォーカスを設定できます。

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

<input #myInput></input>

この回答は、HTMLで別の要素をプログラムで選択する簡単な方法を示しています。これは、私が探していたものです(すべての「初期フォーカス」の回答は、フォーカスを変更することによってイベントに反応する方法を解決しません)-残念ながら、私は反応する必要がありましたmat-select selectionChanged他のUIのものよりも前に発生するイベントに対応するため、その時点でフォーカスを引くことはできませんでした。代わりに、メソッドを記述setFocus(el:HTMLElement):void { setTimeout(()=>el.focus(),0); }して、イベントハンドラーから呼び出す必要がありました<mat-select (selectionChanged)="setFocus(myInput)">。それほど良くはありませんが、シンプルでうまく機能します。THX!
ガス

6

ブール値が変更された後に実行を行い、タイムアウトの使用を回避するには、次のようにします。

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

constructor(private cd: ChangeDetectorRef) {}

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

5
角度7でこれを試しましたが、機能しませんでした。タイムアウトを使用すると
正常に

私にとっても
Angular8



1

私は同じシナリオを持っています、これは私のために働きました、しかし私はあなたが持っている「非表示/表示」機能を持っていません。したがって、フィールドが常に表示されているときにフォーカスが得られるかどうかを最初に確認してから、表示を変更したときになぜ機能しないのかを解決しようとすることができます(おそらく、スリープまたは約束を適用する必要があるのはそのためです)

フォーカスを設定するには、これが必要な唯一の変更です。

HTMLマット入力は次のようになります。

<input #yourControlName matInput>

TSクラスでは、変数セクションでこのように参照します(

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

あなたのボタンは大丈夫です、呼び出します:

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

以上です。私が見つけたこの投稿でこの解決策を確認できるので、-> https://codeburst.io/focusing-on-form-elements-the-angular-way-e9a78725c04fに感謝し ます


0

コンポーネントのhtml:

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

コンポーネントのコントローラー:

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

..そして@angular / cdk / a11yからA11yModuleをインポートすることを忘れないでください

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

-1

より簡単な方法もこれを行うことです。

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

3
あなたは本当にdomに直接問い合わせたくありません。
Richard.Davenport
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.