Skip to content
Advertisement

Star Rating Js + html with symfony

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.

User contributions licensed under: CC BY-SA
6 People found this is helpful
Advertisement