启用交互性
事件监听器
事件监听器函数可以像指定任何其他属性一样分配给虚拟节点,以实例化节点。在输出VNode
时,VNodeProperties
中事件监听器的命名反映了HTMLElement
上的等效事件。自定义小部件的作者可以随意命名他们的事件,但通常也遵循类似的onEventName
命名约定。
诸如事件处理程序之类的函数属性会自动绑定到实例化虚拟节点的小部件的this
上下文。但是,如果给定一个已绑定的函数作为属性值,则不会再次绑定this
。
处理焦点
在输出VNode
时,小部件可以使用VNodeProperties
的focus
属性来控制渲染时生成的 DOM 元素是否应获得焦点。这是一个特殊属性,它接受一个boolean
或一个返回boolean
的函数。
当直接传递true
时,元素仅在先前值为非true
时才会获得焦点(类似于常规属性更改检测)。当传递一个函数时,元素将在返回true
时获得焦点,无论先前的返回值是什么。
例如
给定元素排序,以下“firstFocus”输入将在初始渲染时获得焦点,而“subsequentFocus”输入将在所有后续渲染中获得焦点,因为它使用函数作为其focus
属性。
src/widgets/FocusExample.tsx
基于函数的变体
import { create, tsx, invalidator } from '@dojo/framework/core/vdom';
const factory = create({ invalidator });
export default factory(function FocusExample({ middleware: { invalidator } }) {
return (
<div>
<input key="subsequentFocus" type="text" focus={() => true} />
<input key="firstFocus" type="text" focus={true} />
<button onclick={() => invalidator()}>Re-render</button>
</div>
);
});
基于类的变体
import WidgetBase from '@dojo/framework/core/WidgetBase';
import { tsx } from '@dojo/framework/core/vdom';
export default class FocusExample extends WidgetBase {
protected render() {
return (
<div>
<input key="subsequentFocus" type="text" focus={() => true} />
<input key="firstFocus" type="text" focus={true} />
<button onclick={() => this.invalidate()}>Re-render</button>
</div>
);
}
}
委托焦点
基于函数的小部件可以使用focus
中间件为其子级提供焦点,或接受来自父级小部件的焦点。基于类的小部件可以使用FocusMixin
(来自@dojo/framework/core/mixins/Focus
)以类似的方式委托焦点。
FocusMixin
向小部件类的 API 添加了一个this.shouldFocus()
方法,而基于函数的小部件使用focus.shouldFocus()
中间件方法来实现相同目的。此方法检查小部件是否处于可以执行焦点操作的状态,并且仅在小部件的this.focus()
方法再次调用之前(基于函数的小部件使用focus.focus()
中间件等效项)才会返回true
。
FocusMixin
或focus
中间件还会向小部件的 API 添加一个focus
函数属性。框架使用此属性的布尔结果来确定小部件(或其子级之一)在渲染时是否应获得焦点。通常,小部件会通过其focus
属性将shouldFocus
方法传递给特定子级小部件或输出节点,从而允许父级小部件将焦点委托给其子级。
有关基于函数的小部件的示例,请参阅 Dojo 中间件参考指南中的focus
中间件委托示例。
以下展示了在基于类的跨小部件层次结构和输出 VNode 中委托和控制焦点的示例
src/widgets/FocusableWidget.tsx
import WidgetBase from '@dojo/framework/core/WidgetBase';
import { tsx } from '@dojo/framework/core/vdom';
import Focus from '@dojo/framework/core/mixins/Focus';
interface FocusInputChildProperties {
onFocus: () => void;
}
class FocusInputChild extends Focus(WidgetBase)<FocusInputChildProperties> {
protected render() {
/*
The child widget's `this.shouldFocus()` method is assigned directly to the
input node's `focus` property, allowing focus to be delegated from a higher
level containing parent widget.
The input's `onfocus()` event handler is also assigned to a method passed
in from a parent widget, allowing user-driven focus changes to propagate back
into the application.
*/
return <input onfocus={this.properties.onFocus} focus={this.shouldFocus} />;
}
}
export default class FocusableWidget extends Focus(WidgetBase) {
private currentlyFocusedKey = 0;
private childCount = 5;
private onFocus(key: number) {
this.currentlyFocusedKey = key;
this.invalidate();
}
/*
Calling `this.focus()` resets the widget so that `this.shouldFocus()` will return true when it is next invoked.
*/
private focusPreviousChild() {
--this.currentlyFocusedKey;
if (this.currentlyFocusedKey < 0) {
this.currentlyFocusedKey = this.childCount - 1;
}
this.focus();
}
private focusNextChild() {
++this.currentlyFocusedKey;
if (this.currentlyFocusedKey === this.childCount) {
this.currentlyFocusedKey = 0;
}
this.focus();
}
protected render() {
/*
The parent widget's `this.shouldFocus()` method is passed to the relevant child element
that requires focus, based on the simple previous/next widget selection logic.
This allows focus to be delegated to a specific child node based on higher-level logic in
a container/parent widget.
*/
return (
<div>
<button onclick={this.focusPreviousChild}>Previous</button>
<button onclick={this.focusNextChild}>Next</button>
<FocusInputChild
key={0}
focus={this.currentlyFocusedKey === 0 ? this.shouldFocus : undefined}
onFocus={() => this.onFocus(0)}
/>
<FocusInputChild
key={1}
focus={this.currentlyFocusedKey === 1 ? this.shouldFocus : undefined}
onFocus={() => this.onFocus(1)}
/>
<FocusInputChild
key={2}
focus={this.currentlyFocusedKey === 2 ? this.shouldFocus : undefined}
onFocus={() => this.onFocus(2)}
/>
<FocusInputChild
key={3}
focus={this.currentlyFocusedKey === 3 ? this.shouldFocus : undefined}
onFocus={() => this.onFocus(3)}
/>
<FocusInputChild
key={4}
focus={this.currentlyFocusedKey === 4 ? this.shouldFocus : undefined}
onFocus={() => this.onFocus(4)}
/>
</div>
);
}
}