如何让 "click" 和 "hasFocus" 事件同时触发?
How to make "click" and "hasFocus" events firing at same time work together?
我正在创建一个由单个输入字段组成的淘汰组件,可以在其中搜索用户。
我正在使用 click
事件从列表中获取选定的用户,我想做一些事情,比如隐藏列表,如果输入失去焦点(也就是他们点击了其他地方)。
问题是,当我单击列表中的一个元素时,hasFocus
事件会触发,并且不知何故会阻止 click
事件触发。
我不明白为什么,因为他们是兄弟姐妹,而不是父子关系。
我已经设法通过在 hasFocus
的订阅函数中添加超时来使其工作,但我认为这不是正确的方法。
这是唯一的方法吗?让你的代码工作的超时是不好的做法,对吧?
这是一个有效的 fiddle:http://jsfiddle.net/57kxyud9/
- 事实上,只有
click
事件被启用并且它工作正常。
- 您可以启用
hasFocus
事件订阅,您可能会注意到它停止保存用户信息。
- 然后您可以在
hasFocus.subscribe
中启用超时,程序再次运行。
我是否也应该在此 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>
我猜 focus
在 click
事件之前被触发。这就是 matches
在被点击之前被隐藏的原因。因此,我们可以添加 200 秒的 rateLimit
。这基本上是指示 knockout 等待 200 毫秒,以便 click
事件在再次计算 show_matchingUsers
之前完成。
我正在创建一个由单个输入字段组成的淘汰组件,可以在其中搜索用户。
我正在使用 click
事件从列表中获取选定的用户,我想做一些事情,比如隐藏列表,如果输入失去焦点(也就是他们点击了其他地方)。
问题是,当我单击列表中的一个元素时,hasFocus
事件会触发,并且不知何故会阻止 click
事件触发。
我不明白为什么,因为他们是兄弟姐妹,而不是父子关系。
我已经设法通过在 hasFocus
的订阅函数中添加超时来使其工作,但我认为这不是正确的方法。
这是唯一的方法吗?让你的代码工作的超时是不好的做法,对吧?
这是一个有效的 fiddle:http://jsfiddle.net/57kxyud9/
- 事实上,只有
click
事件被启用并且它工作正常。 - 您可以启用
hasFocus
事件订阅,您可能会注意到它停止保存用户信息。 - 然后您可以在
hasFocus.subscribe
中启用超时,程序再次运行。
我是否也应该在此 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>
我猜 focus
在 click
事件之前被触发。这就是 matches
在被点击之前被隐藏的原因。因此,我们可以添加 200 秒的 rateLimit
。这基本上是指示 knockout 等待 200 毫秒,以便 click
事件在再次计算 show_matchingUsers
之前完成。