3
votes

Plusieurs boutons radio comme filtres

Problème

J'essaie de créer un double filtre, dans lequel vous pouvez combiner des filtres de plusieurs catégories. Par exemple, la première catégorie de filtres correspond aux années et la seconde aux types de médias. Je veux faire en sorte que vous puissiez filtrer uniquement à travers les années, le type de média ou les deux année x type de média (Musique des années 1960). De plus, j'essaie de garder les filtres sélectionnés en surbrillance, d'une manière ou d'une autre, afin que vous puissiez savoir lesquels sont actifs (j'ai essayé de les mettre en gras, et cela fonctionne pour le premier ensemble de filtres, mais échoue pour le second. Comment résoudre ce problème?

Codepen

https: // codepen. io / erutuf / pen / ZPwdBq

Attempt

<div id="myBtnContainer" align="center" style="line-height: 20pt">
  <!-- <button class="btn active" onclick="filterSelection('all')"> Show all</button>   Show ALL -->
  <button class="btn active" onclick="filterSelection('ShowAll')">Show all</button>
  <button class="btn" onclick="filterSelection('1950')">1950</button>
  <button class="btn" onclick="filterSelection('1960')">1960</button>
  <button class="btn" onclick="filterSelection('1970')">1970</button>
</div>
<br>
<div id="myBtnContainer" align="center" style="line-height: 20pt">
  <!-- <button class="btn active" onclick="filterSelection('all')"> Show all</button>   Show ALL -->
  <button class="btn" onclick="filterSelection('AllMedia')">All Media</button>
  <button class="btn" onclick="filterSelection('Movies')">Movies</button>
  <button class="btn" onclick="filterSelection('Music')">Music</button>
  <button class="btn" onclick="filterSelection('Newspapers')">Newspapers</button>
</div>
<br><br>
<div class="filterDiv AllShapes ShowAll 1950 Newspapers">
  <div class="content">
    1950 Newspapers Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
  </div>
</div>
<div class="filterDiv AllShapes ShowAll 1960 Music">
  <div class="content">
    1960 Music Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
  </div>
</div>
<div class="filterDiv AllShapes ShowAll 1960 Newspapers">
  <div class="content">
    1960 Newspapers Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
  </div>
</div>
.filterDiv {
  float: left;
  background-color: white;
  color: black;
  width: 37vw;
  line-height: 100px;
  text-align: center;
  margin: 5px;
  display: none;
  margin-right: 15px;
}

.show {
  display: block;
}

.container {
  margin-top: 20px;
  overflow: hidden;
}

/* Style the buttons */
.btn {
  border: none;
  outline: none;
  padding: 0;
  padding-right: 40px;
  background-color: white;
  cursor: pointer;
  font-size: 16px;
  font-weight: normal;
}

.btn:hover {}

.btn.active {
  font-weight: bold;
}

.content {
  font-size: 16px;
  line-height: 20px;
  text-align: left;
}
filterSelection("all")

function filterSelection(c) {
  var x, i;
  x = document.getElementsByClassName("filterDiv");
  if (c == "all") c = "";
  for (i = 0; i < x.length; i++) {
    w3RemoveClass(x[i], "show");
    if (x[i].className.indexOf(c) > -1) w3AddClass(x[i], "show");
  }
}

function w3AddClass(element, name) {
  var i, arr1, arr2;
  arr1 = element.className.split(" ");
  arr2 = name.split(" ");
  for (i = 0; i < arr2.length; i++) {
    if (arr1.indexOf(arr2[i]) == -1) {
      element.className += " " + arr2[i];
    }
  }
}

function w3RemoveClass(element, name) {
  var i, arr1, arr2;
  arr1 = element.className.split(" ");
  arr2 = name.split(" ");
  for (i = 0; i < arr2.length; i++) {
    while (arr1.indexOf(arr2[i]) > -1) {
      arr1.splice(arr1.indexOf(arr2[i]), 1);
    }
  }
  element.className = arr1.join(" ");
}

// Add active class to the current button (highlight it)
var btnContainer = document.getElementById("myBtnContainer");
var btns = btnContainer.getElementsByClassName("btn");
for (var i = 0; i < btns.length; i++) {
  btns[i].addEventListener("click", function() {
    var current = document.getElementsByClassName("active");
    current[0].className = current[0].className.replace(" active", "");
    this.className += " active";
  });
}

6 commentaires

Pouvez-vous expliquer la différence entre les boutons "Afficher toutes les couleurs" et "Afficher toutes les formes"?


Bok Roko :) Désolé mon mauvais, j'ai utilisé de mauvais mots-filtres pour l'exemple, je vais le changer maintenant. Mais l'idée est que la première catégorie englobe les filtres possibles de la deuxième / la deuxième rangée de filtres sont essentiellement des sous-catégories. Donnez-moi une seconde, je vais résoudre la question


