Помогите сделать функционал кнопок отзыва на сайте

Рейтинг: -2Ответов: 2Опубликовано: 29.03.2023

В моем макете присутствуют кнопки-оценки пользователей, то есть отзывы, как сделать так,чтобы пользователь сам мог выбрать кликом оценку которая ему нужна, вот скрин пример как должно выглядеть (кнопки представлены в виде звездочек)

введите сюда описание изображения

Ответы

▲ 1Принят

В одном из проектов я писал такую колбасу jQuery для звёзд.
Уверен кто-то сможет и сократить код, но я не умею)).
В общем рабочий вариант, а если нужно с надписями, то нужно раскомментировать в html - class="rating_message"

введите сюда описание изображения

$(function() {
  let stars = $('.rating_wrap .star');
  let starsCurrent = $('.stars_current');
  let message = $('.rating_message');
  stars.on('click', function () {
      let currentStar = $(this);
      if (currentStar.data('current_width') === 20) {
          starsCurrent.css("width",currentStar.data('current_width') + '%');
          starsCurrent.attr('data-rating',currentStar.data('rating_value'));
          starsCurrent.data('rating',currentStar.data('rating_value'));
          message.text(currentStar.data('message'));
      } else if (currentStar.data('current_width') === 40) {
          starsCurrent.css("width",currentStar.data('current_width') + '%');
          starsCurrent.attr('data-rating',currentStar.data('rating_value'));
          starsCurrent.data('rating',currentStar.data('rating_value'));
          message.text(currentStar.data('message'));
      } else if (currentStar.data('current_width') === 60) {
          starsCurrent.css("width",currentStar.data('current_width') + '%');
          starsCurrent.attr('data-rating',currentStar.data('rating_value'));
          starsCurrent.data('rating',currentStar.data('rating_value'));
          message.text(currentStar.data('message'));
      } else if (currentStar.data('current_width') === 80) {
          starsCurrent.css("width",currentStar.data('current_width') + '%');
          starsCurrent.attr('data-rating',currentStar.data('rating_value'));
          starsCurrent.data('rating',currentStar.data('rating_value'));
          message.text(currentStar.data('message'));
      } else if (currentStar.data('current_width') === 100) {
          starsCurrent.css("width",currentStar.data('current_width') + '%');
          starsCurrent.attr('data-rating',currentStar.data('rating_value'));
          starsCurrent.data('rating',currentStar.data('rating_value'));
          message.text(currentStar.data('message'));
      }
  });
  stars.on('mouseover', function () {
      let currentStar = $(this);
      if (currentStar.data('current_width') === 20) {
          starsCurrent.css("width",currentStar.data('current_width') + '%');
          message.text(currentStar.data('message'));
      } else if (currentStar.data('current_width') === 40) {
          starsCurrent.css("width",currentStar.data('current_width') + '%');
          message.text(currentStar.data('message'));
      } else if (currentStar.data('current_width') === 60) {
          starsCurrent.css("width",currentStar.data('current_width') + '%');
          message.text(currentStar.data('message'));
      } else if (currentStar.data('current_width') === 80) {
          starsCurrent.css("width",currentStar.data('current_width') + '%');
          message.text(currentStar.data('message'));
      } else if (currentStar.data('current_width') === 100) {
          starsCurrent.css("width",currentStar.data('current_width') + '%');
          message.text(currentStar.data('message'));
      }
  });

  stars.on('mouseout', function () {
      if (starsCurrent.data('rating') === 0) {
          starsCurrent.css("width",0);
          message.text('Без оценки');
      } else {
          let width = stars.filter('[data-rating_value='+starsCurrent.data('rating')+']').data('current_width');
          let messageVal = stars.filter('[data-rating_value='+starsCurrent.data('rating')+']').data('message');
          starsCurrent.css("width",width+'%');
          message.text(messageVal);
      }
  });
});
.rating {
  float: left;
  position: relative;
  width: 150px;
  height: 24px;
  font-size: 0;
  line-height: 0;
  background: url("https://i.sstatic.net/ceEGP.jpg") 0 -26px no-repeat;
}

.rating .star {
  position: relative;
  z-index: 1;
  margin: 0;
  padding: 0;
  height: 24px;
  display: inline-block;
  width: 20%;
  background: none;
  cursor: pointer;
  border-radius: 0;
}

.rating .stars_current {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  display: block;
  width: 0;
  background: url("https://i.sstatic.net/ceEGP.jpg") 0 0 no-repeat;
}

.rating_message {
  margin: 0 0 0 13px;
  float: left;
  position: relative;
  padding: 0 0 0 30px;
  color: #999999;
}

