Как реализовать «mustMatch» и «selectFirst» в jQuery UI Autocomplete? - PullRequest
38 голосов
/ 06 апреля 2010

Недавно я перенес несколько своих плагинов для автозаполнения с одного из bassistance на автозаполнение jQuery UI .

Как реализовать "mustMatch" и "selectFirst", используя только обратные вызовы и другие параметры, без изменения самого кода автозаполнения ядра?

Ответы [ 12 ]

40 голосов
/ 11 апреля 2010

Мне кажется, я решил обе функции ...

Чтобы упростить задачу, я использовал обычный пользовательский селектор:

$.expr[':'].textEquals = function (a, i, m) {
    return $(a).text().match("^" + m[3] + "$");
};

Остальной код:

$(function () {
    $("#tags").autocomplete({
        source: '/get_my_data/',
        change: function (event, ui) {
            //if the value of the textbox does not match a suggestion, clear its value
            if ($(".ui-autocomplete li:textEquals('" + $(this).val() + "')").size() == 0) {
                $(this).val('');
            }
        }
    }).live('keydown', function (e) {
        var keyCode = e.keyCode || e.which;
        //if TAB or RETURN is pressed and the text in the textbox does not match a suggestion, set the value of the textbox to the text of the first suggestion
        if((keyCode == 9 || keyCode == 13) && ($(".ui-autocomplete li:textEquals('" + $(this).val() + "')").size() == 0)) {
            $(this).val($(".ui-autocomplete li:visible:first").text());
        }
    });
});

Если любое из ваших предложений по автозаполнению содержит какие-либо «специальные» символы, используемые regexp, вы должны экранировать эти символы в пределах m [3] в пользовательском селекторе:

function escape_regexp(text) {
  return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
}

и измените пользовательский селектор:

$.expr[':'].textEquals = function (a, i, m) {
  return $(a).text().match("^" + escape_regexp(m[3]) + "$");
};
32 голосов
/ 19 августа 2011

Я использовал что-то простое для mustMatch, и оно работает. Надеюсь, это кому-нибудь поможет.

        change: function (event, ui) {
            if (!ui.item) {
                 $(this).val('');
             }
        }
3 голосов
/ 10 апреля 2010

Я думаю, что получил mustMatch, работающий с этим кодом ... хотя нужно тщательно проверить:

<script type="text/javascript">
    $(function() {
        $("#my_input_id").autocomplete({
            source: '/get_my_data/',
            minChars: 3,
            change: function(event, ui) {
                // provide must match checking if what is in the input
                // is in the list of results. HACK!
                var source = $(this).val();
                var found = $('.ui-autocomplete li').text().search(source);
                console.debug('found:' + found);
                if(found < 0) {
                    $(this).val('');
                }
            }
        });
    });
</script>
2 голосов
/ 27 апреля 2010

Мне показался этот вопрос полезным.

Я решил опубликовать код, который я сейчас использую (адаптировано из Ответ Эстебана Фельдмана ).

Я добавил свой собственный параметр mustMatch и класс CSS, чтобы выделить проблему перед сбросом значения текстового поля.

       change: function (event, ui) {
          if (options.mustMatch) {
            var found = $('.ui-autocomplete li').text().search($(this).val());

            if (found < 0) {
              $(this).addClass('ui-autocomplete-nomatch').val('');
              $(this).delay(1500).removeClass('ui-autocomplete-nomatch', 500);
            }
          }
        }

CSS

.ui-autocomplete-nomatch { background: white url('../Images/AutocompleteError.gif') right center no-repeat; }
1 голос
/ 27 июня 2014

Может быть, это просто потому, что это старая проблема, но я обнаружил, что самое простое решение уже есть в плагине, вам просто нужно использовать соответствующие функции для доступа к нему.

Этот код будет обрабатывать случаи, когда автозаполнение теряет фокус с недопустимым значением:

change: function(e, ui) {
    if (!ui.item) {
        $(this).val("");
    }
}

И этот код, так же, как и исходная функциональность bassistance, будет обрабатывать случаи, когда при автозаполнении нет совпадений:

response: function(e, ui) {
    if (ui.content.length == 0) {
        $(this).val("");
    }
}

Это хорошо работает как с источником статического массива, так и с источником данных JSON. В сочетании с параметром autoFocus: true он, кажется, делает все необходимое эффективным образом.

Последний случай, который вы, возможно, захотите обработать, - это что делать, когда нажата клавиша ESCAPE с недопустимым значением в текстовом поле. Что я делаю, так это использую значение первого соответствующего результата. И вот как я это делаю ...

