如何在 Angular 2 中打开模态元素后立即将焦点设置在模态元素上?
How to set focus on the elements of the modal as soon as they are opened in Angular 2?
我是 Angular JS 的新手并且有了 Angular 2 的新版本,我在执行指令时遇到了麻烦,该指令可以处理打开的模态上的强制焦点单击按钮。
以前问过几个类似的问题,答案在Angular1中如下:
app.directive('focusMe',
['$timeout',
function ($timeout) {
return {
link: {
pre: function preLink(scope, element, attr) {
// ...
},
post: function postLink(scope, element, attr) {
$timeout(function () {
element[0].focus();
}, 0);
}
}
}
}]);
});
我正在尝试转换 Angular 2 中的同一段代码。但我不确定如何实现它。谁能给我更多关于如何实现这一目标的信息,为我指明正确的方向。
编辑:
我尝试按如下方式实现该指令,当我调试代码时,我什至看到正在调用该指令,但我仍然无法将焦点放在模式对话框的元素上:
import { Directive, ElementRef } from "@angular/core";
@Directive({
selector: "[ModFocus]"
})
export class ModalFocus {
constructor(private _el: ElementRef) {
this._el.nativeElement.focus();
}
}
我是不是做错了什么?或者除了在 nativeElement 上调用 focus() 之外,我还需要做其他事情吗?
HTML 模态:
<div class="modal-dialog modal-sm" tabindex="-1">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Are you sure?</h4>
</div>
<div class="modal-body">
Warning
</div>
<div class="modal-footer ok-cancel" ModFocus>
<button type="button" class="btn btn-default" (click)="cancel()">Cancel</button>
<button type="button" class="btn btn-primary" (click)="delete()" data-dismiss="modal">Delete</button>
</div>
</div>
</div>
谢谢。
当您使用 Angular 2 创建组件时,您使用的语法与 AngularJS 不同。您的示例代码可能如下所示:
import { Component, ElementRef, ViewChild } from "@angular/core";
@Component({
selector: "whatever",
template: `<input #myControl type='input'/>`
})
export class MyComponent {
@ViewChild("myControl") myCtrl: ElementRef;
constructor () {
// This is wrong. It should be done in ngOnInit not in the
// constructor as the element might not yet be available.
//this.myCtrl.nativeElement.focus();
}
}
我是在没有检查的情况下从脑海中写下这篇文章的,但这应该让您对应该前进的方向有一个很好的了解。
更新:
在您更新问题时,我认为这里有一些错误。在您的新代码中,您将焦点设置在构造函数中。可能是您的视图仍未生成,因此您要设置焦点的元素仍然不可用(在我之前的示例中,我在构造函数中实例化它时误导了您,当我想要 OnInit
。对此我深表歉意。)。我会做以下事情:
import { Directive, ElementRef, OnInit } from "@angular/core";
@Directive({
selector: "[ModFocus]"
})
export class ModalFocus implements OnInit {
constructor(private _el: ElementRef) {
}
ngOnInit(): any {
// At this point, your element should be available.
this._el.nativeElement.focus();
}
}
OnInit
是 Angular 2 发出的 lifecycle hooks 之一。我建议您仔细阅读它们以便在调用它们时获得更好的理解。
更新 2:
问题是 Directives
没有模板。对添加它们的元素执行操作。创建指令时,其构造函数如下所示:
构造函数(私有 el:ElementRef,私有渲染器:Renderer){ }
el
应该可以访问 this.el.nativeElement.focus()
看看 angular.io 上关于 Attribute Directives
的这篇文章
我在问题中对指令的执行实际上是正确的。焦点没有进入模式的问题是因为 tabindex = -1 放置错误。
以下是我创建的指令。我没有使用 ElementRef anymore directly. Instead, I used the Renderer as Angular docs clearly mentioned to avoid the classes which are tagged with security risk。
import { Directive, ElementRef, Renderer} from "@angular/core";
@Directive({
selector: "[ModFocus]"
})
export class modalFocus {
constructor(private _el: ElementRef, private renderer: Renderer) {
this.renderer.invokeElementMethod(this._el.nativeElement, 'focus');
}
}
HTML:
<div class="modal fade" tabindex="-1">
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Are you sure?</h4>
</div>
<div class="modal-body">
Warning
</div>
<div class="modal-footer ok-cancel">
<button type="button" class="btn btn-default" (click)="cancel()" ModFocus>Cancel</button>
<button type="button" class="btn btn-primary" (click)="delete()" data-dismiss="modal">Delete</button>
</div>
</div>
</div>
</div>
在上面的 html 中,我实际上缺少主标签上带有 class 模态淡入淡出的 tabindex。在此处添加 tabindex 时,焦点会转移到模态框上的按钮上。
Husein 提供了非常有用的宝贵意见。因此,我接受他的回答。再次感谢侯赛因。
trapFocus(){
// add all the elements inside modal which you want to make focusable
const focusableElements = 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])';
const modal:any = document.querySelector('#deactivate-modal'); // select the modal by it's id
const firstFocusableElement = modal.querySelectorAll(focusableElements)[0]; // get first element to be focused inside modal
const focusableContent = modal.querySelectorAll(focusableElements);
const lastFocusableElement = focusableContent[focusableContent.length - 1]; // get last element to be focused inside modal
document.addEventListener('keydown', function(e) {
let isTabPressed = e.key === 'Tab' || e.keyCode === 9;
if (!isTabPressed) {
return;
}
if (e.shiftKey) { // if shift key pressed for shift + tab combination
if (document.activeElement === firstFocusableElement) {
lastFocusableElement.focus(); // add focus for the last focusable element
e.preventDefault();
}
} else { // if tab key is pressed
if (document.activeElement === lastFocusableElement) { // if focused has reached to last focusable element then focus first focusable element after pressing tab
firstFocusableElement.focus(); // add focus for the first focusable element
e.preventDefault();
}
}
});
firstFocusableElement.focus();
}
我是 Angular JS 的新手并且有了 Angular 2 的新版本,我在执行指令时遇到了麻烦,该指令可以处理打开的模态上的强制焦点单击按钮。
以前问过几个类似的问题,答案在Angular1中如下:
app.directive('focusMe',
['$timeout',
function ($timeout) {
return {
link: {
pre: function preLink(scope, element, attr) {
// ...
},
post: function postLink(scope, element, attr) {
$timeout(function () {
element[0].focus();
}, 0);
}
}
}
}]);
});
我正在尝试转换 Angular 2 中的同一段代码。但我不确定如何实现它。谁能给我更多关于如何实现这一目标的信息,为我指明正确的方向。
编辑:
我尝试按如下方式实现该指令,当我调试代码时,我什至看到正在调用该指令,但我仍然无法将焦点放在模式对话框的元素上:
import { Directive, ElementRef } from "@angular/core";
@Directive({
selector: "[ModFocus]"
})
export class ModalFocus {
constructor(private _el: ElementRef) {
this._el.nativeElement.focus();
}
}
我是不是做错了什么?或者除了在 nativeElement 上调用 focus() 之外,我还需要做其他事情吗?
HTML 模态:
<div class="modal-dialog modal-sm" tabindex="-1">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Are you sure?</h4>
</div>
<div class="modal-body">
Warning
</div>
<div class="modal-footer ok-cancel" ModFocus>
<button type="button" class="btn btn-default" (click)="cancel()">Cancel</button>
<button type="button" class="btn btn-primary" (click)="delete()" data-dismiss="modal">Delete</button>
</div>
</div>
</div>
谢谢。
当您使用 Angular 2 创建组件时,您使用的语法与 AngularJS 不同。您的示例代码可能如下所示:
import { Component, ElementRef, ViewChild } from "@angular/core";
@Component({
selector: "whatever",
template: `<input #myControl type='input'/>`
})
export class MyComponent {
@ViewChild("myControl") myCtrl: ElementRef;
constructor () {
// This is wrong. It should be done in ngOnInit not in the
// constructor as the element might not yet be available.
//this.myCtrl.nativeElement.focus();
}
}
我是在没有检查的情况下从脑海中写下这篇文章的,但这应该让您对应该前进的方向有一个很好的了解。
更新:
在您更新问题时,我认为这里有一些错误。在您的新代码中,您将焦点设置在构造函数中。可能是您的视图仍未生成,因此您要设置焦点的元素仍然不可用(在我之前的示例中,我在构造函数中实例化它时误导了您,当我想要 OnInit
。对此我深表歉意。)。我会做以下事情:
import { Directive, ElementRef, OnInit } from "@angular/core";
@Directive({
selector: "[ModFocus]"
})
export class ModalFocus implements OnInit {
constructor(private _el: ElementRef) {
}
ngOnInit(): any {
// At this point, your element should be available.
this._el.nativeElement.focus();
}
}
OnInit
是 Angular 2 发出的 lifecycle hooks 之一。我建议您仔细阅读它们以便在调用它们时获得更好的理解。
更新 2:
问题是 Directives
没有模板。对添加它们的元素执行操作。创建指令时,其构造函数如下所示:
构造函数(私有 el:ElementRef,私有渲染器:Renderer){ }
el
应该可以访问 this.el.nativeElement.focus()
看看 angular.io 上关于 Attribute Directives
的这篇文章我在问题中对指令的执行实际上是正确的。焦点没有进入模式的问题是因为 tabindex = -1 放置错误。
以下是我创建的指令。我没有使用 ElementRef anymore directly. Instead, I used the Renderer as Angular docs clearly mentioned to avoid the classes which are tagged with security risk。
import { Directive, ElementRef, Renderer} from "@angular/core";
@Directive({
selector: "[ModFocus]"
})
export class modalFocus {
constructor(private _el: ElementRef, private renderer: Renderer) {
this.renderer.invokeElementMethod(this._el.nativeElement, 'focus');
}
}
HTML:
<div class="modal fade" tabindex="-1">
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Are you sure?</h4>
</div>
<div class="modal-body">
Warning
</div>
<div class="modal-footer ok-cancel">
<button type="button" class="btn btn-default" (click)="cancel()" ModFocus>Cancel</button>
<button type="button" class="btn btn-primary" (click)="delete()" data-dismiss="modal">Delete</button>
</div>
</div>
</div>
</div>
在上面的 html 中,我实际上缺少主标签上带有 class 模态淡入淡出的 tabindex。在此处添加 tabindex 时,焦点会转移到模态框上的按钮上。
Husein 提供了非常有用的宝贵意见。因此,我接受他的回答。再次感谢侯赛因。
trapFocus(){
// add all the elements inside modal which you want to make focusable
const focusableElements = 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])';
const modal:any = document.querySelector('#deactivate-modal'); // select the modal by it's id
const firstFocusableElement = modal.querySelectorAll(focusableElements)[0]; // get first element to be focused inside modal
const focusableContent = modal.querySelectorAll(focusableElements);
const lastFocusableElement = focusableContent[focusableContent.length - 1]; // get last element to be focused inside modal
document.addEventListener('keydown', function(e) {
let isTabPressed = e.key === 'Tab' || e.keyCode === 9;
if (!isTabPressed) {
return;
}
if (e.shiftKey) { // if shift key pressed for shift + tab combination
if (document.activeElement === firstFocusableElement) {
lastFocusableElement.focus(); // add focus for the last focusable element
e.preventDefault();
}
} else { // if tab key is pressed
if (document.activeElement === lastFocusableElement) { // if focused has reached to last focusable element then focus first focusable element after pressing tab
firstFocusableElement.focus(); // add focus for the first focusable element
e.preventDefault();
}
}
});
firstFocusableElement.focus();
}