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.