3

我正在接管一个新网站,它使用的是 jquery 自动完成插件的旧版本,已弃用。我正在尝试使用最新的 jquery ui 自动完成功能重新创建功能,并且我似乎无法复制一项功能。

在有多个值的情况下,我试图复制“mustMatch”功能。

所以基本上,如果我开始输入任何没有出现在任何搜索结果中的测试(甚至是部分字符串搜索),它会重置该字段的条目(而不是让我输入不在有效列表中的垃圾选择)

所以假设我的列表(本地)是 {"Bird", "Song", "Happy"}

它会让我打字

 Bird, Son

但是如果我在那之后输入z,它就会保持打开状态

    Bird, Son

让我知道这是一个无效条目

这可以在 jquery ui 自动完成中做到吗?

我看到很多来自谷歌的旧帖子都在询问类似的问题和答案,但似乎没有一个适用于多个值(而且许多似乎根本不起作用:()

4

2 回答 2

6

您可以使用这种片段:

{ 我在这里使用 keyup 事件来检查,但在现代浏览器上,你可以使用 input (oninput) 事件代替或绑定 onpaste 事件}

http://jsfiddle.net/q2SSF/

 var availableTags = [
     "Bird",
     "Son",
     "Happy"];

 function split(val) {
     return val.split(/,\s*/);
 }

 function checkAvailable(term) {
     var length = term.length,
         chck = false,
         term = term.toLowerCase();
     for (var i = 0, z = availableTags.length; i < z; i++)
     if (availableTags[i].substring(0, length).toLowerCase() === term) return true;


     return false;
 }

 function extractLast(term) {
     return split(term).pop();
 }

 var $autocomplete = $("#autocomplete")
 // don't navigate away from the field on tab when selecting an item
 .on("keydown", function (event) {
     if (event.keyCode === $.ui.keyCode.TAB && $(this).data("ui-autocomplete").menu.active) {
         event.preventDefault();
     }
 })
     .on("keyup", function (event) {
     var ac_value = this.value;
     if (!checkAvailable(extractLast(ac_value))) this.value = ac_value.substring(0, ac_value.length - 1);
 })
     .autocomplete({
     minLength: 0,
     source: function (request, response) {
         // delegate back to autocomplete, but extract the last term
         response($.ui.autocomplete.filter(
         availableTags, extractLast(request.term)));
     },
     focus: function () {
         // prevent value inserted on focus
         return false;
     },
     select: function (event, ui) {
         var terms = split(this.value);
         // remove the current input
         terms.pop();
         // add the selected item
         terms.push(ui.item.value);
         // add placeholder to get the comma-and-space at the end
         terms.push("");
         this.value = terms.join(", ");
         return false;
     }
 });
于 2013-06-19T11:57:31.320 回答
3

我主要从jQuery UI 多选示例中复制,但做了一些更改。目标是完全按照您描述的方式工作,并且可以处理任何输入方法:附加到字符串、插入字符串以及复制和粘贴。

这两个键修改多示例以满足您的需要,其中创建自定义过滤器和添加到源方法。最初我更改了搜索方法,但源让我更多地控制如何显示选择(实现最小长度并在最后一个术语被修剪后继续显示)。

当执行源方法时,似乎在所有输入类型(键入、粘贴、剪切)中都会触发,我拆分输入并检查每个输入的有效性。我检查每一个,因为如果有人粘贴文本,那么中间的某些内容可能会在之前有效的地方变得无效。最后一个术语之前的任何内容都应用了确切的过滤器,而最后一个元素应用了从开始过滤器。最后一项的处理方式也不同,因为它被修剪到出现不匹配输入的点。

之后,如果发生任何更改,我会更新输入值。然后我显示对 lastTerm 的响应,考虑到 minLength 值,即使是原始的多示例也忘记了。

我相信我的解决方案是最好的,因为它可以处理所有输入方法,并且很简单,因为它只添加了原始示例中的一个函数。一个缺点是,为了保持解决方案的简单性而创建了一些低效率,但这些都非常小,不会导致任何明显的性能影响。

其他想法:另一个想法是将拆分正则表达式更改为 /,?\s*/ 以便逗号是可选的。在我的测试中,每次响应后输入空格是很自然的。另一种方法是每次都更新输入值,以使逗号间距保持一致。

jsFiddle

var availableTags = ['Bird', 'Song', 'Happy'];
function split(val) {
    return val.split(/,\s*/);
}

// removes the last term from the array, and adds newValue if given
function removeLastTerm(val, newValue) {
    var terms = split(val);
    terms.pop();
    if (newValue) {
        terms.push(newValue);
    }
    terms.push('');
    return terms.join(', ');;
}

// filter from start position from:
// http://blog.miroslavpopovic.com/jqueryui-autocomplete-filter-words-starting-with-term
function filterFromStart(array, term) {
    var matcher = new RegExp('^' + $.ui.autocomplete.escapeRegex(term), 'i');
    return $.grep(array, function (value) {
        return matcher.test(value.label || value.value || value);
    });
}
function filterExact(array, term) {
    var matcher = new RegExp('^' + $.ui.autocomplete.escapeRegex(term) + '$', 'i');
    return $.grep(array, function (value) {
        return matcher.test(value.label || value.value || value);
    });
}
$('#tags')
// don't navigate away from the field on tab when selecting an item
.bind('keydown', function (event) {
    if (event.keyCode === $.ui.keyCode.TAB &&
    $(this).data('ui-autocomplete').menu.active) {
        event.preventDefault();
    }
})
.autocomplete({
    minLength: 0,
    delay: 0,
    source: function (request, response) {
        var terms = split(request.term),
            lastTrimmed = false,
            lastTerm,
            originalMaxIndex = terms.length - 1,
            filteredMaxIndex;

        if (originalMaxIndex >= 0) {
            // remove any terms that don't match exactly
            for (var i = originalMaxIndex - 1; i >= 0; i--) {
                if (filterExact(availableTags, terms[i]).length == 0) {
                    terms.splice(i, 1);
                }
            }

            filteredMaxIndex = terms.length - 1;
            // trim the last term until it matches something or is emty
            lastTerm = terms[filteredMaxIndex];
            while (lastTerm.length != 0 &&
                filterFromStart(availableTags, lastTerm).length == 0) {
                lastTerm = lastTerm.substr(0, lastTerm.length - 1);
                lastTrimmed = true;
            }

            if (lastTrimmed) {
                // add modified LastTerm or reduce terms array
                if (lastTerm.length == 0) {
                    terms.splice(filteredMaxIndex--, 1);
                    terms.push('');
                }
                else terms[filteredMaxIndex] = lastTerm;
            }

            if (filteredMaxIndex >= 0) {
                // only execute if we've removed something
                if (filteredMaxIndex < originalMaxIndex || lastTrimmed) {
                    this.element.val(terms.join(', '));
                }
            } else {
                this.element.val(request.term);
            }

            if (this.options.minLength <= lastTerm.length) {
                response(filterFromStart(availableTags, lastTerm));
            }
            else {
                response([]);
            }
        }
        else {
            response(filterFromStart(availableTags, ''));
        }

    },
    focus: function () {
        // prevent value inserted on focus
        return false;
    },
    select: function (event, ui) {
        // add the selected value to the input.
        this.value = removeLastTerm(this.value, ui.item.value);
        return false;
    }
});
于 2013-06-23T12:46:57.410 回答