Поиск с фильтром через input
У меня есть 4 карточки с товаром (card). Внутри контент с текстом, мне необходимо, чтобы при вводе в поле для поиска, карточки фильтровались и отображались на странице, либо же пропадали в соответствии с введенным значением в поле input и текстом в них(теги 'p'). Сделать это нужно без доп.фреймворков(jquery,react и тд),только нативный JS. Нашел пример в интернете, выполненный с тегами ul, li, a. Сделал по примеру со своими тегами, но не работает - ошибка:
TypeError: Cannot read property 'innerHTML' of undefined
Насколько я понимаю, вся путаница в тегах происходит. Вот код-верстка одной из карточек(еще 3 дублировать не буду) и код javascript. Буду благодарен за помощь, либо же за свой предложенный вариант подобной фильтрации через input.
let search = document.getElementById('search')
search.onkeyup = (event) => {
let filter = search.value.toUpperCase();
let card = document.getElementsByClassName("card");
let cardBody = document.getElementsByClassName("card-body");
var i;
for (i = 0; i < 15; i++) {
let p = cardBody[i].getElementsByTagName("p")[0];
if (p.innerHTML.toUpperCase().indexOf(filter) > -1) {
card[i].style.display = "";
} else {
card[i].style.display = "none";
}
}
}
<input id='search' type="search" placeholder="Search">
<div class="card" style="width: 16.5rem;">
<div class='text-center py-4'>
<img src="img/phone_one.png"style="width: 6.25rem;" class="card-img-top" alt="phone">
</div>
<div class="card-body text-center d-flex flex-column justify-content-between ">
<p class="card-title position-relative">Huawei Mate S
</p>
<p class="card-text">$280.00</p>
</div>
<div class="card-body d-flex justify-content-center ">
<a href="#" class="card-link border px-2"><i class="fas fa-trash-alt"></i></a>
<a href="#" class="card-link border px-2 mx-0"><i class="fas fa-shopping-cart"></i></a>
</div>
</div>
Ответы (1 шт):
Поиск элементов по их текстовому содержимому - это одна из проблемных задач, т.к. из стандартного инструментария, такой поиск "умеют" только XPath-селекторы.
Можно использовать перебор элементов, в каждом из них выполняя поиск подстроки средствами JS (строковыми методами / регулярками), но это очень неэффективно.
Поэтому, я предлагаю использовать именно XPath - через вспомогательный метод для текстового поиска элементов, взятый из моего старого ответа (shameless plug ?):
HTMLElement.prototype.getNodesByText = function (text) {
const expr = `.//*[text()[contains(
translate(.,
'ABCDEFGHIJKLMNOPQRSTUVWXYZАБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ',
'abcdefghijklmnopqrstuvwxyzабвгдеёжзийклмнопрстуфхцчшщъыьэюя'
),
'${text.toLowerCase()}'
)]]`; /**/
const nodeSet = document.evaluate(expr, this, null,
XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
null);
return Array.from({ length: nodeSet.snapshotLength },
(v, i) => nodeSet.snapshotItem(i)
);
};
// код решения начинается здесь
const allCards = document.querySelectorAll('.card'),
container = document.querySelector('.container');
document.getElementById('search').addEventListener('input', evt => {
allCards.forEach(card => card.classList.remove('search-result')); // убираем класс, добавленный при предыдущем поиске
const searchStr = container.dataset.search = evt.target.value.trim(); // заносим поисковую строку в data-атрибут, чтобы магия скрытия карточек в CSS работала
if (!searchStr.length) return;
for (const el of container.getNodesByText(searchStr)) { // в цикле по найденным элементам...
const card = el.closest('.card'); // ...находим родительскую карточку элемента...
if (card) card.classList.add('search-result'); // ...и добавляем ей класс со стилем "видимости"
}
});
[data-search]:not([data-search=""]) .card:not(.search-result) { display: none; }
.container { padding: 0.5rem 0; }
.card { padding: 0.5rem; border: 1px solid #ccc; }
.card + .card { margin-top: -1px; }
.card img { display: none; } /* картинки все равно "битые" из-за URL */
.card p { margin: 0; }
<input id="search" type="search" placeholder="Search" autocomplete="off">
<div class="container">
<div class="card" style="width: 24rem;">
<div class='text-center py-4'>
<img src="img/phone_one.png" style="width: 6.25rem;" class="card-img-top" alt="phone" </div>
<div class="card-body text-center d-flex flex-column justify-content-between ">
<p class="card-title position-relative">Huawei Mate S</p>
<p class="card-text">$280.00</p>
</div>
<div class="card-body d-flex justify-content-center ">
<a href="#" class="card-link border px-2"><i class="fas fa-trash-alt"></i></a>
<a href="#" class="card-link border px-2 mx-0"><i class="fas fa-shopping-cart"></i></a>
</div>
</div>
</div>
<div class="card" style="width: 24rem;">
<div class='text-center py-4'>
<img src="img/phone_one.png" style="width: 6.25rem;" class="card-img-top" alt="">
</div>
<div class="card-body text-center d-flex flex-column justify-content-between ">
<p class="card-title position-relative">Лень что-то придумывать, просто текст</p>
<p class="card-text">$265.00</p>
</div>
<div class="card-body d-flex justify-content-center ">
<a href="#" class="card-link border px-2"><i class="fas fa-trash-alt"></i></a>
<a href="#" class="card-link border px-2 mx-0"><i class="fas fa-shopping-cart"></i></a>
</div>
</div>
<div class="card" style="width: 24rem;">
<div class='text-center py-4'>
<img src="img/phone_one.png" style="width: 6.25rem;" class="card-img-top" alt="">
</div>
<div class="card-body text-center d-flex flex-column justify-content-between ">
<p class="card-title position-relative">Скрипту все равно пофигу какой текст искать</p>
<p class="card-text">$148.00</p>
</div>
<div class="card-body d-flex justify-content-center ">
<a href="#" class="card-link border px-2"><i class="fas fa-trash-alt"></i></a>
<a href="#" class="card-link border px-2 mx-0"><i class="fas fa-shopping-cart"></i></a>
</div>
</div>
</div>
Чтобы комментарии в коде примера было удобнее читать, советую развернуть его на всю страницу.
Метод getNodesByText генерирует XPath-селектор поиска по тексту (чего CSS-селекторы не могут) с приведением к нижнему регистру. Затем с этим селектором выполняет выборку из документа, и заполняет ее содержимым массив. В принципе, объяснять там нечего - это просто бойлерплейт работы с document.evaluate спрятанный внутрь метода.
Разбор селектора "магического" CSS-правила скрытия карточек, если интересно:
[data-search]:not([data-search=""]) == наличие непустого атрибута data-search
.card:not(.search-result) == элементы имеющие класс card, но не имеющие класса search-result
То есть, когда скрипт помещает строку поискового запроса в data-атрибут search контейнера, и удаляет класс search-result у всех карточек - все карточки скрываются. Сразу после этого, скрипт пробегает по карточкам найденных элементов, добавляя класс тем карточкам которые должны быть видны, и тем самым исключая их из попадания под CSS-селектор правила с display: none.