如何使用自动完成下拉菜单中的按钮进一步过滤已显示的结果

sio*_*orn 5 javascript c# ajax asp.net-mvc jquery

我在我的 asp.net mvc4 站点上实现了一个自动完成搜索框。我目前能够让框返回结果,当我在搜索框中键入时更新。我还根据结果“类型 ID”动态生成“类别”按钮,并将它们插入到自动完成生成结果时出现的标题中。

我想介绍这样的功能:当用户单击类别按钮时,现有的自动完成结果会得到进一步过滤,因此只显示该“类型 ID”的结果。之后,如果用户想再次查看与搜索字符串匹配的所有结果,可以单击“全部”按钮。

要查看此版本的工作版本,请查看 Discogs.com 上的搜索框。我还在下面粘贴了这个小部件的屏幕截图,以供参考。

我该如何实施?我找不到任何关于此的 stackoverflow 帖子,因为我不知道如何表达我的问题。

在此处输入图片说明

我的代码如下。在其中,我已经有一个功能正常的自动完成功能,并且我有动态生成类别按钮的部分。现在我需要帮助的是找到一种设计模式,以便在我单击动态生成的类别按钮时进一步过滤自动完成结果。

@model myproject.Models.Search_Term

@Scripts.Render("~/bundles/jquery")

<script type="text/javascript">

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // autopopulate input boxes
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    //detect the browser resize and close the Autocomplete box when that event is triggered
    $(window).resize(function() {
       $("#searchBox").autocomplete("close");
    });

    //helper method for autopopulate.
    ///sf/ask/170517511/
    //this helps in creating a autocomplete menu with custom HTML formatting
    function monkeyPatchAutocomplete() {
      $.ui.autocomplete.prototype._renderItem = function( ul, item) {
          var inner_html = '<img src="' + item.imgPathSmall + '">';
          return $("<li>")
                .data("ui-autocomplete-item", item)
                .append(inner_html)
                .appendTo(ul);
      };
    }

    // look up search term
    $(document).ready(function () {
        //call this to enable the autocomplete menu with custom HTML formatting
        monkeyPatchAutocomplete();
        //trigger autocomplete
        $("#searchBox").autocomplete({
            source: function (request, response) {
                $.ajax({
                    url: "/Explore/SearchAutocomplete",
                    type: "POST",
                    dataType: "json",
                    data: { search: request.term },
                    success: function (data) {
                        response($.map(data, function (item) {
                            return {
                                objectName: item.ObjectName,
                                detail1: item.Detail1,
                                detail2: item.Detail2,
                                detail3: item.Detail3,
                                imgPathSmall: item.Image_Data_SmallPad_string,
                                objectType: item.ObjectType,
                                objectID: item.ObjectID,
                                image_Data_SmallPad: item.Image_Data_SmallPad,
                                image_MimeType_SmallPad: item.Image_MimeType_SmallPad
                            };
                        }))
                    }
                })
            },
            select: function (event, ui) {
                event.preventDefault();
                //redirect to result page
                var url;
                switch (ui.item.objectType) {
                  case 1:
                    url = '@Url.Action("Category1", "Explore")?i=' + ui.item.objectID;
                    break;
                  case 2:
                     url = '@Url.Action("Category2", "Explore")?i=' + ui.item.objectID;
                    break;
                  case 3:
                    url = '@Url.Action("Category3", "Explore")?i=' + ui.item.objectID;
                    break;
                  case 4:
                    url = '@Url.Action("Category4", "Explore")?i=' + ui.item.objectID;
                    break;
                  case 5:
                    url = '@Url.Action("Category5", "Explore")?i=' + ui.item.objectID;
                    break;
                  case 6:
                    url = '@Url.Action("Category6", "Explore")?i=' + ui.item.objectID;
                    break;
                  case 7:
                    url = '@Url.Action("Category7", "Explore")?i=' + ui.item.objectID;
                }
                window.location.href = url;
            }
        }).data("ui-autocomplete")._renderMenu = function (ul, items) {
            //------------------------------------------------------------------------------------
            //Append the header
            //------------------------------------------------------------------------------------
            var header = `
                <li>
                    <div class='acmenu_header'>
                        <div class="btn-group special" role="group" aria-label="...">
                            <button type="button" class="btn btn-default btn-xs">All</button>
            `;
            //helps determine the category buttons to generate
            var categories = [];
            $.each(items, function (index, item) {
                if (item.objectType) {
                        switch (item.objectType) {
                            case 1:
                                categories.push(1);
                                break;
                            case 2:
                                categories.push(2);
                                break;
                            case 3:
                                categories.push(3);
                                break;
                            case 4:
                                categories.push(4);
                                break;
                            case 5:
                                categories.push(5);
                                break;
                            case 6:
                                categories.push(6);
                                break;
                            case 7:
                                categories.push(7);
                        }
                    }
            });
            //helps determine the category buttons to generate
            var uniqueCategories = [...new Set(categories)];
            var arrayLength = uniqueCategories.length;
            //generate the category buttons within the header
            for (var i = 0; i < arrayLength; i++) {
                switch (uniqueCategories[i]) {
                    case 1:
                        header = header + '<button type="button" class="btn btn-default btn-xs">Category1</button>'
                        break;
                    case 2:
                        header = header + '<button type="button" class="btn btn-default btn-xs">Category2</button>'
                        break;
                    case 3:
                        header = header + '<button type="button" class="btn btn-default btn-xs">Category3</button>'
                        break;
                    case 4:
                        header = header + '<button type="button" class="btn btn-default btn-xs">Category4</button>'
                        break;
                    case 5:
                        header = header + '<button type="button" class="btn btn-default btn-xs">Category5</button>'
                        break;
                    case 6:
                        header = header + '<button type="button" class="btn btn-default btn-xs">Category6</button>'
                        break;
                    case 7:
                        header = header + '<button type="button" class="btn btn-default btn-xs">Category7</button>'
                }
            }
            header = header + `
                        </div>
                    </div>
                </li>
            `;
            $(ul).append(header);
            //------------------------------------------------------------------------------------
            //append the autocomplete results
            var that = this;
            var currentCategory = "";
            var currentCategoryLabel = "";
            $.each(items, function (index, item) {
                if (item.objectType != currentCategory) {
                    if (item.objectType) {
                        switch (item.objectType) {
                            case 1:
                                currentCategoryLabel = "Category1";
                                break;
                            case 2:
                                currentCategoryLabel = "Category2";
                                break;
                            case 3:
                                currentCategoryLabel = "Category3";
                                break;
                            case 4:
                                currentCategoryLabel = "Category4";
                                break;
                            case 5:
                                currentCategoryLabel = "Category5";
                                break;
                            case 6:
                                currentCategoryLabel = "Category6";
                                break;
                            case 7:
                                currentCategoryLabel = "Category7";
                        }
                        ul.append("<li class='ui-autocomplete-category'>" + currentCategoryLabel + "</li>");
                    }
                    currentCategory = item.objectType;
                }
                that._renderItem(ul, item);
            });
            //append the footer
            var footer = `
                <li>
                    <mark><span class="glyphicon glyphicon-cog" aria-hidden="true"></span> Advanced search</mark>
                </li>
            `;
            $(ul).append(footer);
        };
    })
