Как объединить несколько фильтров в изотопе JS?

У меня есть две области типов контента, которые содержат уникальные параметры фильтра. Это:

  1. type
  2. tag

Я пытаюсь использовать isotope.js, чтобы добиться макета двойной фильтрации, но он всегда отдает приоритет фильтру, на который нажали последним.

См. пример использования здесь (ссылка ниже демо):

  1. Если я нажму «Блог и новости», я увижу две публикации (работы).
  2. Если я затем нажму «Примеры использования», я увижу как тематические исследования, так и сообщения в блоге. Любой отмеченный элемент должен отображаться в списке.

Комбинированные фильтры не работают.

В документации говорится, что метод arrange() может обрабатывать несколько экземпляров фильтра, но в моем случае он не работает.

Я также пробовал использовать функцию concatValues() для объединения значений (как показано во многих демонстрациях), но она все равно не дает правильных результатов.

Интерактивную демонстрацию смотрите здесь.

document.addEventListener('DOMContentLoaded', function() {

  var container = document.querySelector('.grid');
  var gridItems = container.querySelectorAll('.grid-item');
  const optionLinks = document.querySelectorAll('.rSidebar__options-li');

  var iso = new Isotope(container, {
    itemSelector: '.resourceCard',
    layoutMode: 'fitRows',
    transitionDuration: '0.5s',
  });

  var filters = {};

  function concatValues( obj ) {
    var value = '';
    for ( var prop in obj ) {
      value += obj[ prop ];
    }
    return value;
  }

  function handleFilterClick(event, filters, iso) {
    var listItem = event.target;
    var filterGroup = listItem.closest('.rSidebar__options').getAttribute('data-filter-group');
    var filterValue = listItem.getAttribute('data-filter');

    if (filters[filterGroup] === filterValue) {
      delete filters[filterGroup];
    } else {
      filters[filterGroup] = filterValue;
    }

    // Combine filters
    var filterValues = Object.values(filters).join(', ');
    // var filterValues = concatValues( filters );


    // debugging
    console.info('List Item:', listItem);
    console.info('Filter Group:', filterGroup);
    console.info('Filter Value:', filterValue);
    console.info('Filters Object:', filters);
    console.info('Filter Values:', filterValues);

    iso.arrange({ filter: filterValues });
  }

  optionLinks.forEach(function(optionLink) {
    optionLink.addEventListener('click', function(event) {
      event.preventDefault();
      this.classList.toggle('selected');
      handleFilterClick(event, filters, iso);
    });
  });


});
.post {
  padding: 100px;
}

.rSidebar__box {
  margin-bottom: 30px;
}
.rSidebar__options {
  padding-left: 0;
}
.rSidebar__options-li {
  margin-bottom: 17px;
  display: flex;
  align-items: center;
  cursor: pointer;
  width: fit-content;
}
.rSidebar__options-li.selected .rSidebar__options-square {
  background-color: #185A7D;
}
.rSidebar__options-square {
  height: 20px;
  width: 20px;
  transition: all 0.5s ease;
  border: 2px solid #000000;
}
.rSidebar__options-label {
  margin-left: 10px;
}

.grid {
  display: flex;
  flex-wrap: wrap;
  margin: -14px 0 0 -14px;
}
.grid-item {
  box-sizing: border-box;
  width: calc(33.33% - 14px);
  margin: 14px 0 18px 14px;
}
<link href = "https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel = "stylesheet" integrity = "sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin = "anonymous">
<script src = "https://unpkg.com/isotope-layout@3/dist/isotope.pkgd.min.js"></script>


<div class = "post">
  <div class = "container">
    <div class = "row justify-content-between">

      <!-- SIDEBAR -->
      <div class = "col-3">
        <div class = "rSidebar">

          <!-- tags -->
          <div class = "rSidebar__box">
            <span class = "rSidebar__label d-block fw-bold">Filter by tag</span>
            <ul class = "rSidebar__options button-group" data-filter-group = "type">
              <li class = "rSidebar__options-li" data-filter = ".pdf">
                <span class = "rSidebar__options-square"></span>
                <span class = "rSidebar__options-label d-block buttonTemp" data-filter = ".pdf">PDF</span>
              </li>
                <li class = "rSidebar__options-li" data-filter = ".article">
                <span class = "rSidebar__options-square"></span>
                <span class = "rSidebar__options-label d-block buttonTemp" data-filter = ".article">Article</span>
              </li>
            </ul>
          </div>

          <!--  type -->
          <div class = "rSidebar__box">
            <span class = "rSidebar__label d-block fw-bold">Filter by type</span>
            <ul class = "rSidebar__options button-group" data-filter-group = "type">
              <li class = "rSidebar__options-li" data-filter = ".blogs-and-news">
                <span class = "rSidebar__options-square"></span>
                <span class = "rSidebar__options-label d-block buttonTemp" data-filter = ".blogs-and-news">Blog & News</span>
              </li>
                <li class = "rSidebar__options-li" data-filter = ".case-study">
                <span class = "rSidebar__options-square"></span>
                <span class = "rSidebar__options-label d-block buttonTemp" data-filter = ".case-study">Case Studies</span>
              </li>
            </ul>
          </div>
          <!-- end -->
        </div>
      </div>
      <!-- END -->

      <!-- GRID -->
      <div class = "col-7">
        <div class = "grid">
          <article class = "resourceCard grid-item case-study pdf"><span class = "resourceCard__body-title">Case study, PDF post</span></article>
          <article class = "resourceCard grid-item blogs-and-news"><span class = "resourceCard__body-title">Blogs and news post</span></article>
          <article class = "resourceCard grid-item blogs-and-news article"><span class = "resourceCard__body-title">Blogs and news, article post</span></article>
        </div>
      </div>
      <!-- END -->

    </div>
  </div>