Сначала объявите переменную, которая будет содержать лучшее совпадение. Делайте это за пределами вашего плагина автозаполнения.

var bestMatch = "";

Затем используйте следующую опцию:

open: function(e, ui) {
    bestMatch = "";

    var acData = $(this).data('uiAutocomplete');
    acData.menu.element.find("A").each(function () {
        var me = $(this);

        if (me.parent().index() == 0) {
            bestMatch = me.text();
        }
    });
}

Наконец, добавьте следующее событие к вашему автозаполнению:

.on("keydown", function(e) {
    if (e.keyCode == 27)        // ESCAPE key
    {
        $(this).val(bestMatch);
    }
})

Вы также можете легко заставить поле быть пустым при нажатии клавиши выхода. Все, что вам нужно сделать, это установить значение пустой строки при нажатии клавиши вместо переменной bestMatch (которая вообще не нужна, если вы решите очистить поле).

1 голос
/ 17 июня 2011

Это официальное демо JQuery-UI имеет mustMatch, среди прочих интересных вещей: http://jqueryui.com/demos/autocomplete/#combobox

Я обновил его, чтобы добавить автозаполнение, и несколько других вещей.1006 *



/* stolen from http://jqueryui.com/demos/autocomplete/#combobox
 *
 * and these options added.
 *
 * - autoFill (default: true):  select first value rather than clearing if there's a match
 *
 * - clearButton (default: true): add a "clear" button
 *
 * - adjustWidth (default: true): if true, will set the autocomplete width the same as
 *    the old select.  (requires jQuery 1.4.4 to work on IE8)
 *
 * - uiStyle (default: false): if true, will add classes so that the autocomplete input
 *    takes a jQuery-UI style
 */
(function( $ ) {
    $.widget( "ui.combobox", {
        options: {
            autoFill: true,
            clearButton: true,
            adjustWidth: true,
            uiStyle: false,
            selected: null,
        },
    _create: function() {
        var self = this,
          select = this.element.hide(),
          selected = select.children( ":selected" ),
          value = selected.val() ? selected.text() : "",
              found = false;
        var input = this.input = $( "" )
                .attr('title', '' + select.attr("title") + '')
        .insertAfter( select )
        .val( value )
        .autocomplete({
            delay: 0,
            minLength: 0,
            source: function( request, response ) {
                var matcher = new RegExp( $.ui.autocomplete.escapeRegex(request.term), "i" );
                        var resp = select.children( "option" ).map(function() {
                    var text = $( this ).text();
                    if ( this.value && ( !request.term || matcher.test(text) ) )
                    return {
                        label: text.replace(
                        new RegExp(
                            "(?![^&;]+;)(?!]*)(" +
                            $.ui.autocomplete.escapeRegex(request.term) +
                            ")(?![^]*>)(?![^&;]+;)", "gi"
                        ), "<strong>$1</strong>" ),
                        value: text,
                        option: this
                    };
                });
                        found = resp.length > 0;
                response( resp );
            },
            select: function( event, ui ) {
                ui.item.option.selected = true;
                self._trigger( "selected", event, {
                    item: ui.item.option
                });
            },
            change: function( event, ui ) {
                if ( !ui.item ) {
                    var matcher = new RegExp( "^" + $.ui.autocomplete.escapeRegex( $(this).val() ) + "$", "i" ),
                    valid = false;
                    select.children( "option" ).each(function() {
                    if ( $( this ).text().match( matcher ) ) {
                        this.selected = valid = true;
                        return false;
                    }
                    });
                    if ( !valid || input.data("autocomplete").term=="" ) {
                    // set to first suggestion, unless blank or autoFill is turned off
                                var suggestion;
                                if(!self.options.autoFill || input.data("autocomplete").term=="") found=false;
                                if(found) {
                                    suggestion = jQuery(input.data("autocomplete").widget()).find("li:first")[0];
                                    var option = select.find("option[text="+suggestion.innerText+"]").attr('selected', true);
                                    $(this).val(suggestion.innerText);
                        input.data("autocomplete").term = suggestion.innerText;
                            self._trigger( "selected", event, { item: option[0] });
                                } else {
                                    suggestion={innerText: ''};
                                    select.find("option:selected").removeAttr("selected");
                                    $(this).val('');
                        input.data( "autocomplete" ).term = '';
                                    self._trigger( "selected", event, { item: null });
                                }
                    return found;
                    }
                }
            }
        });

            if( self.options.adjustWidth ) { input.width(select.width()); }

            if( self.options.uiStyle ) {
                input.addClass( "ui-widget ui-widget-content ui-corner-left" );
            }


        input.data( "autocomplete" )._renderItem = function( ul, item ) {
            return $( "<li></li>" )
                .data( "item.autocomplete", item )
                .append( "" + item.label + "" )
                .appendTo( ul );
        };

        this.button = $( " " )
            .attr( "tabIndex", -1 )
            .attr( "title", "Show All Items" )
            .insertAfter( input )
            .button({
                icons: {
                primary: "ui-icon-triangle-1-s"
                },
                text: false
            })
            .removeClass( "ui-corner-all" )
            .addClass( "ui-corner-right ui-button-icon" )
            .click(function() {
                // close if already visible
                if ( input.autocomplete( "widget" ).is( ":visible" ) ) {
                input.autocomplete( "close" );
                return;
                }

                // work around a bug (likely same cause as #5265)
                $( this ).blur();

                // pass empty string as value to search for, displaying all results
                input.autocomplete( "search", "" );
                input.focus();
            });

            if( self.options.clearButton ) {
            this.clear_button = $( " " )
                .attr( "tabIndex", -1 )
                .attr( "title", "Clear Entry" )
                .insertAfter( input )
                .button({
                    icons: {
                    primary: "ui-icon-close"
                    },
                    text: false
                })
                .removeClass( "ui-corner-all" )
                .click(function(event, ui) {

                        select.find("option:selected").removeAttr("selected");
                        input.val( "" );
                    input.data( "autocomplete" ).term = "";
                        self._trigger( "selected", event, { item: null });

                    // work around a bug (likely same cause as #5265)
                    $( this ).blur();
                });
            }

    },

    destroy: function() {
        this.input.remove();
        this.button.remove();
        this.element.show();
        $.Widget.prototype.destroy.call( this );
    }
    });
})( jQuery );

