jquery搜索栏上的等待时间过长


你好,我的搜索表单有问题。

我在DOM中有一个搜索函数(这不是AJAX请求(。当用户在表单中键入(事件输入(时,必须执行此功能。但这个功能需要很长时间才能执行,因此搜索栏在这段时间内被屏蔽,因此用户在几秒钟内无法键入任何内容。所以我想要的是让搜索功能运行,但不阻塞搜索栏。你知道如何解决这个问题吗?PS:我只能用javascript或jquery 写

我的代码

<script type="text/javascript">
$("#search_bar").on("input", function(e) {
var value = e.currentTarget.value.trim().toLocaleUpperCase();
do_research(value);
})
function do_research(value) {
$("tr").each(function(index, element) {
if (index > 0) {
var first_name = element.cells[1].innerText.trim().toLocaleUpperCase();
var last_name = element.cells[2].innerText.trim().toLocaleUpperCase();
if (first_name.indexOf(value) == -1 && last_name.indexOf(value) == -1) {
$(element).fadeOut(0);
} else if (!$(element).is(":visible")) {
$(element).fadeIn(0);
}
}
})
}
</script>

我不得不做过几次类似的事情,所以我想出了4件有用的事情;按影响最大的顺序:

  1. 去抖动
  2. 提前中止
  3. 重新使用dom元素
  4. 分页输出

天真

对于基线比较,这里有一个天真的方法。试着输入短单词(例如"hello"(,注意到UI冻结。

let $ = q => document.querySelector(q);
let slowComputeOutput = inputStr => {
let s = 0;
for (let i = 0; i < 2000000; i++)
s += Math.sin(Math.sqrt(i) ** Math.atan2(i, i + 1)) % 3;
s = [...inputStr].reduce((a, b) => a + b.charCodeAt(0), s);
return inputStr + ' ' + Math.round(s);
};
let updateOutput = () => {
let line = document.createElement('div');
line.textContent = slowComputeOutput($('#input').value);
$('#output').append(line);
};
$('#input').addEventListener('input', updateOutput);
<input id="input">
<div id="output"></div>

去抖动

去抖动的目标是,如果输入被新的用户输入覆盖,则跳过输入。例如,键入"hello"将仅计算"h"one_answers"hello"的输出。相反,在没有去抖动的情况下,"h"、"he"、"hel"、"hell"one_answers"hello"的输出都是计算出来的。

let $ = q => document.querySelector(q);
let sleep = async ms => new Promise(r => setTimeout(r, ms));
let slowComputeOutput = inputStr => {
let s = 0;
for (let i = 0; i < 2000000; i++)
s += Math.sin(Math.sqrt(i) ** Math.atan2(i, i + 1)) % 3;
s = [...inputStr].reduce((a, b) => a + b.charCodeAt(0), s);
return inputStr + ' ' + Math.round(s);
};
let updateOutput = () => {
let line = document.createElement('div');
line.textContent = slowComputeOutput($('#input').value);
$('#output').append(line);
};
let debounceTimeout = sleep(0), debounceId = 0;
let updateDebounced = async () => {
let id = ++debounceId;
await debounceTimeout;
if (id !== debounceId)
return;
updateOutput();
debounceTimeout = sleep(500);
};
$('#input').addEventListener('input', updateDebounced);
<input id="input">
<div id="output"></div>

提前中止

这里的目标是,如果输入发生变化,我们将中止任何不再需要的正在进行的计算。与如上所述仅去抖动相比,键入"hello"将仅计算"hello"的输出,而不是"h"。

乍一看,这似乎使退出变得不再必要,但两者都有是个好主意,尤其是当你要处理例如费率限制或任务启动成本时。

请注意,UI现在在任何时候都是响应的,无论句子的类型有多长。

let $ = q => document.querySelector(q);
let sleep = async ms => new Promise(r => setTimeout(r, ms));
let slowComputeOutput = async (inputStr, abortObj) => {
let s = 0;
for (let groupI = 0; groupI < 2000000; groupI += 20000) {
for (let i = groupI; i < groupI + 20000; i++)
s += Math.sin(Math.sqrt(i) ** Math.atan2(i, i + 1)) % 3;
await sleep(0);
if (abortObj.abort)
return inputStr + ' aborted';
}
s = [...inputStr].reduce((a, b) => a + b.charCodeAt(0), s);
return inputStr + ' ' + Math.round(s);
};
let debounceTimeout = sleep(0), debounceId = 0, abortObj = {};
let updateDebouncedAndAbortCheck = async () => {
abortObj.abort = true;
abortObj = {};

let id = ++debounceId;
await debounceTimeout;
if (id !== debounceId)
return;
let line = document.createElement('div');
line.textContent = await slowComputeOutput($('#input').value, abortObj);
$('#output').append(line);
debounceTimeout = sleep(500);
};
$('#input').addEventListener('input', updateDebouncedAndAbortCheck);
<input id="input">
<div id="output"></div>

重复使用dom元素

这个很简单,我不认为它需要一个例子。如果大部分延迟来自于更改dom,请尝试更新现有的dom元素,而不是删除过时的元素并创建新元素。

分页输出

如果我们处理的是10000个元素,即使重用dom元素也可能有值得注意的延迟。在这种情况下,我们可以将输出截断为例如5000个元素,并为用户提供查看所有输出或遍历输出页面的选项。

我没有证据表明这对您的DOM更快,但它应该更快。

  1. 我缓存表中没有第一行的行(不需要索引0(ss(大事(
  2. 使用表id(使设置稍微快一点-微观优化(
  3. 以一种简单的方式消除反弹-可按时配置debounceRate: 500
  4. 在命名空间中本地化功能,而不是在"查找"中查找;窗口";每个函数调用
  5. 空白时短循环名称匹配(按退格清除(
  6. .show() .hide()是最快、最简单的方式(较少的代码运行(,因为无论如何都有0持续时间
  7. 在我获得列表后显示/隐藏列表,而不是单独显示-可能会减少DOM流失/重新流动

(function(searcher, jQuery, undefined) {
var searchThings = {
rows: {},
searchValue: "",
debounce: {},
debounceRate: 500
};
var filterOnName = function(index, row) {
if (searchThings.searchValue === "") return false;
let notmatchfirst_name = row.cells[1].innerText.trim()
.toLocaleUpperCase()
.indexOf(searchThings.searchValue) === -1;
let notmatchlast_name = row.cells[2].innerText.trim()
.toLocaleUpperCase()
.indexOf(searchThings.searchValue) === -1;
return (notmatchfirst_name && notmatchlast_name);
}
var do_search = function(value) {
let nomatch = searchThings.rows.filter(filterOnName);
let showme = searchThings.rows.filter(function(index, element) {
return !filterOnName(index,element);
});
nomatch.hide();
showme.filter(":hidden").show();
}
$("#search_bar").on("input", function(e) {
window.clearTimeout(searchThings.debounce);
searchThings.debounce = setTimeout(function() {
searchThings.searchValue = e.target.value.trim().toLocaleUpperCase();
do_search(searchThings.searchValue);
}, searchThings.debounceRate);
});
searcher.setup = function() {
// all but first row (no skipping index == 0 required)
searchThings.rows = $('#searcheMe')
.find('tbody')
.find("tr").slice(1);
}
})(window.searcher = window.searcher || {}, jQuery);
window.searcher.setup();
tr {
border: solid blue 1px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input id="search_bar" type="text" />
<table id="searcheMe">
<tbody>
<tr>
<td>Head? 0</td>
<td>Skip Me</td>
<td>Also waffles</td>
</tr>
<tr>
<td>1</td>
<td>Not Skip Me</td>
<td>Keep Also waffles</td>
</tr>
<tr>
<td>2</td>
<td>Cheese</td>
<td>Also beer waffles</td>
</tr>
<tr>
<td>1A</td>
<td>Test</td>
<td>Keep wafe</td>
</tr>
<tr>
<td>3</td>
<td>cheese bits</td>
<td>large beer</td>
</tr>
<tr>
<td>4</td>
<td>John</td>
<td>Doe</td>
</tr>
<tr>
<td>5</td>
<td>Jim</td>
<td>Doe</td>
</tr>
</tbody>
</table>

最新更新