如何使“ click”和“ hasFocus”事件同时触发?

时间:2019-01-21 13:10:55

标签: knockout.js

我正在创建一个由单个输入字段组成的敲除组件,您可以在其中搜索用户。 我正在使用click事件从列表中获取选定的用户,并且如果输入失去焦点(也就是他们单击了其他位置),我想做一些事情,例如隐藏列表。 / p>

问题是,当我单击列表中的某个元素时,会触发hasFocus事件,并且以某种方式阻止了click事件的触发。 我不明白为什么,因为它们是兄弟姐妹,而不是父子事件。

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

这是唯一的方法吗?有这样的超时来使代码正常工作是不好的做法,对吧?

这是一个工作的小提琴:http://jsfiddle.net/57kxyud9/

  • 按原样,仅启用click事件,它可以正常工作。
  • 您可以启用hasFocus事件订阅,您可能会注意到它停止保存用户的信息。
  • 然后您可以在hasFocus.subscribe内启用超时,程序将再次运行。

我还应该在本文中嵌入代码段吗?

即使在input_hasFocus.subscribe处于活动状态的情况下,我也希望能够保存用户。

1 个答案:

答案 0 :(得分:1)

添加setTimeout本身来更改subscribe的顺序没有错。因为rateLimit是淘汰赛中的有效概念。但是,一个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>

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

Updated fiddle