【问题标题】:How to make "click" and "hasFocus" events firing at same time work together?如何使“click”和“hasFocus”事件同时触发?
【发布时间】:2019-01-21 13:10:55
【问题描述】:

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

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

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

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

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

  • 事实上,只有click 事件被启用并且工作正常。
  • 您可以启用hasFocus 事件订阅,您可能会注意到它会停止保存用户信息。
  • 然后您可以在hasFocus.subscribe 中启用超时,程序再次运行。

我还应该在这篇文章中嵌入代码 sn-ps 吗?

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

【问题讨论】:

    标签: knockout.js


    【解决方案1】:

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

    例如,

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

    因此,您可以将这些设置为 computed observables:

    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

    【讨论】:

    • 感谢您的回答。虽然这确实有效,但恐怕它不符合我的需要。不幸的是,当输入失去焦点时,我需要做更多的事情,我只是跳过了问题中的那一部分,因为我认为它们没有必要。我应该扩展/更新我的问题还是应该创建一个新问题?
    • @oust 请更新小提琴。我觉得你仍然可以在 self.input_hasFocus.subscribe() 里面做所有这些事情
    • 抱歉耽搁了,我试图根据您的提示创建一个工作小提琴并最终解决了问题!感谢您的帮助:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-10-27
    • 1970-01-01
    • 2023-03-23
    • 2012-02-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多