我正在搜索超过10000行的数据集。我已经提到了可用的dom结构。虽然我尝试在单次输入后进行实时搜索检查每个结果,但我的浏览器正在挂起。有没有其他有效的方法可以降低其复杂性。
<label class="label">
<input type="checkbox" name="123" value="">
</label>
<label class="label">
<input type="checkbox" name="123" value=" General AUX"> General AUX
</label>
<label class="label">
<input type="checkbox" name="123" value=" Annser"> Annser
</label>
<label class="label">
<input type="checkbox" name="123" value=" LRIPL"> LRIPL
</label>
<label class="label">
<input type="checkbox" name="123" value=" Soy Impulse"> Soy Impulse
</label>
**是的,对DOM进行实时搜索** JS代码,我用于实时搜索
$(".form-container #filter").keyup(function() {
var filter = $(this).val(),
count = 0;
if(filter.length>=2){
// Loop through the comment list
$(".label").each(function() {
// If the list item does not contain the text phrase fade it out
if ($(this).text().search(new RegExp(filter, "i")) < 0) {
$(this).fadeOut();
// Show the list item if the phrase matches and increase the count by 1
} else {
$(this).show();
count++;
}
});
// Update the count
var numberItems = count;
// $(".popopup-header").text(count + " results");
//$("#filter-count").text(count + "results");
}
});
答案 0 :(得分:5)
有三种不同的方法可以提高实时DOM搜索的性能。 (我将采用固有的性能问题来渲染10000个DOM行作为给定;这个答案将仅涵盖搜索及其结果。)使用.indexOf()而不是regexp等小细节也会有所帮助,但我猜这种细节并不是你的瓶颈。
实时搜索DOM总是比搜索简单数据对象慢得多。我猜这到目前为止,这是你目前最大的性能瓶颈。
看起来你只是每行匹配一个字符串,这使事情变得更容易。如果你可以依赖你的行的顺序永远不会改变,那么你可以逃避搜索这些字符串的简单数组,并只使用数组索引来指示你将隐藏或显示哪些DOM行(更多关于稍后) - 但是如果行顺序可能会改变,则您需要至少包含一个ID,以便您可以将字符串与正确的行匹配。所以最简单的情况可能是
var search = function(searchString) {
var searchableRows = ["General AUX", "Ansser", "Etcetera", ...]
var matchedSearch = [];
for (var i=0; i<searchableRows.length; i++) {
if (searchableRows[i].indexOf(searchString) > -1) {
matchedSearch[i]=1;
}
}
// you'll use matchedSearch[] later to control which rows are visible.
您可以对输入进行去抖动,以确保在各个搜索之间至少 n 毫秒,而不是在每次用户击键时运行搜索。有些框架内置了去抖功能,但推出自己的功能非常简单。规范的插入示例可能是this from David Walsh,我无法改进:
// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds. If `immediate` is passed, trigger the function on the
// leading edge, instead of the trailing.
function debounce(func, wait, immediate) {
var timeout;
return function() {
var context = this, args = arguments;
var later = function() {
timeout = null;
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
};
一个大的DOM操作比许多小DOM操作便宜。而且你需要更少的DOM来获得你想要的结果,就越好。
因此,在搜索过程中逐步隐藏或删除DOM行的简单方法 - 这是肯定的。我能想到的两种处理方法是非常不同的,老实说,我不确定哪种方法会有更好的表现;哪个更好可能归结为其他因素,例如HTML需要多么复杂以及它是否需要保留js绑定,在javascript中生成多少令人满意等等。
所以策略1:将结果html生成为一个大字符串然后将其放入DOM中以在单个操作中替换原始HTML:
//assuming you have a filled matchedSearch array from above:
var searchResults = "";
for (var i=0; i<searchableRows.length; i++) {
if (matchedSearch[i]) {
searchResults = searchResults + '<label>...'+searchableRows[i]+'</label'>;
}
}
document.getElementById('ResultsLocation').innerHTML(searchResults);
或者策略2采取相反的方法:渲染完整列表一次,并在每次搜索后最小化您更改它的数量。再次,这将是在您完成匹配搜索数组的生成之后:
var allLabels = $('.label'); // I'm being lazy and depending on jQuery in this example
for (var i=0; i<allLabels.length; i++) {
if (matchedSearch[i]) {
allLabels[i].removeClass('hidden');
} else {
allLabels[i].addClass('hidden');
}
}
(在显示方式方面还有其他可能的优化 - 我注意到你当前正在使用.fadeOut()
;我不确定这是否比使用基于CSS类的速度慢动画,但它值得检查。有了这么多行,你可能会考虑省略不必要的视觉繁荣。)
答案 1 :(得分:0)
降低复杂性的一个步骤是在发送请求之前添加超时。如果在到期之前按下更多键,则追加要搜索的值。这样,您就不会对每个键击执行查询。
答案 2 :(得分:0)
您触发搜索每次击键,这太过分了。你应该推迟搜索。使用Lodash这样做很容易。另外,用indexOf替换正则表达式会更快。
您的新代码可能是:
$(".form-container #filter").keyup(_.debounce(function() {
var filter = $(this).val(),
count = 0;
if(filter.length>=2){
// Loop through the comment list
$(".label").each(function() {
// If the list item does not contain the text phrase fade it out
if ($(this).text().indexOf(filter)) < 0) {
$(this).fadeOut();
// Show the list item if the phrase matches and increase the count by 1
} else {
$(this).show();
count++;
}
});
// Update the count
var numberItems = count;
// $(".popopup-header").text(count + " results");
//$("#filter-count").text(count + "results");
}
}, 400));