</div>

Последняя попытка

document.addEventListener('DOMContentLoaded', function() {

  var container = document.querySelector('.grid');
  var gridItems = container.querySelectorAll('.grid-item');
  const optionLinks = document.querySelectorAll('.rSidebar__options-li');

  var iso = new Isotope(container, {
    itemSelector: '.resourceCard',
    layoutMode: 'fitRows',
    transitionDuration: '0.5s',
  });

  var filters = {};

  function concatValues( obj ) {
    var value = '';
    for ( var prop in obj ) {
      value += obj[ prop ];
    }
    return value;
  }

  function handleFilterClick(event, filters, iso) {
    var listItem = event.target;
    var filterGroup = listItem.closest('.rSidebar__options').getAttribute('data-filter-group');
    var filterValue = listItem.getAttribute('data-filter');

    var allowMultiple = listItem.closest('.rSidebar__options').getAttribute('data-multiple') === 'true';

    if (allowMultiple) {
      // toggle the filter value
      if (filters[filterGroup] && filters[filterGroup].includes(filterValue)) {
        // remove the filter value if it already exists
        filters[filterGroup] = filters[filterGroup].filter(value => value !== filterValue);
      } else {
        // add the filter value if it doesn't exist
        if (!filters[filterGroup]) {
          filters[filterGroup] = [];
        }
        filters[filterGroup].push(filterValue);
      }
    } else {
      // replace the filter value
      filters[filterGroup] = [filterValue];
    }

    var filterValues = concatValues( filters );

    // console.info('List Item:', listItem);
    // console.info('Filter Group:', filterGroup);
    // console.info('Filter Value:', filterValue);
    // console.info('Filters Object:', filters);
    // console.info('Filter Values:', filterValues);

    iso.arrange({ filter: filterValues });

  }


  optionLinks.forEach(function(optionLink) {
    optionLink.addEventListener('click', function(event) {
      event.preventDefault();
      this.classList.toggle('selected');
      handleFilterClick(event, filters, iso);
    });
  });


});
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
0
138
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вам необходимо обновить значение объекта, которое создается при каждом щелчке по тегу/типу с выбранным значением, а затем использовать этот обновленный массив для передачи в фильтр.

В приведенном ниже коде я добавил условие, т.е.: если в целевом элементе есть выбранный класс, тогда только добавьте/обновите значение массива (фильтров), иначе просто удалите это значение фильтра данных из ключа.

Демо-код:

var filters = {};
var container = document.querySelector('.grid');
var gridItems = container.querySelectorAll('.grid-item');
const optionLinks = document.querySelectorAll('.rSidebar__options-li');

var iso = new Isotope(container, {
  itemSelector: '.resourceCard',
  layoutMode: 'fitRows',
  transitionDuration: '0.5s',
});

function concatValues(obj) {
  var value = [];
  for (var prop in obj) {
    value.push(obj[prop]);
  }

  return value.flat().join(', ');
}

function handleFilterClick(event, filters) {
  console.clear()
  var listItem = event;
  var filterGroup = listItem.closest('.rSidebar__options').getAttribute('data-filter-group');
  var data_filter = listItem.getAttribute('data-filter');

  //if selected class present do below : 
  if (listItem.classList.contains('selected')) {
    if (!filters[filterGroup]) {
      filters[filterGroup] = []
    }
    filters[filterGroup].push(data_filter)
  } else {
    filters[filterGroup] = filters[filterGroup].filter(data => data !== data_filter)
  }
  var filterValues = concatValues(filters);
  console.info(filterValues)

  iso.arrange({
    filter: filterValues
  });
}

optionLinks.forEach(function(optionLink) {
  optionLink.addEventListener('click', function(event) {
    event.preventDefault();
    this.classList.toggle('selected');
    handleFilterClick(this, filters);
  });
});
.post {
  padding: 100px;
}

.rSidebar__box {
  margin-bottom: 30px;
}

.rSidebar__options {
  padding-left: 0;
}

.rSidebar__options-li {
  margin-bottom: 17px;
  display: flex;
  align-items: center;
  cursor: pointer;
  width: fit-content;
}

.rSidebar__options-li.selected .rSidebar__options-square {
  background-color: #185A7D;
}

.rSidebar__options-square {
  height: 20px;
  width: 20px;
  transition: all 0.5s ease;
  border: 2px solid #000000;
}

