Does jquery UI Autocomplete support restrict typing on invalid values where supporting multiple values?

烈酒焚心 提交于 2019-12-06 07:48:25

问题


I am taking over a new website and it was using an old deprecated version of the jquery autocomplete plugin. I am trying to recreate the functionality using the latest jquery ui autocomplete and there is one feature that i can't seem to replicate.

I am trying to replicate the "mustMatch" functionality in cases where there are multiple values allows.

so basically, if i start typing any test that doesn't show up in any of the search results (even partial string search), it resets the entry for that field (instead of letting me type garbage that is not in the list of valid choices)

So lets say my list (local) is {"Bird", "Song", "Happy"}

It will let me type

 Bird, Son

but if i type z after that it stays on

    Bird, Son

to let me know that is an invalid entry

Is this possible to do either in jquery ui autocomplete?

I see a lot of old posts from google asking similar questions and answers like this one, but none seems to work with multiple values (and many don't seem to work at all :( )


回答1:


You could use this kind of snippet:

{ I'm using here keyup event to check, but on modern browsers, you could use input (oninput) event instead or bind onpaste event too }

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;
     }
 });



回答2:


I mostly copied from the jQuery UI multi-select example, but made a few changes. The goal was to work exactly how you described and that this could handle any method of input: appending to the string, inserting into the string, and copying and pasting.

The two keys to modify the multi example to meet your needs, where the creation of custom filters and adding to the source method. Originally I changed the search method, but source gave me more control on how to display the choices (implement min length and keep showing after the last term was trimmed).

When the source method is executed, which seems to get fired during all manner of input types (typing, pasting, cutting), I split the inputs and check each input for validity. I check each because if somebody pasted text, then something in the middle might become invalid where it was valid before. Anything before the last term gets the exact filter applied while the last element gets the from start filter applied. The last term is also treated differently in that it is trimmed to the point the unmatched input occurs.

After that I update the input value if any changes have occurred. I then display the response to the lastTerm, taking into account the minLength value, which even the original multi example forgot to do.

I believe my solution is the best possible as it handles all methods of input and is simple in that it only adds to one function from the original example. The one downside is that there are some inefficiencies created to keep the solution simple, but these are so minor as to not cause any noticeable performance effects.

additional ideas: One other idea, would be change the split regex to /,?\s*/ so that the comma was optional. In my testing it was natural to type space after each response. Another would be to update the input value every time so the comma spacing is consistent.

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;
    }
});


来源:https://stackoverflow.com/questions/17133687/does-jquery-ui-autocomplete-support-restrict-typing-on-invalid-values-where-supp

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!