Knockout自动完成实现

时间:2017-10-25 20:04:12

标签: javascript jquery html knockout.js autocomplete

我现在正在为输入中的项目实现我自己的简单自动完成功能。一切运行良好,停止输入脚本后500ms做AJAX调用并从后端接收建议。我也在select中输入以下输入。

问题在于我不知道如何控制弹出窗口的可见性。当我在输入模糊时隐藏它,我甚至无法点击它,因为输入失去焦点并且弹出窗口立即被隐藏。更有问题的是,弹出窗口显示在Knockout的foreach / template组合中,因此有多个输入由多个视图模型控制。这使得使用jQuery更难以跟踪输入+弹出区域之外的点击。

代码:

HTML(试图跟踪div的焦点,不成功):

"0"^^<http://www.w3.org/2001/XMLSchema#integer>

相关JS,项目视图模型的一部分:

<div class="form-group">
    <label>Name</label>
    <div class="autocomplete-container" tabindex="0" data-bind="hasFocus: nameFocused">
        <input type="text" class="form-control" placeholder="Name" data-bind="textInput: name, event: { change: changed }">
        <select class="autocomplete" size="5" data-bind="visible: showSuggestions, options: ingredientSuggestions, optionsText: 'name', value: selectedSuggestion"></select>
    </div>                    
    <span class="label label-danger" data-bind="validationMessage: name"></span>
</div>

当输入弹出窗口具有焦点时,我尝试保持弹出窗口可见。但是,当我尝试单击弹出窗口时,输入失去焦点“更快”,弹出窗口隐藏,因此没有点击也没有焦点。

我应该如何处理隐藏建议弹出窗口?

1 个答案:

答案 0 :(得分:0)

我的建议是在showSuggestions中合并三个值:

  1. 有什么建议
  2. 搜索字段是否为焦点
  3. 建议选择框是否为焦点
  4. 当切换焦点时,会有一个短暂的时刻,其中 nothing 处于焦点,隐藏建议框并禁止它有焦点。这就是为什么你可能需要计算可见度的小超时。

    这是一个显示行为的工作示例:

    &#13;
    &#13;
    const data = getData();
    
    const query = ko.observable("");
    const suggestions = ko.pureComputed(
      () => query().length
        ? data.filter(countryMatch(query().toUpperCase()))
        : []
    );
      
    
    const queryFocus = ko.observable(false)
    const selectFocus = ko.observable(false);
    
    
    const suggestionsAvailable = ko.pureComputed(
      () => !!suggestions().length
    );
    
    // The relevant part:
    const showSuggestions = ko.pureComputed(
      () => suggestionsAvailable() && 
            (queryFocus() || selectFocus())
    ).extend({ deferred: true, rateLimit: 100 });
    
    
    ko.applyBindings({ query, queryFocus, selectFocus, suggestions })
    
    
    function countryMatch(q) {
      return c => [c.name, c.code]
        .join(" ")
        .toUpperCase()
        .includes(q);
    };
    
    function getData() { 
      return [{"name":"Afghanistan","code":"AF"},{"name":"Argentina","code":"AR"},{"name":"Belarus","code":"BY"},{"name":"Brazil","code":"BR"},{"name":"Cayman Islands","code":"KY"},{"name":"Congo, The Democratic Republic of the","code":"CD"},{"name":"Dominica","code":"DM"},{"name":"Faroe Islands","code":"FO"},{"name":"Germany","code":"DE"},{"name":"Guinea","code":"GN"},{"name":"India","code":"IN"},{"name":"Jersey","code":"JE"},{"name":"Latvia","code":"LV"},{"name":"Madagascar","code":"MG"},{"name":"Mayotte","code":"YT"},{"name":"Namibia","code":"NA"},{"name":"Niue","code":"NU"},{"name":"Paraguay","code":"PY"},{"name":"Russian Federation","code":"RU"},{"name":"Saudi Arabia","code":"SA"},{"name":"South Africa","code":"ZA"},{"name":"Syrian Arab Republic","code":"SY"},{"name":"Tunisia","code":"TN"},{"name":"United States Minor Outlying Islands","code":"UM"},{"name":"Yemen","code":"YE"}];
    };
    &#13;
    <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
    
    <input type="search" data-bind="textInput: query,
                                    hasFocus: queryFocus"/>
    <select size="5" data-bind="options: suggestions,
                                optionsText: 'name',
                                hasFocus: selectFocus,
                                visible: showSuggestions">
    &#13;
    &#13;
    &#13;