#javascript #jquery
#javascript #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 amp;amp; last_name.indexOf(value) == -1) {
$(element).fadeOut(0);
} else if (!$(element).is(":visible")) {
$(element).fadeIn(0);
}
}
})
}
</script>
Комментарии:
1. Я не думаю, что это проблема JS. Я предполагаю, что у вас много записей, которые ваш браузер не может обработать
fading
все из них очень быстро. Это также замедляет ввод текста в поле ввода.2. пожалуйста, покажите пример HTML, чтобы получить лучший ответ здесь
Ответ №1:
Мне приходилось делать подобные вещи пару раз, поэтому я придумал 4 полезные вещи; в порядке наибольшего воздействия:
- Отменить
- Прервать досрочно
- Повторно используйте элементы dom
- Разбиение вывода на страницы
Наивный
Для сравнения базовой линии, вот наивный подход. Попробуйте ввести даже короткие слова (например, «привет») и заметьте, что пользовательский интерфейс зависает.
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’ и ‘hello’. В отличие от этого, все выходные данные для ‘h’, ‘he’, ‘hel’, ‘hell’ и ‘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’.
На первый взгляд кажется, что в этом больше нет необходимости в отмене, но неплохо иметь и то, и другое, особенно если вы имеете дело, например, с ограничениями скорости или затратами на инициализацию задачи.
Обратите внимание, что пользовательский интерфейс теперь реагирует в любое время, независимо от того, как долго вводится сообщение.
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 вместо удаления устаревших элементов и создания новых элементов.
Разбивка вывода на страницы
Если мы имеем дело с 10 000 элементами, даже при повторном использовании элементов dom все еще может наблюдаться заметная задержка. В этом случае мы можем сократить выходные данные, например, до 5000 элементов и предоставить пользователю возможность просматривать все выходные данные или переходить по страницам выходных данных.
Ответ №2:
У меня нет доказательств, что это быстрее по сравнению с вашим DOM, но так и должно быть.
- Я кэширую строки в таблице без первой (индекс 0 не требуется) ss (важная вещь)
- Используйте идентификатор таблицы (немного ускоряет настройку — микрооптимизация)
- Устранение сбоев простым способом — настраивается вовремя
debounceRate: 500
- Локализуйте функциональность в пространстве имен, а не смотрите в «окно» при каждом вызове функции
- Короткий цикл сопоставления имени, когда оно пустое (нажмите пробел, чтобы очистить)
.show() .hide()
как самый быстрый и простой способ (выполняется меньше кода), поскольку у вас все равно была0
продолжительность- Показывать / скрывать список после того, как я получу его не по отдельности — может быть меньше оттока / повторного потока 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 amp;amp; 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>