jQuery UI自动完成下拉框带有分类。

12

我正在使用jquery ui自动完成组合框,它工作得很好,但现在我有点贪心。我想能够向其中添加类别。如果我添加了类别(如下面的示例),生成的组合框将基于菜单,并且与jquery ui自动完成类别版中的类别显示相同的HTML标签。

<select>
<optgroup name="Cat 1">    
<option value="1a">One A</option>
<option value="1b">One B</option>
<option value="1c">One C</option>
</optgroup>
<optgroup name="Cat 2">    
<option value="2a">Two A</option>
<option value="2b">Two B</option>
<option value="2c">Two C</option>
</optgroup>
</select>

我创建了一个http://jsfiddle.net/nH3b6/11/

感谢任何帮助或指导。

4个回答

11

在 @Jarry 的建议基础上,我会更新你的代码以确定 option 属于哪个 optgroup。然后,您可以使用与 jQueryUI 网站上找到的类似代码:

(function($) {
    $.widget("ui.combobox", {
        _create: function() {
            var input, self = this,
                select = this.element.hide(),
                selected = select.children(":selected"),
                value = selected.val() ? selected.text() : "",
                wrapper = this.wrapper = $("<span>").addClass("ui-combobox").insertAfter(select);

            input = $("<input>").appendTo(wrapper).val(value).addClass("ui-state-default ui-combobox-input").autocomplete({
                delay: 0,
                minLength: 0,
                source: function(request, response) {
                    var matcher = new RegExp($.ui.autocomplete.escapeRegex(request.term), "i");

                    response(select.find("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,
                            category: $(this).closest("optgroup").attr("label")
                        };
                        //MK
                        $('#test').attr('style', 'display: none;');
                    }).get());
                },
                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) {
                            $('#test').attr('style', 'display: block;');
                            // remove invalid value, as it didn't match anything
                            //$( this ).val( "" );
                            //select.val( "" );
                            //input.data( "autocomplete" ).term = "";
                            //return false;                    
                        }
                    }
                }
            }).addClass("ui-widget ui-widget-content ui-corner-left");

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

            input.data("autocomplete")._renderMenu = function(ul, items) {
                var self = this,
                    currentCategory = "";
                $.each(items, function(index, item) {
                    if (item.category != currentCategory) {
                        if (item.category) {
                            ul.append("<li class='ui-autocomplete-category'>" + item.category + "</li>");
                        }
                        currentCategory = item.category;
                    }
                    self._renderItem(ul, item);
                });
            };

            $("<a>").attr("tabIndex", -1).attr("title", "Show All Items").appendTo(wrapper).button({
                icons: {
                    primary: "ui-icon-triangle-1-s"
                },
                text: false
            }).removeClass("ui-corner-all").addClass("ui-corner-right ui-combobox-toggle").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();
            });
        },

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

$(function() {
    $("#combobox").combobox();
    $("#toggle").click(function() {
        $("#combobox").toggle();
    });
});

Example: http://jsfiddle.net/gB32r/


1
你可能想要将所有对 children 的调用更改为 find。这样,在 postbacks(在 ASP.NET 的情况下)和其他情况下它都能正常工作。我本来想改变答案,但我认为公司网络正在阻止 ajax 或其他东西。此外,根据我的经验,您不必更改 _renderItem 方法。 - Peter
@AndrewWhitaker,如何在最新版本的jQuery上实现相同的功能?http://jsfiddle.net/ZeNz0r/K5q3U/1/ 将库添加到资源面板而不是HTML“head”中。jQuery 1.10.2,jQueryUI 1.10.4 - Fernando Silva
3
@FernandoSilva:嘿,Fernando -- 以下更新应该可行:http://jsfiddle.net/K5q3U/2/。较新版本的jQueryUI使用其他数据名称`uiAutocomplete`和`ui-autocomplete-item`。 - Andrew Whitaker
@AndrewWhitaker 太好了!我怎么会花整个下午查看文档并尝试重写代码,却错过了这个。谢谢,现在我只需要调整 CSS 就可以开始了。 - Fernando Silva
@Andrew Whitaker,当我尝试升级到jQuery 1.11和jQuery UI 1.11时,这个功能停止工作了,有什么想法吗?菜单改变了吗? - NinaNa
显示剩余5条评论

3

jQuery 10有一些特性。我从jQuery UI的网站上获取了自动完成组合框:http://jqueryui.com/autocomplete/#combobox,并将其与Andrew Whitaker的答案进行了整合。

