Показать сообщение отдельно
  #1 (permalink)  
Старый 05.05.2013, 16:54
Интересующийся
Отправить личное сообщение для TrogWar Посмотреть профиль Найти все сообщения от TrogWar
 
Регистрация: 15.04.2012
Сообщений: 12

Конъюнкция из нескольких filter()
Доброго времени суток!

Есть фильтр товаров по разным категориям и сам список товаров. Все данные хранятся в data-аттрибутах.
Разметка:
<!-- Чекбоксы -->
<input type="checkbox" data-type="foo" data-foo="foo_item1">
<input type="checkbox" data-type="foo" data-foo="foo_item2">
...
<input type="checkbox" data-type="bar" data-bar="bar_item1">
<input type="checkbox" data-type="bar" data-bar="bar_item2">

<!-- Товары -->
<div class="item" data-foo="foo_item1" data-bar="bar_item1" >
<div class="item" data-foo="foo_item2" data-bar="bar_item2" >
...


В фильтре две категории: "foo" и "bar". Мне нужно чтобы при выборе чекбоксов из обоих типов подсвечивалось только то, что удовлетворяет двум условиям одновременно. Это было несложно:
function getFilteredItems(type) {
  // сомнительный момент – сбор выделенных типов (нужен ли он?)
  var checkedTypes = $(':checked').map(function(i,ch){
    return $(ch).data(type);
  });
  // сбор узлов для подсветки
  var resultItems = $('.item').filter(function(){
    // кэшируем текущий элемент чтобы передать в другой метод
    var item = $(this);
    // Возвращает true если хотя бы один элемент true
    return checkedTypes.some(function(typeItem){
      // проверка на совпадение data-аттрибута товара с data-аттрибутом чекбокса
      return typeItem==item.data(type);
    });
  });
  return resultItems;
}

$('#filter').find(':checkbox').click(function(){
  // кэшируем узлы
  var items = $('#goods').find('.item');
  var checked = $(':checked');

  // убрать подсветку со всех товаров
  items.removeClass('highlighted');

  // Если отмечен хотя бы один чекбокс – фильтровать
  if (checked.length>0) {
    items
      .css({'opacity':'.3'})
      .filter(getFilteredItems('foo'))
      .filter(getFilteredItems('bar'))
      .addClass('highlighted');
  }
  else { // иначе – ничего не делать
    items.css({'opacity':'1'});
  }
});


Всё прекрасно работает, если есть хотя бы один чекбокс в двух категориях одновременно. Но если только в одной, то на выходе из фильтра – 0 элементов.
Как сделать так, чтобы при отмеченных чекбоксах только одной категории фильтра отсутствие отмеченных во второй не фильтровало все элементы до нуля?

Я пробовал топорно возвращать результаты фильтра в переменные и сделать условия:
var fooItems = items.filter(getFilteredItems('foo'));
var barItems = items.filter(getFilteredItems('bar'));
if (fooItems.length > 0 && barItems.length > 0) {
  // фильтр по двум категориям
} else if (fooItems.length > 0) {
  // фильтр только по foo
} else if (barItems.length > 0) {
  // фильтр только по bar
}

Но на мой взгляд так писать грубо, неправильно и поддерживать это ужасно неудобно (особенно, в случае добавления бОльшего количества категорий).
Как мне изменить код?

Спасибо!

ps: В некоторых случаях (но далеко не всегда) отмечание чекбокса подвешивается где-то на >400 мс. Меня это очень сильно печалит. Если кто-то даст советы по оптимизации данного кода – буду рад критике!

Последний раз редактировалось TrogWar, 05.05.2013 в 17:38. Причина: Очепятки + добавлены комменты в код + добавлен ps
Ответить с цитированием