Проблема с JavaScript при фильтрации текста в элементе HTML - PullRequest
0 голосов
/ 27 декабря 2018

Я боролся с одним и тем же фрагментом кода уже несколько дней ...

Итак, для части html у меня есть это:

<input type="text" id="search_immobilier_ville" name="search_immobilier[ville]">
<div class="collection" id="search-ville-collection"></div>

У меня естьвведите, где мне нужно ввести любое название города, затем я хочу отфильтровать совпадающие названия городов в существующий массив городов, например:

let api_results = [['Le Moule',97152],['Lamentin',97189],...]
let ville_input = document.getElementById('search_immobilier_ville');

Затем отобразите соответствующие города в виде списка элементов в DIV.#search-ville-collection.

При каждом событии keyup мне нужно выполнить это действие и обновить визуальный список в режиме реального времени.

Моя проблема в том, что моя система фильтрации испортилась, если янапример, поиск "lam", я могу получить город с именем " lam entin" (пройти тест), а другой - просто с "la", похожим на "capesterre-de-marie-ga la nte "

Пока что я сделал это:

// Previously filled by an API
let api_results = [[name,postalCode],[name,postalCode],...];

ville_input.addEventListener('keyup', () => {
  let value = ville_input.value.toUpperCase().trim();

  // User input
  let input_val = ville_input.value.toUpperCase().trim();

  // Filtering through var ville_input
  api_results.forEach(el => {
    if (el[0].toUpperCase().startsWith(input_val) || input_val.length >= 2 && el[0].toUpperCase().includes(input_val)) {
      result_list.style.display = 'block';

      // if city not present in the html list, add it
      if (document.getElementById(el[1]) === null) {
        $(result_list).append(`<a class="collection-item search-ville-results" id="${el[1]}"> ${el[0]} - ${el[1]} </a>`);
      }
    }
  }); // End forEach

  /* Looping through the collection child nodes to check
    if there are cities that don't match the user input */
  for (let child of result_list.children) {
    console.log(child)

    // if the user input doesn't match with an existing city in the node, delete the node
    if (!child.text.toUpperCase().includes(input_val)) {
      result_list.removeChild(child);
    }
  }

  // Highlight first element of the list
  result_list.firstElementChild.classList.add('active');

  // empty results div if user input is empty
  if (input_val == '') {
    result_list.style.display = 'none';
    result_list.innerHTML = '';
  }
});

Этот код работает ЧАСТИЧНО.Например, если я наберу «lam», я должен получить только один результат, основанный на моем наборе результатов, но проверьте этот сценарий:

Набрав «l»:

Screenshot

набрав "la":

Screenshot

набрав "lam":

Screenshot

(Здесь выначинаю видеть проблему)

Набираю "lame":

Screenshot

Я уверен, что в моем коде что-то не так, но я не могу понятьчто.

Ответы [ 2 ]

0 голосов
/ 27 декабря 2018

Ваша проблема в цикле, который вы используете для удаления недопустимых элементов:

for (let child of result_list.children) {
  console.log(child)

  // if the user input doesn't match with an existing city in the node, delete the node
  if (!child.text.toUpperCase().includes(input_val)) {
    result_list.removeChild(child);
  }
}

children возвращает значение HTMLCollection, что означает, что если вы его измените (например,, удалив элементы), он обновится, что вызовет проблемы с вашим циклом.Вам нужно пройти пункты так, чтобы это не повлияло на изменение коллекции.

Неправильный путь:

Это пример того, как ведет себя циклВ настоящее время.Кнопка должна удалить все элементы, которые содержат «th», но обратите внимание, что она не получает их все и требует нескольких кликов:

document.querySelector('#removeAll').addEventListener('click', () => {
  let list = document.querySelector('#list')
  for (let item of list.children) {
    if (item.textContent.toLowerCase().includes('th')) {
      list.removeChild(item)
    }
  }
})
<button type="button" id="removeAll">Remove All</button>
<ul id="list">
  <li>Stuff</li>
  <li>Things</li>
  <li>Others</li>
  <li>More</li>
</ul>

(A) правильный путь:

Один способ циклически проходить по коллекции, не затрагиваяпо удаляемым элементам следует начинать с последнего индекса и идти назад:

document.querySelector('#removeAll').addEventListener('click', () => {
  let list = document.querySelector('#list')
  let index = list.children.length
  while (index--) {
    let item = list.children[index]
    if (item.textContent.toLowerCase().includes('th')) {
      list.removeChild(item)
    }
  }
})
<button type="button" id="removeAll">Remove All</button>
<ul id="list">
  <li>Stuff</li>
  <li>Things</li>
  <li>Others</li>
  <li>More</li>
</ul>

Лучший способ

В качестве дополнительной заметки, вам может быть лучше просто полностью очистить список и использовать filter, чтобы получить соответствующие результаты, а затем обновить список таким образом.Как и в настоящее время, вы делаете большую проверку, чтобы увидеть, содержит ли список элемент, проверяет текущий список на наличие недопустимых элементов и т. Д. Это повлияет на производительность вашего пользовательского интерфейса, особенно на младших устройствах.

0 голосов
/ 27 декабря 2018

Попытайтесь очистить ваш result_list как первое, что вы делаете в своем keyup событии.

result_list.innerHTML = '';

После этого обязательно отфильтруйте ваши api_results.

const filteredResults = api_results.filter(result => result[0].toUpperCase().includes(input_val));
console.log(filteredResults); // Sanity check.
filteredResults.forEach(result => /* your old function. */);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...