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)
您需要一种设计模式,所以这里是一个:
将类别存储为标题按钮内的自定义数据属性:
//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 + '">')
之后,您可以使用事件委托来过滤列表项:
如果您在严格的循环中设置样式,则浏览器中将只有一次重绘*:
// 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 渲染是否保证在单个(同步)函数执行期间阻塞?