Есть ли способ добавить класс ко всем элементам

Я просматривал различные похожие вопросы, но нигде не нашел реальной помощи по своей проблеме. Есть несколько примеров JQuery, но ни один из них неприменим, так как я хочу делать это только с помощью ванильного JavaScript.

Проблема

У меня есть виджет рейтинга, который я хочу построить, как будто каждый раз, когда я нажимаю на элемент "p", eventListener добавляет класс "хорошо" ко всем элементам p до и на выбранном, и удаляет класс хорошо для всех, которые идут после того, по которому щелкнули. .

Мои мысли

Я попытался выбрать все элементы «p» и перебрать их, используя цикл for, чтобы добавить класс до и после щелчка по элементу, а также оставить его без изменений или удалить после, но где-то я делаю это неправильно, и он добавляет только класс по нажатому элементу не на всех предыдущих.

Мой код

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();

Результат

Ожидаемый результат состоит в том, что каждый элемент p до щелчка, включая щелчок, должен иметь хороший класс после щелчка, а все те, которые находятся после щелчка, не должны иметь классов.

Фактический результат: только выбранный элемент имеет хороший класс.

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
0
1 085
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Использование массива # forEach, Элемент # nextElementSibling и Элемент # previousElementSibling

Общая логика этого состоит в том, чтобы перебрать все предыдущие и следующие одноуровневые элементы. Для каждого элемента добавляйте или удаляйте класс .good, пока не останется больше братьев и сестер для обработки.

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;
    }
  })

})
p.good {
  background-color: red;
}

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

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

Альтернатива:

Использование Array # slice для получения предыдущей и следующей группы p.

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");
    });
  })

})
p.good {
  background-color: red;
}

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

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

Большое спасибо, я пробовал какой-то подход с циклом forEach, но обнаружил, что добавляю классы для всех элементов p. Это решает проблему, как и ES6, так что @kemicofa, молодец, дружище.

Srki 12.01.2019 22:47

Ключевой ингредиент - использовать Node.compareDocumentPosition(), чтобы узнать, предшествует ли элемент другому элементу или следует за ним:

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();
#rating { display: flex; }
p { font-size: 32px; cursor: default; }
p:hover { background-color: #f0f0f0; }
.good { color: orange; }
<div id='rating'>
  <p>*</p>
  <p>*</p>
  <p>*</p>
  <p>*</p>
  <p>*</p>
</div>`

Спасибо тоже @Jeffrey Westerkampf, ваше решение тоже было хорошим, но, честно говоря, я никогда раньше не использовал Node.compareDocumentPosition (), это интересная перспектива, а также решает мою проблему.

Srki 12.01.2019 22:55

Спасибо @Srki, это действительно интересная перспектива. Я честно думаю, что ответ kemicofa лучше подходит для вашего случая. Однако мое решение продолжит работать, если вы, скажем, оберните каждый из своих элементов p в другой элемент.

JJWesterkamp 16.01.2019 20:22

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();

Спасибо @westdabestdb, ваше решение было интересным, но мне не хватает полного удаления класса из элемента p. Ваше решение просто удаляет имя класса и оставляет его с <p class> вместо <p> при удалении класса good. Что не совсем неправильно и работает.

Srki 12.01.2019 23:00

Другие вопросы по теме