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>

проверяем:

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

1

Делаем фильтр, пока без vue =О