Делаем фильтр, пока без vue =О
Vue
Как делать задание 1
Не будем с разу во вью окунаться, начнем с основной концепции о разделении данных и их отображения.
начнем опять с bootstrap шаблона
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/css/bootstrap.min.css" integrity="sha384-B0vP5xmATw1+K9KRQjQERJvTumQW0nPEzvF6L/Z6nronJ3oUOFUFpCjEUQouq2+l" crossorigin="anonymous">
<title>Filter</title>
</head>
<body>
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-Piv4xVNRyMGpqkS2by6br4gNJ7DXjqk09RmUpJ8jgGtD7zP9yug3goQfGII0yAns" crossorigin="anonymous"></script>
</body>
</html>
добавлю теперь следующую разметку
<div class="container">
<div class="pb-2 pt-2 d-flex">
<input class="form-control form-control-lg" placeholder="введите строку поиска" type="text">
<button class="ml-2 btn btn-lg btn-warning">Фильтр</button>
</div>
<ul class="list-group">
<li class="list-group-item">
<div>
<img src="https://i.pinimg.com/originals/a9/bc/e1/a9bce10f11d550b680cc76aa5598d7f1.jpg" alt="">
</div>
<h2>Тоторо</h2>
</li>
<li class="list-group-item">
<div>
<img src="https://upload.wikimedia.org/wikipedia/ru/7/75/Catbus_totoro.jpg" alt="">
</div>
<h2>Котобус</h2>
</li>
<li class="list-group-item">
<div>
<img src="https://i.pinimg.com/originals/a9/15/4c/a9154ccfa6615ec6e66c92737457578a.jpg" alt="">
</div>
<h2>Безликий</h2>
</li>
</ul>
</div>
и файлик main.css со стилями для оформления:
.list-group .list-group-item {
display: flex;
align-items: center;
}
.list-group .list-group-item > div {
height: 75px;
width: 75px;
border: 1px solid silver;
border-radius: 50%;
box-shadow: 0 0 4px silver;
padding: 0.25em;
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
}
.list-group .list-group-item img {
max-height: 100%;
max-width: 100%;
}
.list-group .list-group-item h2 {
margin-left: 0.5em;
}
ну и подключу файлик:
<!-- ... -->
<head>
<!-- ... -->
<link rel="stylesheet" href="main.css">
</head>
<!-- ... -->
получиться так:

теперь попробуем реализовать фильтрацию.
Что такое фильтрация – это по сути скрытие блоков, которые не удовлетворяют условию и оставление тех, которые удовлетворяют.
Как можно быстро скрыть какой-то блок в bootstrap?
На самом деле достаточно добавить блоку класс d-none
Например, скроем “Котобус”
<ul class="list-group">
<li class="list-group-item">
<!-- ... -->
</li>
<li class="list-group-item d-none"> <!-- добавил сюда d-none -->
<div>
<img src="https://upload.wikimedia.org/wikipedia/ru/7/75/Catbus_totoro.jpg" alt="">
</div>
<h2>Котобус</h2>
</li>
<li class="list-group-item">
<!-- ... -->
</li>
</ul>
о как:

вернем код обратно
<ul class="list-group">
<li class="list-group-item">
<!-- ... -->
</li>
<li class="list-group-item"> <!-- убрал d-none -->
<div>
<img src="https://upload.wikimedia.org/wikipedia/ru/7/75/Catbus_totoro.jpg" alt="">
</div>
<h2>Котобус</h2>
</li>
<li class="list-group-item">
<!-- ... -->
</li>
</ul>
теперь попробуем сделать так чтобы по клику на кнопку фильтр у нас этой строчке добавлялся класс d-none уже с помощью кода.
Привязываем сначала как обычно функцию:
<div class="container">
<div class="pb-2 pt-2 d-flex">
<input class="form-control form-control-lg" placeholder="введите строку поиска" type="text">
<!-- добавил функцию -->
<button class="ml-2 btn btn-lg btn-warning" onclick="onFilterClick(this)">Фильтр</button>
</div>
<ul class="list-group">
<!-- ... -->
</ul>
</div>
<script>
function onFilterClick(btn) {
console.log("Фильтрую")
}
</script>
проверяем:

отлично, теперь надо как-то попробовать обратиться к строкам, раньше мы делали это по id, но тут ситуация, когда данных может быть потенциально много и обращаться к каждому элементу по id становится не эффективно.
Поэтому браузер дает нам возможность получать список блоков по имени класса или даже по комбинации классов, открой консольку и попробуй ввести там
document.querySelectorAll(".list-group-item")
вот так:

как видишь, такой код возвращает нам список элементов с классом list-group-item
можно даже положить их в переменную

ну и соответственно можно взять конкретный элемент и добавить ему класс d-none:

встает вопрос как вернуть теперь элемент обратно?
можно конечно просто поменять текст className на исходный:

