Алгоритм фильтрации товаров

88
08 апреля 2021, 23:00

let aside = document.body.querySelector('aside'), 
  set = new Set(), 
  arr = []; 
 
aside.addEventListener('click', () => { 
  let target = event.target; 
  let click = false; 
 
  while (target != aside) { 
    if (target.tagName == 'LABEL') { 
      let text = target.querySelector('span.label').textContent, 
        check = target.querySelector('input'); 
 
      if (check.checked && check.getAttribute('type') == 'checkbox') { 
        set.add(text); 
      } else { 
        set.delete(text); 
      } 
 
      if (check.checked && check.getAttribute('type') == 'radio') { 
        set.add(text); 
      } else { 
        let newText; 
 
        if (target.nextSibling && check.getAttribute('type') == 'radio') { 
          newText = target.nextSibling.querySelector('span.label').textContent; 
        } 
 
        if (target.previousSibling && check.getAttribute('type') == 'radio') { 
          newText = target.previousSibling.querySelector('span.label').textContent; 
        } 
 
        set.delete(newText); 
        click = true; 
      } 
 
    } 
 
    target = target.parentNode; 
  } 
 
  if (click == false) { 
    return; 
  } 
 
  arr.length = 0; 
 
  arr = [...set]; 
 
  let json = JSON.stringify(arr); 
 
  let xhr = new XMLHttpRequest(); 
 
  xhr.open('POST', 'server.js', true); 
  xhr.setRequestHeader('Content-type', 'application/json; charset=utf-8'); 
  xhr.send(json); 
 
  xhr.onreadystatechange = () => { 
    if (xhr.readyState != 4) return; 
 
    let object = JSON.parse(xhr.responseText); 
    let article = document.querySelector('article'); 
 
    while (article.lastChild) { 
      article.removeChild(article.lastChild); 
    }; 
 
    for (let value in object) { 
      let template = document.querySelector('#template').content; 
      let item = template.querySelector('.item'); 
 
      let newItem = item.cloneNode(true); 
      newItem.querySelector('img').src = `${object[value].img}`; 
      newItem.querySelector('.description').textContent = object[value].description; 
      newItem.querySelector('.price-rub').textContent = object[value].rub; 
      newItem.querySelector('.price-penny').textContent = object[value].penny; 
      newItem.querySelector('.country').textContent = object[value].country; 
      article.appendChild(newItem); 
    } 
 
  } 
 
});
<aside> 
  <section> 
    <h3>Страна производства</h3><label> <input type="checkbox"/><span class="checkbox"></span><span class="label">Беларусь</span></label><label> <input type="checkbox"/><span class="checkbox"></span><span class="label">Россия</span></label><label> <input type="checkbox"/><span class="checkbox"></span><span class="label">Украина</span></label><label> <input type="checkbox"/><span class="checkbox"></span><span class="label">Нидерланды</span></label><label> <input type="checkbox"/><span class="checkbox"></span><span class="label">Германия</span></label></section> 
  <section> 
    <h3>Цена</h3> 
    <div class="price-range"><input type="number" placeholder="от: " min="0" /><input type="number" placeholder="до: " min="1" /></div> 
  </section> 
  <section> 
    <h3>Количество в упаковке</h3><label><input type="checkbox"/><span class="checkbox"></span><span class="label">1</span></label><label><input type="checkbox"/><span class="checkbox"></span><span class="label">4 </span></label><label><input type="checkbox"/><span class="checkbox"></span><span class="label">8</span></label></section> 
  <section> 
    <h3>Наличие втулки</h3> 
    <div class="radio"><label><input type="radio" name="sss"/><span class="radiobutton"></span><span class="label">Да</span></label><label><input type="radio" name="sss"/><span class="radiobutton"></span><span class="label">Нет</span></label></div> 
  </section> 
</aside>

Решил написать интернет-магазин для практики. При каждом нажатии по чекбоксам сохраняю их значение(label) в массив и через AJAX посылаю их на сервер, где сравниваю их с полями объекта.

Пример объекта:

{
    img: "picture/52.jpg",
    description: "Туалетная бумага „Kleenex“ нежная ромашка, трехслойная, 8 рулонов.",
    rub: "9р.",
    penny: "99к.",
    country: "Россия",
    sleeve: "Да",
    amount: 8
},
{
    img: "picture/53.jpg",
    description: "Туалетная бумага „Kleenex“ сочная клубника, трехслойная, 8 рулонов.",
    rub: "9р.",
    penny: "99к.",
    country: "Россия",
    sleeve: "Да",
    amount: 8
}

Код фильтрации на сайте:

object.length = 0;
set.clear();
for (let i of set) {
    set.delete(set[i]);
}
arr.forEach(obj => {
    for (let value in obj) {
        for (let i = 0; i < req.body.length; i++) {
            if (obj[value] == req.body[i]) {
                count++;
                set.add(obj);
            }
        }
    }
})
object = [...set];
json = JSON.stringify(object)
res.end(json);

Знаю, что это неправильный код, т.к. он добавляет товар даже в том случае, если только один из критериев удовлетворяет. Как реализовать поиск товаров, удовлетворяющий сразу нескольким пунктам? Возможно надо посылать не массив, а массив массивов. В любом случае я не могу додуматься :)

Answer 1

target.nextSibling.querySelector('span.label').textContent

Могу ошибаться: возможно, что на серверах с Node это делается именно так, но, наверное, вместо таких конструкций все же стоит воспользоваться тегом form, задать всем полям ввода (input, select, checkbox) атрибуты name, value и собирать данные при помощи FormData. Конечно, если вы не практикуетесь в выборках узлов по DOM.

