слишком долгое время ожидания в строке поиска jquery

#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 полезные вещи; в порядке наибольшего воздействия:

  1. Отменить
  2. Прервать досрочно
  3. Повторно используйте элементы dom
  4. Разбиение вывода на страницы

Наивный

Для сравнения базовой линии, вот наивный подход. Попробуйте ввести даже короткие слова (например, «привет») и заметьте, что пользовательский интерфейс зависает.

 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, но так и должно быть.

  1. Я кэширую строки в таблице без первой (индекс 0 не требуется) ss (важная вещь)
  2. Используйте идентификатор таблицы (немного ускоряет настройку — микрооптимизация)
  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 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>