.rating_message:before {
  content: "—";
  position: absolute;
  left: 0;
  top: 0;
}
<div class="rating_wrap clearfix">
  <div class="rating">
    <span class="star" data-current_width="20" data-rating_value="1" data-message="Очень плохо"></span>
    <span class="star" data-current_width="40" data-rating_value="2" data-message="Плохо"></span>
    <span class="star" data-current_width="60" data-rating_value="3" data-message="Нормально"></span>
    <span class="star" data-current_width="80" data-rating_value="4" data-message="Хорошо"></span>
    <span class="star" data-current_width="100" data-rating_value="5" data-message="Отлично"></span>
    <span class="stars_current" data-rating="0"></span>
  </div>
  <!-- <div class="rating_message" data-message="Без оценки">Без оценки</div> -->
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>

▲ 0

Можно так

const svgStarNode = document.querySelector('.svg-star');

svgStarNode.addEventListener('mousemove', (e) => {
  const widthStarNode = svgStarNode.clientWidth;
  const xPercent = Math.round((e.clientX / widthStarNode) * 100);
  
  svgStarNode.style.setProperty('--x', `${xPercent}%`);
});
.svg-star {
  --x: 0%;
}

.svg-star .rect-percent {
  x: var(--x);
}
<svg class="svg-star" width="160" height="32" viewBox="0 0 160 32">
  <defs>
    <mask id="perc">
      <rect x="0" y="0" width="100%" height="100%" fill="white" />
      <rect class="rect-percent" x="0%" y="0" width="100%" height="100%" fill="grey" />
    </mask>

    <symbol viewBox="0 0 32 32" id="star">
      <path d="M31.547 12a.848.848 0 00-.677-.577l-9.427-1.376-4.224-8.532a.847.847 0 00-1.516 0l-4.218 8.534-9.427 1.355a.847.847 0 00-.467 1.467l6.823 6.664-1.612 9.375a.847.847 0 001.23.893l8.428-4.434 8.432 4.432a.847.847 0 001.229-.894l-1.615-9.373 6.822-6.665a.845.845 0 00.214-.869z" />
    </symbol>
    <symbol viewBox="0 0 160 32" id="stars">
      <use xlink:href="#star" x="-64" y="0"></use>
      <use xlink:href="#star" x="-32" y="0"></use>
      <use xlink:href="#star" x="0" y="0"></use>
      <use xlink:href="#star" x="32" y="0"></use>
      <use xlink:href="#star" x="64" y="0"></use>
    </symbol>
  </defs>

  <use xlink:href="#stars" fill="yellow" stroke="black" mask="url(#perc)"></use>
</svg>

Или по звёздам только целым

const svgStarNode = document.querySelector('.svg-star');
const quantityStars = document.querySelectorAll('#stars use').length;

svgStarNode.addEventListener('mousemove', (e) => {
  const widthStarNode = svgStarNode.clientWidth;
  const xPercent = Math.round((e.clientX / widthStarNode) * 100);
  const basePercent = 100 / quantityStars;
  const roundedPercent = Math.round(xPercent / basePercent) * basePercent;
  
  svgStarNode.style.setProperty('--x', `${roundedPercent}%`);
});
.svg-star {
  --x: 0%;
}

.svg-star .rect-percent {
  x: var(--x);
}
<svg class="svg-star" width="160" height="32" viewBox="0 0 160 32">
  <defs>
    <mask id="perc">
      <rect x="0" y="0" width="100%" height="100%" fill="white" />
      <rect class="rect-percent" x="0%" y="0" width="100%" height="100%" fill="grey" />
    </mask>

    <symbol viewBox="0 0 32 32" id="star">
      <path d="M31.547 12a.848.848 0 00-.677-.577l-9.427-1.376-4.224-8.532a.847.847 0 00-1.516 0l-4.218 8.534-9.427 1.355a.847.847 0 00-.467 1.467l6.823 6.664-1.612 9.375a.847.847 0 001.23.893l8.428-4.434 8.432 4.432a.847.847 0 001.229-.894l-1.615-9.373 6.822-6.665a.845.845 0 00.214-.869z" />
    </symbol>
    <symbol viewBox="0 0 160 32" id="stars">
      <use xlink:href="#star" x="-64" y="0"></use>
      <use xlink:href="#star" x="-32" y="0"></use>
      <use xlink:href="#star" x="0" y="0"></use>
      <use xlink:href="#star" x="32" y="0"></use>
      <use xlink:href="#star" x="64" y="0"></use>
    </symbol>
  </defs>

  <use xlink:href="#stars" fill="yellow" stroke="black" mask="url(#perc)"></use>
