将焦点放在<input>元素上

问题描述 投票:0回答:15

我正在使用 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();
  }

搜索框没有设置焦点

我该怎么做?

angular angular5 angular-forms
15个回答
213
投票

编辑 2022:
通过@Cichy 的回答阅读更现代的方式below


像这样修改show search方法

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

67
投票

你应该为此使用 HTML autofocus

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

注意:如果你的组件被持久化和重用,它只会在第一次附加片段时自动对焦。这可以通过使用 global DOM 侦听器 来克服,该侦听器在附加 DOM 片段时检查自动对焦属性,然后重新应用它或通过 JavaScript 聚焦。

这里是一个全局监听器的例子,它只需要在你的 spa 应用程序中放置一次,无论同一个片段被重复使用多少次,自动对焦都会起作用:

(new MutationObserver(function (mutations, observer) {
    for (let i = 0; i < mutations.length; i++) {
        const m = mutations[i];
        if (m.type == 'childList') {
            for (let k = 0; k < m.addedNodes.length; k++) {
                const autofocuses = m.addedNodes[k].querySelectorAll("[autofocus]"); //Note: this ignores the fragment's root element
                console.log(autofocuses);
                if (autofocuses.length) {
                    const a = autofocuses[autofocuses.length - 1]; // focus last autofocus element
                    a.focus();
                    a.select();
                }
            }
        }
    }
})).observe(document.body, { attributes: false, childList: true, subtree: true });

45
投票

该指令将在元素显示后立即聚焦并选择元素中的任何文本。在某些情况下,这可能需要一个 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>

25
投票

组件的html:

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

组件控制器:

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

..并且不要忘记从@angular/cdk/a11y导入A11yModule

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

19
投票

我要权衡这个(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);
    }
  }
}

15
投票

这在没有 setTimeout 的情况下在 Angular 8 中工作:

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,声明您在其中具有输入字段的组件。 然后使用 cdkTrapFocuscdkTrapFocusAutoCapture 指令并在 html 中像这样使用并在输入上设置 tabIndex:

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

我们的下拉菜单在定位和响应方面存在一些问题,因此开始使用 cdk 中的 OverlayModule,而这种使用 A11yModule 的方法完美无缺。


15
投票

在 Angular 中,在 HTML 本身中,您可以将焦点设置为单击按钮时输入。

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

<input #myInput></input>

11
投票

要在布尔值更改后执行并避免使用超时,您可以这样做:

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

constructor(private cd: ChangeDetectorRef) {}

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

6
投票

我有同样的情况,这对我有用,但我没有你拥有的“隐藏/显示”功能。因此,也许您可以先检查当字段始终可见时是否获得焦点,然后尝试解决为什么在更改可见性时不起作用(可能这就是为什么您需要应用睡眠或承诺)

要设置焦点,这是您需要做的唯一更改:

你的 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


5
投票

还有一个名为

cdkFocusInitial
的 DOM 属性,它适用于我的输入。 您可以在这里阅读更多相关信息:https://material.angular.io/cdk/a11y/overview


3
投票

仅使用 Angular 模板

<input type="text" #searchText>

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

0
投票

使用覆盖/对话框时,需要在

cdkFocusInitial
cdkTrapFocus
中使用
cdkTrapFocusAutoCapture

CDK 地区

如果您将 cdkFocusInitial 与 CdkTrapFocus 指令一起使用,除非您也启用了 cdkTrapFocusAutoCapture 选项,否则什么也不会发生。这是因为 CdkTrapFocus 默认不捕获初始化焦点。

在覆盖/对话框组件中:

<div cdkTrapFocus cdkTrapFocusAutoCapture>
  <input cdkFocusInitial>
</div>

0
投票

@john-white 魔术在零 setTimeout 下起作用的原因是因为

this.searchElement.nativeElement.focus();

被发送到浏览器 callStack 的末尾,因此最后/稍后执行,这不是让它工作的好方法,它可能意味着代码中还有其他逻辑可以改进。


0
投票

对我来说,它只有在使用模糊功能后才有效。

为了保持干净我给它做了一个指令:

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

@Directive({
  selector: '[appAutofocus]'
})
export class AutofocusDirective {
  constructor(private el: ElementRef) {
  }
    
  async ngOnInit() {
    const input = this.el.nativeElement as IonInput;

    await input.setBlur();
    await input.setFocus();
  }
}

-2
投票

更简单的方法也是这样做。

let elementReference = document.querySelector('<your css, #id selector>');
    if (elementReference instanceof HTMLElement) {
        elementReference.focus();
    }
© www.soinside.com 2019 - 2024. All rights reserved.