如何让 "click" 和 "hasFocus" 事件同时触发?

How to make "click" and "hasFocus" events firing at same time work together?

我正在创建一个由单个输入字段组成的淘汰组件,可以在其中搜索用户。 我正在使用 click 事件从列表中获取选定的用户,我想做一些事情,比如隐藏列表,如果输入失去焦点(也就是他们点击了其他地方)。

问题是,当我单击列表中的一个元素时,hasFocus 事件会触发,并且不知何故会阻止 click 事件触发。 我不明白为什么,因为他们是兄弟姐妹,而不是父子关系。

我已经设法通过在 hasFocus 的订阅函数中添加超时来使其工作,但我认为这不是正确的方法。

这是唯一的方法吗?让你的代码工作的超时是不好的做法,对吧?

这是一个有效的 fiddle:http://jsfiddle.net/57kxyud9/

我是否也应该在此 post 中嵌入代码片段?

即使 input_hasFocus.subscribe 处于活动状态,我也希望能够在单击它时保存用户。

添加一个 setTimeout 来改变 subscribe 本身的顺序并没有错。因为 rateLimit 在 knockout 中是一个有效的概念。但是,一个 subscribe 更新另一个 observable,这反过来触发它 subscribe 等等,似乎没有必要。因此,如果您想要一个从其他可观察对象派生的 observable,您可以将其设为 computed 属性 而不是从它所依赖的所有可观察对象的订阅者更新它的值。

例如,

  • show_matchingUsers 取决于 input_hasFocus 以及是否生成任何 selection
  • matches 完全依赖于 selection

因此,您可以将这些作为 computed 可观察对象:

var vm = function() {
  var self = this;

  self.users = [{"username":ko.observable("Alice")},{"username":ko.observable("Bob")},{"username":ko.observable("User 3")},{"username":ko.observable("User 4")},{"username":ko.observable("User 5")}];

  self.selection = ko.observable();
  self.chosenUser = ko.observable();
  self.input_hasFocus = ko.observable(false);
  self.matches = ko.observableArray([]);
  self.show_matchingUsers = ko.observable(false);

  // gets computed every time "selection" cahnges
  self.matches = ko.computed(function() {
    let matches = [],
      val = self.selection();

    if (!val) {
      return matches;
    }
    /*for each item in the array...*/
    for (i = 0; i < self.users.length; i++) {
      /*check if the item starts with the same letters as the text field value:*/
      if (self.users[i].username().substr(0, val.length).toUpperCase() == val.toUpperCase()) {
        matches.push(self.users[i]);
      }
    }

    return matches;
  })

  // gets computed every time "input_hasFocus" and "selection" cahnges
  self.show_matchingUsers = ko.computed(function() {
    return self.input_hasFocus() && self.selection()
  }).extend({ rateLimit: 200 });

  self.select_thisUser = function(u) {
    self.chosenUser(u);
    self.selection(u.username());
  };
}

ko.applyBindings(new vm());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<br/> Selected User:
<!-- ko with:chosenUser -->
<span data-bind="text:username"></span>
<!-- /ko -->

<hr style="border-color:white;background-color:white;color:white;border-style:solid;" />

<div class="autocomplete">
  <input data-bind="textInput: selection, hasFocus:input_hasFocus" autocomplete="off" type="search" placeholder="Search" />
  <div data-bind="visible:show_matchingUsers,foreach:matches" class="autocomplete-items">
    <div data-bind="click:function(){$parent.select_thisUser(this);}">
      <span data-bind="text:username"></span>
    </div>
  </div>
</div>

我猜 focusclick 事件之前被触发。这就是 matches 在被点击之前被隐藏的原因。因此,我们可以添加 200 秒的 rateLimit。这基本上是指示 knockout 等待 200 毫秒,以便 click 事件在再次计算 show_matchingUsers 之前完成。

Updated fiddle