ok désolé encore, corrigé. j'espère que c'est plus clair maintenant


Pourquoi n'utilisez-vous pas les boutons radio ? name = "y" (pour les années) et name = "t" (pour le type) De cette façon, vous pouvez simplement choisir this.checked booléen au lieu de jouer avec les classes ...


oui, je sais que la radio serait la meilleure solution, j'ai réussi à faire ça avec les radios, mais je voulais essayer avec des boutons simplement pour l'esthétique. alors vous pensez que ce serait trop compliqué de le faire de cette façon?


Vous pouvez aussi le faire avec des boutons, ouais, mais vous pouvez aussi le faire en utilisant des boutons radio stylisés et masqués avec . Je vais essayer de faire un exemple avec des boutons, restez à l'écoute


3 Réponses :


-1
votes

Vous avez une fonction document.getElementsByClassName () en javascript qui peut vous aider ici. DOCS: https://www.w3schools.com/jsref/met_document_getelementsbyclassname.asp Vous pouvez obtenir un tableau d'éléments qui correspondent à la classe que vous souhaitez cibler et vous pouvez ensuite les parcourir pour ajouter / supprimer la propriété cachée ou la classe css qui les fait apparaître ou se cacher. La même chose peut être appliquée pour les boutons que vous voulez rendre plus audacieux.

Exemple de pseudocode:

Click the RED filter
   Iterate over all elements on the page and
      If it has the RED class, make hidden property = false
      Otherwise, make hidden property = true
   Then, iterate over all buttons of type COLOUR
      If it's the RED button, add it the css class for btn_active
      Otherwise, remove the btn_active class

Si le bouton cliqué est ALL, la logique est un peu différente mais c'est il suffit de jouer avec les if-elses.

N'oubliez pas que vous avez également les méthodes addClass et removeClass sur chaque élément. Vous n'avez pas besoin de convertir la liste de classes en un tableau de classes et de les parcourir. C'est beaucoup plus simple que ça: https://www.w3schools.com/howto/howto_js_remove_class.asp p >


1 commentaires

w3schools est encore à ce jour une très mauvaise ressource d'apprentissage. De plus, un pseudocode qui suggère des tonnes de if / else est-il vraiment la voie à suivre?



3
votes

Le moyen le plus simple serait d'utiliser les entrées et type = "radio" .
Vous pouvez styliser l'élément interne, adjacent ( + ) à l'entrée : checked .

Stockez les références de filtre ( accessoires ) dans un data-filterable = "valeur1 valeur2 valeurZ" délimité par des espaces.

L'astuce consiste à:

  • lors de l'événement change , obtenez les valeurs d'entrées vérifiées sous forme de tableau.
  • Masquer tous les éléments (ajoutez une classe .is-hidden )
  • obtenir un sous-ensemble filtré d'éléments à afficher , selon que toutes les valeurs vérifiées sont présentes dans les données filtrables accessoires (également comme tableau).

L'exemple ci-dessous fonctionnera pour n'importe quel nombre d'ensembles de filtres:

<div class="filterInputs">
  <label><input type="radio" name="year" value="" checked><i></i>All years</label>
  <label><input type="radio" name="year" value="1950"><i></i>1950</label>
  <label><input type="radio" name="year" value="1960"><i></i>1960</label>
  <label><input type="radio" name="year" value="1970"><i></i>1970</label>
</div>

<div class="filterInputs">
  <label><input type="radio" name="type" value=""><i></i>All types</label>
  <label><input type="radio" name="type" value="movies"><i></i>Movies</label>
  <label><input type="radio" name="type" value="music" checked><i></i>Music</label>
  <label><input type="radio" name="type" value="newspapers"><i></i>Newspapers</label>
</div>

<div data-filterable="1950 newspapers">1950 Newspapers</div>
<div data-filterable="1960 music">1960 Music</div>
<div data-filterable="1960 newspapers">1960 Newspapers</div>
/* FILTER INPUTS */

.filterInputs {
  padding-bottom: 5px;
}

.filterInputs input {
  display: none;
}

.filterInputs label {
  display: inline-block;
  position: relative;
  padding: 10px 20px;
  cursor: pointer;
  user-select: none; /* prevent highlight */
}

.filterInputs i {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  box-shadow: inset 0 0 0 2px #0bf;
  z-index: -1;
}

.filterInputs input:checked + i {
  background: #0bf;
}


/* HELPER CLASSES */

.is-hidden {
  display: none;
}
const el_filters = document.querySelectorAll('[name="year"], [name="type"]'),
  el_filterable = document.querySelectorAll('[data-filterable]');

const applyFilter = () => {

  // Filter checked inputs
  const el_checked = [...el_filters].filter(el => el.checked && el.value);
  
  // Collect checked inputs values to array
  const filters = [...el_checked].map(el => el.value);
  
  // Get elements to filter
  const el_filtered = [...el_filterable].filter(el => {
    const props = el.getAttribute('data-filterable').trim().split(/\s+/);
    return filters.every(fi => props.includes(fi))
  });

  // Hide all
  el_filterable.forEach(el => el.classList.add('is-hidden'));

  // Show filtered
  el_filtered.forEach(el => el.classList.remove('is-hidden'));
}