document.getElementById('filter_form') 
  .addEventListener('input', function(event) { 
    const formData = new FormData(this); 
 
    log(formData); 
 
    /** 
     * Пример отправки FormData. 
    const xhr = new XMLHttpRequest(); 
 
    xhr.open('POST', API_URL, true); 
    xhr.onreadystatechange = function() { 
      if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) { 
        const response = JSON.parse(xhr.responseText); 
 
        // Рендеринг списка товаров. 
        console.log(response); 
      } 
    } 
 
    xhr.send(formData);*/ 
  }); 
 
const log = function logToConsoleforDemo(data) { 
  console.clear(); 
  console.log('Эти данные будут отправлены:'); 
 
  for (let pair of data.entries()) { 
    console.log(...pair); 
  } 
};
#app { 
  display: flex; 
  flex-wrap: wrap; 
} 
 
.sidebar { 
  flex: 0 0 288px; 
  padding: 18px; 
  padding-bottom: 188px; 
} 
 
label { 
  display: block; 
}
<div id="app"> 
  <aside class="sidebar"> 
    <form id="filter_form" action="" method="post"> 
      <section> 
        <h3>Страна производства</h3> 
        <label> 
              <input type="checkbox" name="country[]" value="belarus" /> 
              <span class="checkbox"></span> 
              <span class="label">Беларусь</span> 
            </label> 
        <label> 
              <input type="checkbox" name="country[]" value="russia" /> 
              <span class="checkbox"></span> 
              <span class="label">Россия</span> 
            </label> 
        <label> 
              <input type="checkbox" name="country[]" value="ukraine" /> 
              <span class="checkbox"></span> 
              <span class="label">Украина</span> 
            </label> 
        <label> 
              <input type="checkbox" name="country[]" value="netherlands" /> 
              <span class="checkbox"></span> 
              <span class="label">Нидерланды</span> 
            </label> 
        <label> 
              <input type="checkbox" name="country[]" value="germany" /> 
              <span class="checkbox"></span> 
              <span class="label">Германия</span> 
            </label> 
      </section> 
      <section> 
        <h3>Цена</h3> 
        <div class="price-range"> 
          <input type="number" name="price[]" placeholder="от: " min="100" value="100" /> 
          <input type="number" name="price[]" placeholder="до: " min="1000" value="1000" /> 
        </div> 
      </section> 
      <section> 
        <h3>Количество в упаковке</h3> 
        <select name="quantity-per-pack"> 
          <option value="1">1</option> 
          <option value="4">4</option> 
          <option value="8">8</option> 
        </select> 
      </section> 
      <section> 
        <h3>Наличие втулки</h3> 
        <div class="radio"> 
          <label> 
                <input type="radio" name="thimble" value="1" /> 
                <span class="radiobutton"></span> 
                <span class="label">Да</span> 
              </label> 
          <label> 
                <input type="radio" name="thimble" value="0" /> 
                <span class="radiobutton"></span> 
                <span class="label">Нет</span> 
              </label> 
        </div> 
      </section> 
    </form> 
  </aside> 
  <main class="main"></main> 
</div>

Как реализовать поиск товаров, удовлетворяющий сразу нескольким пунктам?

Небольшой пример, просто он нагляднее без использования for:

// Массив товаров. 
const products = [{ 
  "name": "Первый", 
  "price": "100", 
  "country": "russia", 
  "thimble": "true", 
}, { 
  "name": "Второй", 
  "price": "200", 
  "country": "belarus", 
  "thimble": "true", 
}, { 
  "name": "Третий", 
  "price": "1000", 
  "country": "russia", 
  "thimble": "false", 
}, { 
  "name": "Четвертый", 
  "price": "800", 
  "country": "netherlands", 
  "thimble": "false", 
}]; 
 
// Предположим, что это фильтр с фронта. 
const query = { 
  "country": [ 
    "netherlands", 
    "belarus" 
  ], 
  "thimble": "false" 
}; 
 
// Ключи из фильтра, по ним будет фильтрация. 
const queryKeys = Object.keys(query); 
 
// Здесь желательно не забывать о типах данных. 
// ... code. 
 
// Фильтрация массива объектов по нескольким ключам. 
const filtered = products.filter((product) => { 
  // При помощи метода `every` проверяем, 
  // чтобы значения каждого ключа совпадали 
  // со значениями ключей в товаре. 
  return queryKeys.every((key) => { 
    // Если несколько значений у ключа. 
    // В нашем случае это массив `country` с фронта. 
    if (Array.isArray(query[key])) { 
      return query[key].includes(product[key]); 
    } 
 
    return product[key] == query[key]; 
  }); 
}); 
 
console.log(filtered);

READ ALSO
Как правильно делать вложенные ячейки для таблицы с React MATERIAL-UI?

Как правильно делать вложенные ячейки для таблицы с React MATERIAL-UI?

Как реализовать подобную таблицу с использованием React MATERIAL-UI?

92
Почему onclick не работает? [дубликат]

Почему onclick не работает? [дубликат]

Уже много времени ломаю голову над событием onclickПодскажите, почему то что стоит в событии onclick (к примеру, просто console

108
Кнопка scroll to для любого разрешения экрана

Кнопка scroll to для любого разрешения экрана

Как сделать, чтобы вот такая scroll to кнопка приводила не к координатам, а к конкретному диву что-ли?

82