но это не очень надежно потому что мы ж можем не знать какой именно набор классов у элемента который мы пытаемся спрятать, поэтому у любого элемента есть свойство classList

а так как оно возвращает список мы можем легко удалять любой элемент списка по значению, например, убрать класс d-none, во:

красота! =)
И кстати, можно не только удлять но и добавлять класс:

и это намного удобнее чем использовать
a[1].className += " d-none"
И так, объединим теперь это все в кучу и запихаем в функцию:
<script>
function onFilterClick(btn) {
console.log("Фильтрую")
let a = document.querySelectorAll(".list-group-item"); // получаю список строк
a[1].classList.add("d-none"); // прячу вторую строку
}
</script>
проверяем

ура! =)
А как сделать чтобы пряталась строка у которой в названии есть, допустим, какая-та буква, например, буква “т”
<script>
function onFilterClick(btn) {
console.log("Фильтрую")
let a = document.querySelectorAll(".list-group-item");
// проверяем есть ли буква "т" в первой строке
if (a[0].innerText.includes("т")) {
a[0].classList.add("d-none"); // если есть то прячем
}
// проверяем есть ли буква "т" во второй строке
if (a[1].innerText.includes("т")) {
a[1].classList.add("d-none"); // если есть то прячем
}
// проверяем есть ли буква "т" в третей строке
if (a[2].innerText.includes("т")) {
a[2].classList.add("d-none"); // если есть то прячем
}
}
</script>

а что если у нас будет 100 строк, что ж нам на каждую if пилить? Нет конечно!
Очевидно, все наши строчки можно заменить циклом, вот так:
<script>
function onFilterClick(btn) {
console.log("Фильтрую")
let a = document.querySelectorAll(".list-group-item");
for(let row of a) { // это так foreach цикл в js делается, проходим по всем строкам
if (row.innerText.includes("т")) { // если строка содержит "т"
row.classList.add("d-none"); // то прячем ее
}
}
}
</script>
работает так же

А как теперь взять текст из поля для ввода? Ну сначала добавим ему id
<div class="container">
<div class="pb-2 pt-2 d-flex">
<!-- добавил id="filterText" -->
<input id="filterText" class="form-control form-control-lg" placeholder="введите строку поиска" type="text">
<button class="ml-2 btn btn-lg btn-warning" onclick="onFilterClick(this)">Фильтр</button>
</div>
<!-- ... -->
</div>
теперь возьмем значения из поля для ввода:
<script>
function onFilterClick(btn) {
console.log("Фильтрую")
let a = document.querySelectorAll(".list-group-item");
// подключаемся к полю для ввода
let filterText = document.getElementById("filterText");
for(let row of a) {
// вместо проверки на "т", теперь проверяем содержит ли filterText.value
if (row.innerText.includes(filterText.value)) {
row.classList.add("d-none");
}
}
}
</script>
тестим:

работает! =)
только чет, записи которые не содержат строку не возвращаются обратно. Для этого нам пригодится возможность убирать класс через row.classList.remove("d-none");, вот так:
<script>
function onFilterClick(btn) {
console.log("Фильтрую")
let a = document.querySelectorAll(".list-group-item");
let filterText = document.getElementById("filterText");
for(let row of a) {
if (row.innerText.includes(filterText.value)) {
row.classList.add("d-none"); // если текст присутствует, то прячем
} else {
// иначе убираем класс для прятанья,
// даже если у элемента этого класса нет -- он ругаться не будет,
// просто проигнорирует инструкцию
row.classList.remove("d-none");
}
}
}
</script>

вообще у нас получается какой-то фильтр наоборот, он удаляет записи которые подходят и оставляет которые не подходят. Чтобы это починить поменяем местами add с remove
<script>
function onFilterClick(btn) {
console.log("Фильтрую")
let a = document.querySelectorAll(".list-group-item");
let filterText = document.getElementById("filterText");
for(let row of a) {
if (row.innerText.includes(filterText.value)) {
row.classList.remove("d-none"); // заменил на remove
} else {
row.classList.add("d-none"); // заменил на add
}
}
}
</script>

вот, другое дело! =)
можно сделать более прикольно, чтобы фильтр срабатывал прямо в время ввода.
Для этого надо прицепить функцию к событию input поля для ввода (подробнее тут: https://learn.javascript.ru/events-change-input)
вот так:
<div class="pb-2 pt-2 d-flex">
<!-- добавил инпуту oninput="onFilterClick(this)" -->
<input oninput="onFilterClick(this)" id="filterText" class="form-control form-control-lg" placeholder="введите строку поиска" type="text">
<button class="ml-2 btn btn-lg btn-warning" onclick="onFilterClick(this)">Фильтр</button>
</div>
проверяем:

Ну собственно можно теперь и кнопку выкинуть =)