</svg>

Плюс можно обнулять процент при уведении

const svgStarNode = document.querySelector('.svg-star');
const quantityStars = document.querySelectorAll('#stars use').length;

svgStarNode.addEventListener('mousemove', (e) => {
  const widthStarNode = svgStarNode.clientWidth;
  const xPercent = Math.round((e.clientX / widthStarNode) * 100);
  const basePercent = 100 / quantityStars;
  const roundedPercent = Math.round(xPercent / basePercent) * basePercent;
  
  svgStarNode.style.setProperty('--x', `${roundedPercent}%`);
});

svgStarNode.addEventListener('mouseout', () => {
  svgStarNode.style.setProperty('--x', `0%`);
});
.svg-star {
  --x: 0%;
}

.svg-star .rect-percent {
  x: var(--x);
}
<svg class="svg-star" width="160" height="32" viewBox="0 0 160 32">
  <defs>
    <mask id="perc">
      <rect x="0" y="0" width="100%" height="100%" fill="white" />
      <rect class="rect-percent" x="0%" y="0" width="100%" height="100%" fill="grey" />
    </mask>

    <symbol viewBox="0 0 32 32" id="star">
      <path d="M31.547 12a.848.848 0 00-.677-.577l-9.427-1.376-4.224-8.532a.847.847 0 00-1.516 0l-4.218 8.534-9.427 1.355a.847.847 0 00-.467 1.467l6.823 6.664-1.612 9.375a.847.847 0 001.23.893l8.428-4.434 8.432 4.432a.847.847 0 001.229-.894l-1.615-9.373 6.822-6.665a.845.845 0 00.214-.869z" />
    </symbol>
    <symbol viewBox="0 0 160 32" id="stars">
      <use xlink:href="#star" x="-64" y="0"></use>
      <use xlink:href="#star" x="-32" y="0"></use>
      <use xlink:href="#star" x="0" y="0"></use>
      <use xlink:href="#star" x="32" y="0"></use>
      <use xlink:href="#star" x="64" y="0"></use>
    </symbol>
  </defs>

  <use xlink:href="#stars" fill="yellow" stroke="black" mask="url(#perc)"></use>
</svg>

Плюс, так как я изменяю CSS свойство x у svg элемента, его можно плавно изменять с помощью transition

const svgStarNode = document.querySelector('.svg-star');
const quantityStars = document.querySelectorAll('#stars use').length;

svgStarNode.addEventListener('mousemove', (e) => {
  const widthStarNode = svgStarNode.clientWidth;
  const xPercent = Math.round((e.clientX / widthStarNode) * 100);
  const basePercent = 100 / quantityStars;
  const roundedPercent = Math.round(xPercent / basePercent) * basePercent;
  
  svgStarNode.style.setProperty('--x', `${roundedPercent}%`);
});

svgStarNode.addEventListener('mouseout', () => {
  svgStarNode.style.setProperty('--x', `0%`);
});
.svg-star {
  --x: 0%;
}

.svg-star .rect-percent {
  x: var(--x);
  transition: x 0.1s ease;
}
<svg class="svg-star" width="160" height="32" viewBox="0 0 160 32">
  <defs>
    <mask id="perc">
      <rect x="0" y="0" width="100%" height="100%" fill="white" />
      <rect class="rect-percent" x="0%" y="0" width="100%" height="100%" fill="grey" />
    </mask>

    <symbol viewBox="0 0 32 32" id="star">
      <path d="M31.547 12a.848.848 0 00-.677-.577l-9.427-1.376-4.224-8.532a.847.847 0 00-1.516 0l-4.218 8.534-9.427 1.355a.847.847 0 00-.467 1.467l6.823 6.664-1.612 9.375a.847.847 0 001.23.893l8.428-4.434 8.432 4.432a.847.847 0 001.229-.894l-1.615-9.373 6.822-6.665a.845.845 0 00.214-.869z" />
    </symbol>
    <symbol viewBox="0 0 160 32" id="stars">
      <use xlink:href="#star" x="-64" y="0"></use>
      <use xlink:href="#star" x="-32" y="0"></use>
      <use xlink:href="#star" x="0" y="0"></use>
      <use xlink:href="#star" x="32" y="0"></use>
      <use xlink:href="#star" x="64" y="0"></use>
    </symbol>
  </defs>

  <use xlink:href="#stars" fill="yellow" stroke="black" mask="url(#perc)"></use>
</svg>