</script>


@using (Html.BeginForm("Search", "Explore", FormMethod.Post, new { id = "searchFormNavbar", @class = "nav navbar-form navbar-left", enctype = "multipart/form-data" }))
{
    @Html.AntiForgeryToken()

    <div class="input-group" id="searchDiv">
        @Html.EditorFor(m => Model.SearchTerm, new { htmlAttributes = new { @class = "form-control", @id = "searchBox", placeholder = "Search x, y, z, and more...", style = "width:100%; min-width: 380px;" } })
        <div class="input-group-btn">

            <button id="searchBtn" class="btn btn-default" type="submit" style="color:steelblue">
                <span class="glyphicon glyphicon-search" aria-hidden="true"></span>
            </button>

        </div>
    </div>
}
Run Code Online (Sandbox Code Playgroud)

deb*_*ker 3

您需要一种设计模式,所以这里是一个:

  • 将类别存储为标题按钮内的自定义数据属性:

    //generate the category buttons within the header
    for (var i = 0; i < arrayLength; i++) {
      header += '<button type="button" data-category="' + uniqueCategories[i] + '" ';
      header += 'class="btn btn-default btn-xs">Category' + uniqueCategories[i] + '</button>'
    
    Run Code Online (Sandbox Code Playgroud)

    对于showAll按钮,只需设置一个空属性:

    <button type="button" class="btn btn-default btn-xs" data-category>All</button>

  • _renderItem 函数内部存储每个列表项的类别:

    return $('<li data-category="' + item.objectType + '">')

之后,您可以使用事件委托来过滤列表项:

  1. 设置显示:阻止所有列表项
  2. 对于不匹配的列表项设置显示:无

如果您在严格的循环中设置样式,则浏览器中将只有一次重绘*:

// this function can be further optimized
function applyFilter(c) {
  $("li[data-category]").show().each(function() {
    if(c && c != $(this).data("category")) {
      $(this).hide();
    }
  });
}

$(document).on("click", ".btn[data-category]", function(e){
  // category shall be undefined to show all items, otherwise 1,2, and so on
  applyFilter($(this).data("category"));
})
Run Code Online (Sandbox Code Playgroud)

请随意询问更多细节,但我相信,您会明白的。

(*) DOM 渲染是否保证在单个(同步)函数执行期间阻塞?