CSS (.hjq-combobox - это интервал переноса)

.hjq-combobox .ui-button { margin-left: -1px; }
.hjq-combobox .ui-button-icon-only .ui-button-text { padding: 0; }
.hjq-combobox button.ui-button-icon-only { width: 20px; }
.hjq-combobox .ui-autocomplete-input { margin-right: 0; }
.hjq-combobox {white-space: nowrap;}

Примечание: этот код обновляется и поддерживается здесь: https://github.com/tablatom/hobo/blob/master/hobo_jquery_ui/vendor/assets/javascripts/combobox.js

1 голос
/ 10 июня 2011

Я обнаружил одну проблему.Пока список предложений активен, вы можете отправить свою форму, даже если значение не соответствует предложению.Чтобы отменить это, я добавил:

$('form').submit(function() {
        if ($(".ui-autocomplete li:textEquals('" + $(this).val() + "')").size() == 0) {
            $(this).val('');
            $("span").text("Select a valid city").show();
            return false;
        }
});

Это предотвращает отправку формы и отображает сообщение.

1 голос
/ 11 мая 2011

Решение, которое я использовал для реализации mustMatch:

<script type="text/javascript">
...

$('#recipient_name').autocomplete({
    source: friends,
    change: function (event, ui) {
        if ($('#message_recipient_id').attr('rel') != $(this).val()) {
            $(this).val('');
            $('#message_recipient_id').val('');
            $('#message_recipient_id').attr('rel', '');
        }
    },
    select: function(event, ui) {
        $('#message_recipient_id').val(ui.item.user_id);
        $('#message_recipient_id').attr('rel', ui.item.label);
    }
}); 

...
</script>
0 голосов
/ 08 декабря 2015

Поздний ответ, но может помочь кому-то!

С учетом двух событий в виджете автозаполнения

1) изменение - срабатывает, когда поле размыто и значение изменяется.

2) ответ - срабатывает после завершения поиска и отображения меню.

Изменить события изменения и ответа следующим образом:

change : function(event,ui)
{  
if(!ui.item){
$("selector").val("");
}
},

response : function(event,ui){
if(ui.content.length==0){
  $("selector").val("");
}
}

Надеюсь, это поможет!

0 голосов
/ 24 сентября 2014

На основании принятого ответа:

Мои дополнительные требования: несколько автозаполнений , ненавязчивая проверка ошибок .

change: function () {
    var target = $(this),
        widget = target.autocomplete('widget'),
        cond = widget.find('li:textEquals("' + target.val() + '")').length === 0;

    target.toggleClass('input-validation-error', cond);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...