最近收到一个需求,需要参(抄)考(袭)传统盯盘软件,当用户处于行情页面时,无需点击”输入代码“之类的<input>,只要在键盘按下一个键,就有一个股票代码搜索提示弹出来,供用户选中需要查看的股票。

这样的需求分析一下,会发现有些麻烦,前端需要判断当前页面没有弹出的<prompt>/<confirm>,没有正在输入的<input>/<textarea>;换个说法是,前端需要判断当前用户交互的焦点,需要知道用户的确是在盯盘。

HTML Standard 有个章节是讲 Focus 的,并且提供了一个相关的document.activeElement,利用这个API,可以知道用户正在交互的元素。但是现在的页面开发不是基于HTMLElement,而是基于某某框架的组件,问题是好像并没有哪个框架会提供app.activeComponent这样的API。

传统的 Windows GUI 开发中,有两个概念——foregroundactive。传统的窗口叠窗口模式里,很容易区分用户的交互焦点,因为最前面的就是active的;而foreground/background是和线程有关。在浏览器单线程世界里,任务的执行没有foreground/background之分,,但是UI里,是可以有foreground/background组件。比如,弹出的对话框,天猫右滑出来的购物车组建,对于整个页面来说,是foreground。我想,z-index是判断foreground/background的依据。然后像<input>,一般z-index不变,但用户可以通过鼠标点击,实现focus/blur,从而影响倒页面的活动元素。

目前项目里的代码,只判断了document.activeElement这种情况,也就是只考虑了active,但是foreground是没有处理的,所以往后应该潜在的问题。

今天稍微想了一下(顺便水一篇),对于比较大型的应用来说,需要一个FocusTrackingService。我本来想在VSCode源码里查查,发现他们的方案也是挺依赖原生的focus属性,也算一种方案吧。不过一种通用的和平台无关的设计,应该是,焦点是可以让渡的。比如支付宝密码数据,六位数字,显示成六个输入的格子,当前面一位输入完成后,后一位会自动聚焦。这样的组件如果用操作DOM元素的思路来做,input后blur前一个input然后focus下一个元素。显然,这样的写法相当底层,不太符合面向xx编程的原则。

我会给需要可以焦点切换的组件,实现这样的interface

1
2
3
4
interface Focusable{
deliverFocus(): void
acceptFocus(): void
}

其中,deliverFocus()是,当组件不需要focus时,自行让出焦点。比如说,对话框都会有cancel()dismiss(),这两个方法对于其他组件来说,是让出焦点。应用内,应该需要有一个FocusTracker,跟踪当前聚焦的组件,以及计算让渡焦点后,下一个应该聚焦的组件(如果有的话)。下一个聚焦组件的acceptFocus()会由FocusTrackerz自动调用,从而实现支付宝密码输入时的切换效果。