.rSidebar__options-label {
  margin-left: 10px;
}

.grid {
  display: flex;
  flex-wrap: wrap;
  margin: -14px 0 0 -14px;
}

.grid-item {
  box-sizing: border-box;
  width: calc(45% - 14px);
  margin: 14px 0 18px 14px;
  border: 2px solid;
  padding: 20px;
}
<link href = "https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel = "stylesheet" integrity = "sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin = "anonymous">
<script src = "https://unpkg.com/isotope-layout@3/dist/isotope.pkgd.min.js"></script>


<div class = "post">
  <div class = "container">
    <div class = "row justify-content-between">

      <!-- SIDEBAR -->
      <div class = "col-3">
        <div class = "rSidebar">

          <!-- tags -->
          <div class = "rSidebar__box">
            <span class = "rSidebar__label d-block fw-bold">Filter by tag</span>
            <ul class = "rSidebar__options button-group" data-filter-group = "tag">
              <li class = "rSidebar__options-li" data-filter = ".pdf">
                <span class = "rSidebar__options-square"></span>
                <span class = "rSidebar__options-label d-block buttonTemp" data-filter = ".pdf">PDF</span>
              </li>
              <li class = "rSidebar__options-li" data-filter = ".article">
                <span class = "rSidebar__options-square"></span>
                <span class = "rSidebar__options-label d-block buttonTemp" data-filter = ".article">Article</span>
              </li>
            </ul>
          </div>

          <!--  type -->
          <div class = "rSidebar__box">
            <span class = "rSidebar__label d-block fw-bold">Filter by type</span>
            <ul class = "rSidebar__options button-group" data-filter-group = "type">
              <li class = "rSidebar__options-li" data-filter = ".blogs-and-news">
                <span class = "rSidebar__options-square"></span>
                <span class = "rSidebar__options-label d-block buttonTemp" data-filter = ".blogs-and-news">Blog & News</span>
              </li>
              <li class = "rSidebar__options-li" data-filter = ".case-study">
                <span class = "rSidebar__options-square"></span>
                <span class = "rSidebar__options-label d-block buttonTemp" data-filter = ".case-study">Case Studies</span>
              </li>
            </ul>
          </div>
          <!-- end -->
        </div>
      </div>
      <!-- END -->

      <!-- GRID -->
      <div class = "col-7">
        <div class = "grid">
          <article class = "resourceCard grid-item case-study pdf"><span class = "resourceCard__body-title">Case study, PDF post</span></article>
          <article class = "resourceCard grid-item blogs-and-news"><span class = "resourceCard__body-title">Blogs and news post</span></article>
          <article class = "resourceCard grid-item blogs-and-news article"><span class = "resourceCard__body-title">Blogs and news, article post</span></article>
          <article class = "resourceCard grid-item blogs-and-news article pdf case-study"><span class = "resourceCard__body-title">Blogs and news,article,pdf,case study </span></article>
        </div>
      </div>
      <!-- END -->

    </div>
  </div>
</div>

Привет, Свати, спасибо за твой ответ. Однако похоже, что ваша демо-версия демонстрирует результаты, которые точно соответствуют результатам filterValue. Например, посмотрите этот JSFiddle на основе вашей демонстрации: jsfiddle.net/7f6ujL9t. В этом фрагменте, если вы выберете следующие параметры «блоги и новости», «статья» и «pdf». Это не дает никаких результатов, на самом деле должно отображаться сообщение «Блоги и новости, статьи, тематическое исследование», потому что оно имеет 2 фильтра («блог и новости» и «статья»).

Freddy 30.09.2023 23:54

Посмотрите видео-демонстрацию моего сообщения выше: i.imgur.com/MtBWXcB.mp4

Freddy 30.09.2023 23:55

Привет, я в замешательстве, потому что в заданном тобой вопросе ты сказал If I then also click "PDF", I should then see no posts (as no blog post that has pdf as a class exists).. что тогда это значит?

Swati 01.10.2023 05:46

Извиняюсь, Свати, кажется, я сам запутался во время написания вопроса и заметил неправильный вариант использования. Чтобы уточнить, фильтры по своей природе являются «флажками», и все, что есть .selected, должно отображаться в результатах. Т.е. если проверяются блоги и тематические исследования, то должны отображаться оба из них (а не результаты, которым присвоены оба .blogs-and-news.case-studies. Имеет ли это смысл?

Freddy 01.10.2023 13:30

Да, @Фредди. Пожалуйста, проверьте, работает ли обновленный ответ для вас.

Swati 01.10.2023 16:37

Привет, Свати, то, что ты сделал, — это именно то, что мне нужно в описанном выше варианте использования. Хотя требование/логика изменилась с момента вашего ответа. Поскольку вы ответили на исходный вопрос, я отмечу это как ответ (спасибо за помощь). Но мне также интересно, можете ли вы помочь мне с измененным вариантом использования, который я подробно описал в новом вопросе здесь: stackoverflow.com/questions/77217964/…

Freddy 02.10.2023 21:16

Другие вопросы по теме