如何使用 javascript 过滤 div?

How to filter divs with javascript?

我正在尝试使用 javascript 创建过滤器。我想要完成的是:

单击复选框时,每个 div 与所选 class(汽车或动物)应显示(显示:方块),其他 div 不包含它应该隐藏(显示:none)。

我想出了下面的代码,但它并没有像我想的那样工作。只有在您选中这两个复选框后它才会起作用,但是包含汽车和动物的 classes 会被隐藏。它应该显示它是否包含其中之一。

这个想法很简单,看看复选框是否被选中,然后将它们推送到一个数组中。如果它在该数组中,则更改为 "display: block",否则添加 "display: none"。但正如我所说,有些地方不对劲.. 知道可能是什么问题吗?

Javascript:

let anotherOne = (x) => {
    let all = document.getElementsByClassName('filterDiv'); //all filterable divs
    let checkBoxCars = document.getElementById('cars'); 
    let checkBoxAnimals = document.getElementById('animals');
    let elements = document.getElementsByClassName(x); //divs with selected class 

    //Empty arrays for pushing checked and unchecked boxes.
    let isChecked = []
    let unChecked = []

    if (checkBoxCars.checked == true) {
        isChecked.push('cars')
    } else {
        unChecked.push('cars')
    }

    if (checkBoxAnimals.checked == true) {
        isChecked.push('animals')
    } else {
        unChecked.push('animals')
    }

    //if checkbox value is in checked array then
    if (isChecked.indexOf(x) > -1) {
        //In the array
        for (i = 0; i < elements.length; i++) {
            elements[i].style.display = "block";
        }
    } else {
        //Not in the array
        for (i = 0; i < elements.length; i++) {
            elements[i].style.display = "none";
        }
    }

    //If all checkboxes is unset, show all
    if (isChecked.length == 0) {
        for (i = 0; i < all.length; i++) {
            all[i].style.display = "block";
        }
    }
}

HTML:

<body>
<div id="myBtnContainer">
    <input type="checkbox" id="cars" onclick="anotherOne('cars')">
    <label for="cars">cars</label>
    <input type="checkbox" id="animals" onclick="anotherOne('animals')">
    <label for="animals">animals</label>
  </div>

  <!-- The filterable elements. Note that some have multiple class names (this can be used if they belong to multiple categories) -->
  <div class="container">
    <div id="cars" class="filterDiv show cars">BMW</div>
    <div class="filterDiv show cars animals">CarDog1</div>
    <div class="filterDiv show cars">Volvo</div>
    <div class="filterDiv show cars">Mustang</div>
    <div class="filterDiv show animals">Cat</div>
    <div class="filterDiv show animals">Dog</div>
    <div class="filterDiv show cars animals">CarDog2</div>
    <div class="filterDiv show animals">Cow</div>
  </div>
</body>

一种方法如下,但请记住,我从以下元素中删除了重复的 id

<div id="cars" class="filterDiv show cars">BMW</div>

因为 id 也用于 - 更恰当地 - 用于 <input> 元素,并且重复的 id 会使文档无效并导致 JavaScript 出现问题。

这是我对 HTML 所做的唯一更改,演示如下:

// use of a named function, composed using an Arrow function expression,
// as we don't need to refer to a 'this' within the function, here we
// also pass in the Event Object ('e') from the use of
// EventTarget.addEventListener() (later):
const filterToggle = (e) => {
    // here use document.querySelectorAll() to find all <input>
    // elements with a type-attribute with that value set to
    // 'checkbox' which is also checked; we then use the spread
    // operator to convert the iterable NodeList into an Array:
    const checkedFilters = [
      ...document.querySelectorAll('input[type=checkbox]:checked')
      // we then use Array.prototype.map():
      ].map(
        // to create an Array of the checked check-boxes id properties:
        (el) => el.id
      ),

      // we then derive an Array, as above, of all elements matching
      // the '.filterDiv' selector:
      toFilter = [...document.querySelectorAll('.filterDiv')];


    // if there are no checked filters we show all elements:
    if (checkedFilters.length === 0) {

      // iterating over the Array of nodes using
      // Array.prototype.forEach() along with an anonymous
      // Arrow function expression to update the display
      // property to 'block':
      toFilter.forEach(
        (el) => el.style.display = 'block'
      )
    } else {
      // in the event that some check-boxes were checked,
      // we first iterate over the .filterDiv elements to
      // hide them all:
      toFilter.forEach(
        (el) => el.style.display = 'none'
      );

      // then create a new Array from the checkedFilters
      // Array using Array.prototype.map():
      let selector = checkedFilters.map(

        // here we use a template-literal to create a string of
        // '.filterDiv.<filter-id>'
        (f) => `.filterDiv.${f}`

      // then we join the elements of the Array of selectors
      // together, into a String, using Array.prototype.join()
      // using a comma (',') 
      ).join(',');

      // here we use document.querySelectorAll() with the
      // created selector, and iterate over that NodeList
      // using NodeList.prototype.forEach() to update the
      // display of all elements matching the selector in
      // order to show them again:
      document.querySelectorAll(selector).forEach(
        (el) => el.style.display = 'block'
      );
    }
  },

  // here we find the check-boxes:
  filters = document.querySelectorAll('input[type=checkbox]');