(function( $ ) {
$.widget( "custom.combobox_with_optgroup", {
    _create: function() {
        this.wrapper = $( "<span>" )
            .addClass( "custom-combobox" )
            .insertAfter( this.element );
        this.element.hide();
        this._createAutocomplete();
        this._createShowAllButton();
    },
    _createAutocomplete: function() {
        var selected = this.element.find( ":selected" ),
            value = selected.val() ? selected.text() : "";
        this.input = $( "<input>" )
            .appendTo( this.wrapper )
            .val( value )
            .attr( "title", "" )
            .addClass( "custom-combobox-input ui-widget ui-widget-content ui-state-default ui-corner-left" )
            .autocomplete({
                delay: 0,
                minLength: 0,
                source: $.proxy( this, "_source" )
            })
            .tooltip({
                tooltipClass: "ui-state-highlight"
            });
        this._on( this.input, {
            autocompleteselect: function( event, ui ) {
                ui.item.option.selected = true;
                this._trigger( "select", event, {
                    item: ui.item.option
                });
            },
            autocompletechange: "_removeIfInvalid"
        });

        this.input.data("uiAutocomplete")._renderMenu = function(ul, items) {
            var self = this,
                currentCategory = "";
            $.each(items, function(index, item) {
                if (item.category != currentCategory) {
                    if (item.category) {
                        ul.append("<li class='custom-autocomplete-category'>" + item.category + "</li>");
                    }
                    currentCategory = item.category;
                }
                self._renderItemData(ul, item);
            });
        };
    },
    _createShowAllButton: function() {
        var input = this.input,
            wasOpen = false;
        $( "<a>" )
            .attr( "tabIndex", -1 )
            .attr( "title", "Показать все" )
            .tooltip()
            .appendTo( this.wrapper )
            .button({
                icons: {
                    primary: "ui-icon-triangle-1-s"
                },
                text: false
            })
            .removeClass( "ui-corner-all" )
            .addClass( "custom-combobox-toggle ui-corner-right" )
            .mousedown(function() {
                wasOpen = input.autocomplete( "widget" ).is( ":visible" );
            })
            .click(function() {
                input.focus();

                if ( wasOpen ) {
                    return;
                }

                input.autocomplete( "search", "" );
            });
    },
    _source: function( request, response ) {
        var matcher = new RegExp( $.ui.autocomplete.escapeRegex(request.term), "i" );
        response( this.element.find( "option" ).map(function() {
            var text = $( this ).text();
            if ( this.value && ( !request.term || matcher.test(text) ) )
                return {
                    label: text,
                    value: text,
                    option: this,
                    category: $(this).closest("optgroup").attr("label")
                };
        }) );
    },
    _removeIfInvalid: function( event, ui ) {

        if ( ui.item ) {
            return;
        }

        var value = this.input.val(),
            valueLowerCase = value.toLowerCase(),
            valid = false;
        this.element.find( "option" ).each(function() {
            if ( $( this ).text().toLowerCase() === valueLowerCase ) {
                this.selected = valid = true;
                return false;
            }
        });

        if ( valid ) {
            return;
        }

        this.input
            .val( "" )
            .attr( "title", value + " не существует" )
            .tooltip( "open" );
        this.element.val( "" );
        this._delay(function() {
            this.input.tooltip( "close" ).attr( "title", "" );
        }, 2500 );
        this.input.data( "ui-autocomplete" ).term = "";
    },
    _destroy: function() {
        this.wrapper.remove();
        this.element.show();
    }
});
})( jQuery );

2
正如您在jQueryUI文档中所看到的,您需要自定义小部件才能实现这一点。
_renderMenu: function( ul, items ) {
            var self = this,
                currentCategory = "";
            $.each( items, function( index, item ) {
                if ( item.category != currentCategory ) {
                    ul.append( "<li class='ui-autocomplete-category'>" + item.category + "</li>" );
                    currentCategory = item.category;
                }
                self._renderItem( ul, item );
            });
        }

这个还没有测试过,但应该是一个不错的开始:
_renderMenu: function( ul, items ) {
            var self = this,
                currentCategory = "";
            $.each( items, function( index, item ) {
                if ( item.parent.attr('label') != currentCategory ) {
                    ul.append( "<li class='ui-autocomplete-category'>" + item.parent.attr('label') + "</li>" );
                    currentCategory = item.parent.attr('label');
                }
                self._renderItem( ul, item );
            });
        }

如果这个方法不起作用,也许你应该进行调试,看看传递给 _renderMenu 的 items 数组中有什么。
另外一件事:这被称为MonkeyPatching,我不建议经常这样做,但既然文档中展示了这种方法,那就可以使用。

谢谢回复。如果我说错了,请纠正我,但您是从“类别”版本的自动完成代码开始的,而我是从“组合框”版本开始的。因此,上面的代码不起作用。 - Mark K
你没有错。这段代码属于该类别。我不确定它是否有效,也无法立即测试它。我认为它会起作用,只需要覆盖(又称猴子补丁)_renderMenu函数。我打赌无论项来自何处,它都会被调用。 - Jarry

1
我在我的Web应用程序中使用了jqueryui自动完成小部件,配合组合框猴子补丁、optgroup(类别)以及能够搜索类别名称。搜索术语也在兼容选项和optgroup中强调显示。我使用了stackoverflow和jqueryui网站上的多个答案来达到这一点,非常感谢!
我希望它能在最新版本的jqueryui上正常工作。Jqueryui 1.9和1.11引入了破坏性更改(在自动完成和菜单插件中,后者被前者使用),我最终成功地使其与最新版本的jqueryui(1.11.0)和jquery(2.1.1)配合使用。 jsbin here 重要部分:通过新项目选项更改菜单小部件选项,不再将类别视为普通菜单链接(该选项尚未包含在文档中,而是在jqueryui升级指南1.11中)。
$.extend($.ui.menu.prototype.options, {
    items: "> :not(.aureltime-autocomplete-category)"
});

太完美了!一开始我有些犹豫是否使用它,但它真的解决了我的问题,非常感谢! - NinaNa

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接