支持交互
事件监听器
在实例化节点时,为虚拟节点指定事件监听器的方法与指定任何其他属性的方法相同。当输出 VNode
时,VNodeProperties
上事件监听器的名字会镜像到 HTMLElement
的等价事件上。虽然自定义部件的作者可以根据自己的选择命名事件,但通常也遵循类似的 onEventName
的命名约定。
函数属性(如事件处理程序)会自动绑定到实例化此虚拟节点的部件的 this
上下文。但是,如果将已绑定的函数传给属性值,将不会重复绑定给 this
。
处理 focus
输出 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
基于函数的部件可使用 focus
中间件为其子部件设置焦点,或者接受来自父部件的焦点。基于类的部件可使用 FocusMixin
(来自 @dojo/framework/core/mixins/Focus
)以相同的方式委托 focus。
FocusMixin
会给部件的类中添加一个 this.shouldFocus()
方法,而基于函数的部件使用 focus.shouldFocus()
中间件方法实现相同的目的。此方法会检查部件是否处于执行了获取焦点的状态(译注:即调用了 this.focus()
),并且仅对单个调用返回 true
,直到再次调用部件的 this.focus()
方法(基于函数的部件使用等价的 focus.focus()
)。
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>
);
}
}