// we iterate over the check-boxes using NodeList.prototype.forEach():
filters.forEach(
  // along with an anonymous Arrow function expression, to bind the
  // filterToggle() (note the deliberate lack of parentheses) as the
  // event-handler for the 'change' event:
  (f) => f.addEventListener('change', filterToggle)
);

const filterToggle = (e) => {
    const checkedFilters = [...document.querySelectorAll('input[type=checkbox]:checked')].map(
        (el) => el.id
      ),
      toFilter = [...document.querySelectorAll('.filterDiv')];

    if (checkedFilters.length === 0) {
      toFilter.forEach(
        (el) => el.style.display = 'block'
      )
    } else {
      toFilter.forEach(
        (el) => el.style.display = 'none'
      );

      let selector = checkedFilters.map(
        (f) => `.filterDiv.${f}`
      ).join(',');
      document.querySelectorAll(selector).forEach(
        (el) => el.style.display = 'block'
      );
    }
  },
  filters = document.querySelectorAll('input[type=checkbox]');

filters.forEach(
  (f) => f.addEventListener('change', filterToggle)
);
.container {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  grid-template-rows: 4em;
  grid-auto-rows: 4em;
  grid-gap: 0.5em;
}

.filterDiv {
  border: 2px solid palegreen;
}
<div id="myBtnContainer">
  <input type="checkbox" id="cars">
  <label for="cars">cars</label>
  <input type="checkbox" id="animals">
  <label for="animals">animals</label>
</div>

<!-- The filterable elements. Note that some have multiple class names (this can be used if they belong to multiple categories) -->
<div class="container">
  <div class="filterDiv show cars">BMW</div>
  <div class="filterDiv show cars animals">CarDog1</div>
  <div class="filterDiv show cars">Volvo</div>
  <div class="filterDiv show cars">Mustang</div>
  <div class="filterDiv show animals">Cat</div>
  <div class="filterDiv show animals">Dog</div>
  <div class="filterDiv show cars animals">CarDog2</div>
  <div class="filterDiv show animals">Cow</div>
</div>

JS Fiddle demo.

参考文献:

我自己解决了这个问题,让它变得比我最初想象的要简单,只保留了我的两个数组,根据是否选中它们将 类 推入两个数组,然后添加显示: none 并显示:阻止他们。适用于多个 类。我猜你有时会想太多..

这是我的解决方案:

let anotherOne = (x) => {
    console.log(x)
    let all = document.getElementsByClassName('filterDiv'); //all filterable divs
    let checkBoxCars = document.getElementById('cars'); 
    let checkBoxAnimals = document.getElementById('animals');

    //remembers if checked
    let isChecked = []
    let unChecked = []

    if (checkBoxCars.checked == true){isChecked.push('cars')}else{unChecked.push('cars')}
    if (checkBoxAnimals.checked == true){isChecked.push('animals')}else{unChecked.push('animals')}
    if (checkBoxPlant.checked == true){isChecked.push('plants')}else{unChecked.push('plants')}
    console.log('checked: '+ isChecked + ' unchecked: ' + unChecked)

    //Add display none to unChecked array
for(i=0;i<unChecked.length;i++) {
    let getClass = document.getElementsByClassName(unChecked[i]);
    for(c=0;c<getClass.length;c++) {
        getClass[c].style.display ="none";
    }}
//Add display block to isChecked array
    for(i=0;i<isChecked.length;i++) {
        let getClass = document.getElementsByClassName(isChecked[i]);
        for(c=0;c<getClass.length;c++) {
            getClass[c].style.display ="block";
        }}