1
votes

Y a-t-il un moyen d'ajouter une classe à tous les éléments

J'ai parcouru diverses questions similaires, mais je n'ai trouvé nulle part d'aide réelle sur mon problème. Il y a quelques exemples de Jquery, mais aucun d'entre eux n'était pas applicable, car je veux le faire en JavaScript vanilla uniquement.

Le problème

J'ai un widget d'évaluation que je veux construire comme chaque fois que je clique sur l'élément "p", eventListener ajoute la classe "good" sur tous les p éléments avant et sur celui qui a cliqué, et supprimer la classe bien sur tout ce qui vient après celui qui a cliqué.

Mes pensées

J'ai essayé de sélectionner tous les éléments "p" et de les parcourir en utilisant une boucle for pour ajouter une classe avant et sur l'élément cliqué, et pour le laisser inchangé ou le supprimer après, mais quelque part je le fais mal, et il ajoute une classe uniquement sur l'élément cliqué et non sur tous les précédents.

Mon code

function rating () {
var paragraph = document.getElementsByTagName('p');
for (var i = 0; i < paragraph.length; i++) {
    paragraph[i].addEventListener('click', function(e) {
        e.target.className='good';
        })
      }
    } 

rating();

Résultat

Le résultat attendu est que chaque élément p avant celui qui a cliqué, y compris celui qui a cliqué, doit avoir une bonne classe après le clic et tous ceux qui sont après celui qui a cliqué ne devraient avoir aucune classe.

Résultat réel: Seul l'élément cliqué a une bonne classe.


0 commentaires

3 Réponses :


1
votes

En utilisant Array # forEach, Élément # nextElementSibling a > et Element # previousElementSibling

Logique générale derrière cela est de parcourir tous les éléments frères précédents et suivants. Pour chaque élément, ajoutez ou supprimez la classe .good jusqu'à ce qu'il n'y ait plus de frères et sœurs à gérer.

<div id='rating'>
  <p>*</p>
  <p>*</p>
  <p>*</p>
  <p>*</p>
  <p>*</p>
</div>
p.good {
  background-color: red;
}

p.good::after {
  content: ".good"
}

p {
  background-color: lightgrey;
}
const ps = document.querySelectorAll('p');

ps.forEach((p,i,list) => {
  p.addEventListener("click", function() {
    
    const arr = [...list];
    const previous = arr.slice(0,i+1);
    const next = arr.slice(i+1);
    
    previous.forEach(pre=>{
       pre.classList.add("good");
    })
    
    next.forEach(nex=>{
        nex.classList.remove("good");
    });
  })

})

Utilisation du tableau # slice pour obtenir le groupe de p précédent et suivant.

<div id='rating'>
  <p>*</p>
  <p>*</p>
  <p>*</p>
  <p>*</p>
  <p>*</p>
</div>
p.good {
  background-color: red;
}

p.good::after {
  content: ".good"
}

p {
  background-color: lightgrey;
}
const ps = document.querySelectorAll('p');

ps.forEach(p => {
  p.addEventListener("click", function() {
    let next = this.nextElementSibling;
    let prev = this;
    while(prev !== null){
      prev.classList.add("good");
      prev = prev.previousElementSibling;
    }
    while(next !== null){
      next.classList.remove("good");
      next = next.nextElementSibling;
    }
  })

})


1 commentaires

Merci beaucoup, j'ai essayé une approche avec la boucle forEach mais je me suis retrouvé à ajouter des classes sur tous les éléments p. Cela résout le problème, et c'est aussi ES6, donc @kemicofa, bravo mec.



1
votes

L'ingrédient clé est d'utiliser Node. compareDocumentPosition () pour savoir si un élément précède ou suit un autre élément:

<div id='rating'>
  <p>*</p>
  <p>*</p>
  <p>*</p>
  <p>*</p>
  <p>*</p>
</div>`
#rating { display: flex; }
p { font-size: 32px; cursor: default; }
p:hover { background-color: #f0f0f0; }
.good { color: orange; }
var paragraphs;

function handleParagraphClick(e) {

  this.classList.add('good');

  paragraphs.forEach((paragraph) => {

    if (paragraph === this) {
      return;
    }

    const bitmask = this.compareDocumentPosition(paragraph);

    if (bitmask & Node.DOCUMENT_POSITION_FOLLOWING) {
      paragraph.classList.remove('good');
    }

    if (bitmask & Node.DOCUMENT_POSITION_PRECEDING) {
      paragraph.classList.add('good');
    }

  });
}

function setup() {

  paragraphs = [...document.getElementsByTagName('p')];

  paragraphs.forEach((paragraph) => {
    paragraph.addEventListener('click', handleParagraphClick);
  });
}

setup();


2 commentaires

Merci aussi @Jeffrey Westerkampf votre solution était aussi une bonne solution, mais je dois être honnête, je n'ai jamais utilisé Node.compareDocumentPosition () auparavant, c'est une perspective intéressante, et résout également mon problème.


Merci @Srki, c'est en effet une perspective intéressante. Je pense honnêtement que la réponse de kemicofa correspond mieux à votre cas. Cependant, ma solution continuerait de fonctionner lorsque, par exemple, vous envelopperiez chacun de vos p éléments dans un autre élément.



1
votes

function setup() {
  var paragraph = document.querySelectorAll("p");
  for (p of paragraph) {
    p.onclick = (event) => {
      let index = Array.from(paragraph).indexOf(event.target);
      [].forEach.call(paragraph, function(el) {
        el.classList.remove("good");
      });
      for (let i = 0; i <= index; i++) {
        paragraph[i].classList.add("good");
      }

    }
  }
}

//Example case

document.body.innerHTML = `
<div id='rating'>
<p>*</p>
<p>*</p>
<p>*</p>
<p>*</p>
<p>*</p>
</div>`;

setup();


1 commentaires

Merci @westdabestdb aussi votre solution était intéressante mais ce qui me manque, c'est la suppression complète de la classe de l'élément p. Votre solution supprime simplement le nom de la classe et l'a laissé avec

au lieu de

lorsque la classe good est supprimée. Ce qui n'est pas complètement faux et fonctionne.