如何在模块化用户界面中管理基于事件的输入?

时间:2012-06-02 09:35:56

标签: javascript user-interface event-handling modular

用户界面通常由不同的输入设备组成,如按钮,输入字段,对话框,滑块等。事件顺序通常决定了预期的行为,并且这种行为通常不容易在一个简单的规则中捕获。

是否存在解决此类问题的通用方法?

为了说明界面变得多么复杂,请使用带有3个切换按钮的界面。如果按钮单击的行为取决于每个按钮的状态,则可能有2 ^ 3 * 3 = 24个事件情况。如果行为还取决于事件历史记录,则事件数量会呈指数级增长。

作为一个真实的例子,请看一下我正在研究的所见即所得的文本编辑器。我在编辑器上选择焦点/模糊事件来启用/禁用编辑器。一些按钮(小部件)立即将焦点返回到编辑器,而其他按钮则打开一个对话框。在下图中,箭头显示了单击界面元素时焦点的位置。

我发现在这里管理焦点是一个棘手的问题,经常引入不受欢迎或反直觉的行为。

user interface sketch

3 个答案:

答案 0 :(得分:2)

您可以使用Mediator AKA Message Broker 模式来分离UI组件(通常是任何类型的应用程序组件),这里有两篇关于它的文章:

  1. Mediator Pattern applied to Javascript
  2. Mediator pattern javascript

答案 1 :(得分:1)

正确回答我的问题是答案的一部分。我的问题是一个特殊情况,其中小部件不仅是数据的表示,而且是共享信息的输入设备。

Matteo Migliore建议的事件调度员有不同的用途。在信息流更加线性的情况下,它很有用:一方面可以触发事件的一个或多个对象,另一方面是侦听这些事件的对象。

就我而言,不仅应该集中管理事件,更重要的是,还要管理逻辑。该逻辑的特征在于若干致动器以易于引起循环的方式影响相同的数据源。在我的例子中,这个数据源是:焦点在哪里以及何时激活/取消激活编辑器。

防止循环的解决方案是使用内部状态变量并仔细设计映射,将每个状态+事件组合转换为动作+新状态。基本实现可能如下所示:

switch (eventdescription) {
  case 'click_in_txt':
    switch (state) {
      case 'inactive':
        activate();
        state = 'active';
        break;
      case 'plugin_has_focus';
        close_plugin();
        state = 'active'
        break;
      default:
        console.log('undefined situation ' + state + ' / ' + eventdescription);
    }
  ...
}

这种方法仍然需要一些试验和错误,但很容易看出哪种情况会导致错误,然后您可以单独改变该情况的行为。此外,console.log()函数还会显示您忽略可能导致意外行为的某些事件组合的位置。

decision table for events/state

答案 2 :(得分:0)

我能想象的最通用的方法是绘制一个事件树。此事件树的单个分支的通用表示形式如下:

(start state) -> [event] -> <action> -> [event] -> <action> ...

由于每个点都可能发生不同的事件,因此事件树在观察到的分支越长时呈指数增长。幸运的是,界面通常会返回某个过去的状态,例如关闭对话框后,它将返回到开始状态。

查找和建模这些返回状态是一个创建过程,对每个应用程序都是唯一的。命名每个状态都很有帮助。中间结果是上面概述的状态/事件/动作图。