使用 javascript 或 jquery 创建实时搜索的有效方法是什么?
What is the efficient way to create a live search using javascript or jquery?
我正在实时搜索超过 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");
}
});
降低复杂性的一个步骤是在发送请求之前添加超时。如果在它过期之前按下了更多键,则会附加要搜索的值。这样您就不会为每个击键执行查询。
你每次击键都会触发一次搜索,这太多了。你应该推迟搜索。使用 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));
您可以通过三种不同的方式提高实时 DOM 搜索的性能。 (我将把呈现 10000 DOM 行的固有性能问题作为给定的;这个答案将只涵盖搜索及其结果。)使用 .indexOf() 而不是正则表达式等小细节将也有帮助,但我想那种精细的细节并不是真正的瓶颈。
使搜索更快
Live-searching 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 毫秒。一些框架内置了去抖动功能,但你自己动手也很简单。规范的 drop-in 示例可能是 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 是采用相反的方法:呈现一次完整列表,并尽量减少每次搜索后对它的更改量。同样,这将在您完成生成 matchedSearch 数组之后:
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 class-based 动画,但值得检查。有这么多行,您可能会考虑忽略不必要的视觉效果。)
我正在实时搜索超过 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");
}
});
降低复杂性的一个步骤是在发送请求之前添加超时。如果在它过期之前按下了更多键,则会附加要搜索的值。这样您就不会为每个击键执行查询。
你每次击键都会触发一次搜索,这太多了。你应该推迟搜索。使用 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));
您可以通过三种不同的方式提高实时 DOM 搜索的性能。 (我将把呈现 10000 DOM 行的固有性能问题作为给定的;这个答案将只涵盖搜索及其结果。)使用 .indexOf() 而不是正则表达式等小细节将也有帮助,但我想那种精细的细节并不是真正的瓶颈。
使搜索更快
Live-searching 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 毫秒。一些框架内置了去抖动功能,但你自己动手也很简单。规范的 drop-in 示例可能是 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 是采用相反的方法:呈现一次完整列表,并尽量减少每次搜索后对它的更改量。同样,这将在您完成生成 matchedSearch 数组之后:
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 class-based 动画,但值得检查。有这么多行,您可能会考虑忽略不必要的视觉效果。)