I’m working on a project with symfony 4. I want to implement a star rating system. I display 5 stars in each row in a table. I have two problems, one is more important than the other.
So the first issue is that I want to be able to retrieve the value of the star I selected. If I select 5 stars, I want to get that value back in my entity controller.
The second issue is that, currently, let’s say I have 5 items in my table, so 5 rows. Currently, if I select 5 stars in one row it’s selected for all rows and I can’t select another value anymore. So, it’s global or something.
Here is the javascript I’m using:
<script> const stars = document.querySelectorAll('.star'); let check = false; stars.forEach(star => { star.addEventListener('mouseover', selectStars); star.addEventListener('mouseleave', unselectStars); star.addEventListener('click', activeSelect); }) function selectStars(e) { const data = e.target; const etoiles = priviousSiblings(data); if (!check) { etoiles.forEach(etoile => { etoile.classList.add('hover'); }) } } function unselectStars(e) { const data = e.target; const etoiles = priviousSiblings(data); if (!check) { etoiles.forEach(etoile => { etoile.classList.remove('hover'); }) } } function activeSelect(e) { if (!check) { check = true; document.querySelector('.note').innerHTML = 'Note ' + e.target.dataset.note; } } function priviousSiblings(data) { let values = [data]; while (data === data.previousSibling) { if (data.nodeName === 'I') { values.push(data); } } return values; } </script>
And Here is the twig.html I’m displaying:
<td> <i class="star" data-note="1">★</i> <i class="star" data-note="2">★</i> <i class="star" data-note="3">★</i> <i class="star" data-note="4">★</i> <i class="star" data-note="5">★</i> <i class="note">Note:</i> </td>
I want to be able to retrieve the value once I made a selection, and to have a different selection for each row I have.
Advertisement
Answer
The problem is with the “mouseover” and “mouseleave” event handlers – selectStars
and unselectStars
. In “selectStars”, you are adding the class to only one star. And in “unselectStars”, you were not resetting, or applying the “remove” class method to other stars.
Anyway, here is how I have achieved what you are trying to do:
const ratings = document.querySelectorAll('.rating'); ratings.forEach(rating => rating.addEventListener('mouseleave', ratingHandler) ); const stars = document.querySelectorAll('.rating .star'); stars.forEach(star => { star.addEventListener('mouseover', starSelection); star.addEventListener('mouseleave', starSelection); star.addEventListener('click', activeSelect); }); function ratingHandler(e) { const childStars = e.target.children; for(let i = 0; i < childStars.length; i++) { const star = childStars.item(i) if (star.dataset.checked === "true") { star.classList.add('hover'); } else { star.classList.remove('hover'); } } } function starSelection(e) { const parent = e.target.parentElement const childStars = parent.children; const dataset = e.target.dataset; const note = +dataset.note; // Convert note (string) to note (number) for (let i = 0; i < childStars.length; i++) { const star = childStars.item(i) if (+star.dataset.note > note) { star.classList.remove('hover'); } else { star.classList.add('hover'); } } } function activeSelect(e) { const parent = e.target.parentElement const childStars = parent.children; const dataset = e.target.dataset; const note = +dataset.note; // Convert note (string) to note (number) for (let i = 0; i < childStars.length; i++) { const star = childStars.item(i) if (+star.dataset.note > note) { star.classList.remove('hover'); star.dataset.checked = "false"; } else { star.classList.add('hover'); star.dataset.checked = "true"; } } const noteTextElement = parent.parentElement.lastElementChild.children.item(0) noteTextElement.innerText = `Note: ${note}`; }
You might notice I have a .rating
class component. This is a div
which I have created to hold all these “stars”. Here is a link to a codepen I have created. Feel free to play around with it.
And as a note, please provide codepen (or any other) demos so that we can debug a bit better and faster.
I hope the codepen link would help you solve your problem.