Как прервать операцию javascript при вводе и запустить новую операцию

#javascript #jquery #html #filter

#javascript #jquery #HTML #Фильтр

Вопрос:

Я создал таблицу, но использую <div> s вместо <tr> s и <td> s. вот пример:

 <div class="tbl tbl1">
    <div class="thead">
        <div class="tr">
            <div class="td colTitle" style="width: 120px"><span>Title</span></div>
            <div class="td colLink" style="width: 190px"><span>Link</span></div>
            <div class="td colSize numeric" style="width: 75px"><span>Size(MB)</span></div>
            <div class="td colUploadDate" style="width: 75px"><span>UploadDate</span></div>
            <div class="td colOpen" style="width: 50px; max-width: 50px;"><span>Show</span></div>
        </div>
        <div class="tr">
            <div class="td colTitle">
                <input type="text" class="Filter" />
            </div>
            <div class="td colLink">
                <input type="text" class="Filter" />
            </div>
            <div class="td colSize">
                <input type="text" class="Filter" />
            </div>
            <div class="td colUploadDate">
                <input type="text" class="Filter" />
            </div>
            <div class="td colOpen">
            </div>
        </div>
    </div>
    <div class="tbody">
    </div>
</div>
  

Я заполню tbody часть операциями на стороне сервера.

Я использую приведенные ниже коды для фильтрации моих строк на основе значений, введенных во входных данных фильтра.

 $(".Filter").on('input', function () {
      filterGrid();
      $(".rowCount").val($(".tbody .tr:visible").length);
});
function filterGrid() {
    $('.tbody .tr').each(function () {
        var v = 1;
        var x = $(this);
        $(".thead .Filter[value!='']").each(function () {
            var i = $(this).parent(".td").index();
            if (x.children(".td:eq("   i   ")").html().indexOf($(this).val()) == -1) {
                v = 0;
                x.hide();
                return false;
            }
        });
        if (v == 1) {
            x.show();
        }
    });
}
  

Мой код отлично работает, когда у меня есть несколько строк, но когда количество загруженных строк увеличивается, для выполнения операции фильтрации требуется больше времени.

когда я ввожу первую букву, я не могу ввести вторую букву до окончания фильтрации на основе первой буквы. могу ли я принудительно применить javascript для прерывания операции и запуска новой при вводе?

ниже приведен пример моей таблицы

Это пример моей таблицы

Комментарии:

1. Можете ли вы опубликовать несколько примеров строк внутри tbody , чтобы мы могли видеть, на что похожа структура?

2. @CertainPerformance Я прикрепил фотографию, показывающую мою таблицу, содержащую некоторые данные.

3. Первое, что вам следует сделать, это оптимизировать ваш код. В настоящее время у вас есть неэффективный код с большим объемом jQuery и ужасный алгоритм (который, например, не кэширует результаты и пересчитывает только столбец, значение фильтра которого изменилось). Если это не помогает и у вас действительно есть тысячи строк, вам нужно будет выполнить цикл асинхронно и прерваться, если есть новый ввод.

4. Можете ли вы опубликовать фактический HTML ? Картинки не очень полезны, они не показывают структуру HTML

5. пример строки: <div class=»tr» idattachment=»1″><div class=»td colTitle» style=»width: 120px;»>FirstFile</div><div class=»td colLink» style=»width: 190px;»>uf1_1.png</div><div class=»td colSize» style=»width: 75px;»>0.11</ div><div class=»td colUploadDate» style=»width: 75px;»>1397/12/13</div><div class=»td colOpen» style=»width: 50px;»><a class=»link» href=»uploads/uf1_1.png»>Открыть</a></div></div>

Ответ №1:

У вас есть довольно много операций jQuery, которые, будучи объединены во многих строках, могут занять немалое количество времени. Вместо того, чтобы создавать множество коллекций jQuery (которые требуют определенных накладных расходов) и пересчитывать искомый индекс на каждой итерации, рассмотрите возможность использования вместо этого ванильного Javascript, который гораздо более легкий. Вы также можете заранее создать массив значений фильтра и связанный с ними индекс, чтобы вам не приходилось перемещаться по DOM, чтобы находить их на каждой итерации:

 $(".Filter").on('input', function() {
  $(".rowCount").val(filterGrid());
});

function filterGrid() {
  const values = Array.from(
    document.querySelectorAll('.thead .Filter'),
    elm => elm.value
  );
  
  let rowsShown = 0;
  document.querySelectorAll('.tbody .tr').forEach((tr) => {
    const tds = tr.querySelectorAll('.td');
    const noMatch = values.some((value, i) => {
      if (!value) {
        return;
      }
      const td = tds[i];
      return !td.innerHTML.includes(value);
    });
    if (noMatch) {
      tr.style.display = 'none';
    } else {
      tr.style.display = 'block';
      rowsShown  ;
    }
  });
  return rowsShown;
}  
 <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

RowCount: <span class="rowCount">1</span>

<div class="tbl tbl1">
  <div class="thead">
    <div class="tr">
      <div class="td colTitle" style="width: 120px"><span>Title</span></div>
      <div class="td colLink" style="width: 190px"><span>Link</span></div>
      <div class="td colSize numeric" style="width: 75px"><span>Size(MB)</span></div>
      <div class="td colUploadDate" style="width: 75px"><span>UploadDate</span></div>
      <div class="td colOpen" style="width: 50px; max-width: 50px;"><span>Show</span></div>
    </div>
    <div class="tr">
      <div class="td colTitle">
        <input type="text" class="Filter" />
      </div>
      <div class="td colLink">
        <input type="text" class="Filter" />
      </div>
      <div class="td colSize">
        <input type="text" class="Filter" />
      </div>
      <div class="td colUploadDate">
        <input type="text" class="Filter" />
      </div>
      <div class="td colOpen">
      </div>
    </div>
  </div>
  <div class="tbody">
    <div class="tr" idattachment="1">
      <div class="td colTitle" style="width: 120px;">FirstFile</div>
      <div class="td colLink" style="width: 190px;">uf1_1.png</div>
      <div class="td colSize" style="width: 75px;">0.11</div>
      <div class="td colUploadDate" style="width: 75px;">1397/12/13</div>
      <div class="td colOpen" style="width: 50px;"><a class="link" href="uploads/uf1_1.png">Open</a></div>
    </div>
  </div>
</div>  

Если это недостаточно быстро, вы можете использовать for циклы вместо методов массива, что немного ускорит работу (хотя и будет сложнее для чтения).

Если у вас огромное количество строк в .tbody , и это все еще недостаточно быстро, то вы могли бы рассмотреть возможность добавления debouncer в input listener, чтобы он filterGrid вызывался только, скажем, через 200 мс после ввода последнего символа, так что большая операция выполняется только тогда, когда у вас есть хотя бы немного уверенности в том, что только что введенный символ может быть последним, который пользователь хочет ввести (вместо запуска filterGrid после каждого введенного символа):

 let filterTimeout;
$(".Filter").on('input', function() {
  clearTimeout(filterTimeout);
  filterTimeout = setTimeout(() => {
    $(".rowCount").val(filterGrid());
  }, 200);
});
  

Комментарии:

1. Также верните количество совпадений из filterGrid вместо этого ужасного $(".tbody .tr:visible").length

2. Я бы сохранил tr.style.display = noMatch ? 'none' : 'block'; и сделал rowsShown = !noMatch; . Но в любом случае проголосуйте за!

3. Отлично, это действительно эффективно.