Поиск элементов в
Имеется поиск в списке <select>, скрывает все элементы, которые не совпадают с тем, что ввели в поиск. Но есть проблема, при поиске в большом списке (4500 элементов), виснет на пару секунд. Какими еще другими способами можно сделать поиск или оптимизировать?
document.addEventListener("click", function(e) {
let input = document.getElementById(dataTargetID + 'Input');
let val = input.value.trim().toUpperCase();
let searchItems = document.querySelectorAll('#'+dataTargetID + ' option');
if (e.target.classList.contains('keyboard__key')) {
if (val != '') {
searchItems.forEach(function (elem){
if (elem.innerText.toUpperCase().search(val) == -1) {
elem.classList.add('hidden')
}
else {
elem.classList.remove('hidden');
}
});
}
else {
searchItems.forEach(function (elem){
elem.classList.remove('hidden');
});
}
}
})
Ответы (2 шт):
Мне кажется это будет быстрее потому что:
- Не нужно каждый раз искать
inputиsearchItems includesбыстрее чемsearchfor ofбыстрее чемforEach- Нет лишних
if else
// Prepare options (Start)
const select = document.querySelector('#select');
const fragment = document.createDocumentFragment();
for (let i = 0; i < 4500; ++i) {
const option = document.createElement('option');
option.innerHTML = i + 1;
fragment.append(option);
}
select.append(fragment);
// Prepare options (End)
const input = document.querySelector('#input');
const searchItems = document.querySelectorAll('#select option');
input.addEventListener('input', function(e) {
const target = e.target;
const val = target.value.trim().toUpperCase();
if (!target.classList.contains('keyboard__key')) return;
for (const elem of searchItems) {
const needShow = val !== '' && !elem.innerText.toUpperCase().includes(val);
elem.classList.toggle('hidden', needShow);
}
})
.hidden {
display: none;
}
input {
position: absolute;
}
select {
position: absolute;
top: 50px;
}
<input id="input" class="keyboard__key">
<select id="select" size="20">
</select>
Придумал вариант без классов, а через fragment-ы. По моим проверкам и онлайн тестам это работает намного быстрее как в FF так и в Chrome.
Если я правильно понял, то это работает быстрее потому что:
- Поиск
option-ов происходит непосредственно в самомselect - Удалять элементы и потом вставлять разом все нужные работает быстрее, чем каждому отдельно навешивать класс (если честно понятия и не имею почему это так, если кто-то объяснит причину буду премного благодарен)
// Prepare options (Start)
const select = document.querySelector('#select');
const fragment = document.createDocumentFragment();
for (let i = 0; i < 4500; ++i) {
const option = document.createElement('option');
option.innerHTML = i + 1;
fragment.append(option);
}
select.append(fragment);
// Prepare options (End)
const input = document.querySelector('#input');
const searchItems = select.querySelectorAll('option');
input.addEventListener('input', function(e) {
const target = e.target;
const val = target.value.trim().toUpperCase();
const fragment = document.createDocumentFragment();
if (!target.classList.contains('keyboard__key')) return;
for (const elem of searchItems) {
elem.remove();
if (val === '' || elem.innerText.toUpperCase().includes(val)) {
fragment.append(elem);
}
}
select.append(fragment);
})
input {
position: absolute;
}
select {
position: absolute;
top: 50px;
}
<input id="input" class="keyboard__key">
<select id="select" size="20">
</select>
Универсальное решение, для нескольких input для ввода "запроса" и select для поиска.
JS:
const input = document.querySelectorAll('.search-select');
let cache;
[...input].map(elem => {
const select = document.querySelector('#'+elem.dataset.for);
if(select) {
elem.addEventListener('focus', e => cache = [...document.querySelector('#'+e.target.dataset.for).children);
elem.addEventListener('input', e => {
if(e.target.value.length > 0) {
cache.forEach(el => el.style.display = el.innerText.trim().includes(e.target.value) ? '' : 'none')
} else cache.forEach(el => el.style.display = '');
});
}
})
Потестив заметил, что .forEach работает быстрее, по этому используется он.
.includes() находит любое совпадение в тексте, можно заменить на другое, но может замедлить перебор.
Основное преимущество, как по мне, что при focusе inputа сохраняет содержимое selectа (по сути все option), после перебирает.
HTML:
<input type="text" class="search-select" data-for="select">
<select id="select">
<!-- ... -->
</select>
Связка input с select схожа с label и input, JS будет искать select с ID который указан в input[data-for].
Пример работы кода на 4500 option
P.s.
Как сравнивал и что.
Взял массив из 4500 option, что есть по ссылке выше и прошёлся через некоторые способы:
.map() - ~28мс
.forEach() - ~6мс
for() - ~26мс
while() i++ - ~21мс
while(len--) - ~19мс
Действие при переборе - спрятать элементы с чётным value.
Код теста
Попробовал сделать вариант с .filter(), но лучше не становится, наоборот, время выполнения возрастает.
В моём коде используется поиск совпадений по значению, что возвращает .innerText из 4500 option с value и innerText равным порядковому числу, поиск совпадений со значением - 1, занимает ~131мс.
Коллеги советуют заменить поиск по .innerText, я попробовал некоторые вариант и вот результаты:
.innerText - ~131мс
.value - ~20мс
.innerHTML - ~26мс
.textContent - ~25мс
.dataset - ~23мс
У последних четырёх разница не особо заметна, но советуют использовать .value.