// Assign event listener
el_filters.forEach(el => el.addEventListener('change', applyFilter));
// Init
applyFilter();


4 commentaires

J'essaie de comprendre quelle ligne de js force à afficher tous les résultats par défaut au début? Je veux faire en sorte que la première chose qu'il montre soit déjà des divs filtrés et pas tous


@erutuf a modifié ma réponse. Définissez simplement l'attribut vérifié en HTML sur l'élément souhaité, et lancez la fonction JS applyFilter () C'est tout ce qu'il faut.


désolé, mon mauvais, quand j'ai c / p js code, j'ai laissé de côté l'initialisation de sorte que l'attribut vérifié ne fonctionnait pas 🤦🏼‍♀️ merci!


@erutuf non, ce n'est pas votre mal, j'ai ajouté l ' exemple Init applyFilter (); il y a 31min. Mais oui, c'était une amélioration facile. Appelez simplement cette fonction.



1
votes

J'ai utilisé le sélecteur d'attribut ( [filter] [group] ) pour ce scénario,

<div id="myBtnContainer" align="center" style="line-height: 20pt">
  <button class="btn active" filter="ShowAll" group="year">Show all</button>
  <button class="btn" filter="1950" group="year">1950</button>
  <button class="btn" filter="1960" group="year">1960</button>
  <button class="btn" filter="1970" group="year">1970</button>
</div>
<br>
<div id="myBtnContainer" align="center" style="line-height: 20pt">
  <button class="btn active" filter="AllMedia" group="media">All Media</button>
  <button class="btn" filter="Movies" group="media">Movies</button>
  <button class="btn" filter="Music" group="media">Music</button>
  <button class="btn" filter="Newspapers" group="media">Newspapers</button>
</div>

<br><br>

 <div class="filterDiv AllMedia ShowAll 1950 Newspapers">
<div class="content">
  1950 Newspapers Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
</div>
</div>


 <div class="filterDiv AllMedia ShowAll 1960 Music">
<div class="content">
  1960 Music Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
</div>
</div>


 <div class="filterDiv AllMedia ShowAll 1960 Newspapers">
<div class="content">
  1960 Newspapers Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
</div>
</div>
.filterDiv {
  float: left;
  background-color: white;
  color: black;
  width: 37vw;
  line-height: 100px;
  text-align: center;
  margin: 5px;
  display: none;
     margin-right: 15px;
}




.show {
  display: block;
}

.container {
  margin-top: 20px;
  overflow: hidden;
}

/* Style the buttons */
.btn {
  border: none;
  outline: none;
    padding: 0;
    padding-right: 40px;
    background-color: white;
  cursor: pointer;
    font-size: 16px;
 font-weight: normal;
}

.btn:hover {
}

.btn.active {
  font-weight: bold;
}



.content{
  font-size:16px;
  line-height:20px;
  text-align:left;
}
(function(){
  const selectedFilters = [];

  // [].slice.call => converts HTMLCollection to Array

  const yearFilters = [].slice.call(document.querySelectorAll('[filter][group="year"]'));
  const mediaFilters = [].slice.call(document.querySelectorAll('[filter][group="media"]'));
  const allFilters = yearFilters.concat(mediaFilters);
  
  const filterContents = [].slice.call(document.querySelectorAll('.filterDiv'));

  // add click event to all filters
  allFilters.forEach((filter) => {
    filter.addEventListener('click', filterToggle);
  });
    
  function filterToggle() {
    const filter = this.getAttribute('filter');
    const group = this.getAttribute('group');

    resetMediaFilters();
    
    if(group === 'year') {
      resetYearFilters();
      mediaFilters[0].classList.add('active');
    }    

    this.classList.add('active');
    applyFilter();
  }
  
  function resetYearFilters() {
    yearFilters.forEach(filter => filter.classList.remove('active'));
  }
  
  function resetMediaFilters() {
    mediaFilters.forEach(filter => filter.classList.remove('active'));
  }
  
  function resetFilterContent() {
    filterContents.forEach(content => content.classList.remove('show'));
  }

  function applyFilter() {
    const selectedFilters = [].slice.call(document.querySelectorAll('[filter].active'))
                        .map(filter => filter.getAttribute('filter'));

// class starts with number is a invalid query selector, so using attribute selector for class
    const selector = ["filterDiv"].concat(selectedFilters).map(filter => '[class~="'+ filter +'"]').join('');
    
    resetFilterContent();
    
    document.querySelectorAll(selector).forEach(content => content.classList.add('show'));
  }
  
  // initialize
  applyFilter();
})();


0 commentaires