Звездный рейтинг

Доброго времени суток дамы и господа. С помощью этой статьи я хотел бы познакомиться с вами, а заодно опробовать себя в качестве автора. А для того чтобы это знакомство не проходило зря, мы рассмотрим с вами некоторые варианты создания инструмента для оценки чего либо. В интернете прижился такой визуальный элемент как “звездочки”:

К примеру:

Хотя в последнее время все больше используются оценочные инструменты со сторонних сервисов, например “лайки” от Facebook'a или от Вконтакте, которые позволяют оценить материал только в положительном ключе. Хорошо это или плохо, мы с вами решим как - нибудь в другой раз, а сейчас давайте рассмотрим несколько вариантов реализации традиционных “звездочек”.

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

После этого мы можем перейти к самой верстке.

Обычный рейтинг

Начнем мы пожалуй с самого простого: 5 целых звезд, при наведении также должно меняться состояние у предыдущих. Семантически пожалуй наиболее подходит упорядоченный список. Почему именно упорядоченный? Тут все просто, звездочки имеют свой “вес”: от одного до пяти (ну или сколько у вас там зведочек :) ).

Итак структура простая:

<ol id="rating">
    <li>1</li>
	<li>2</li>
	<li>3</li>
	<li>4</li>
    <li>5</li>          
</ol>

Давайте сразу подумаем как же мы можем выделить предыдущие элементы списка при наведении на один из них. Наверняка многие знают о css селекторе “~”. Для тех кто не знает, он позволяет выбрать всех последующих соседей, т.е. работает как +, но с оговоркой на “всех”. Но как он нам поможет? Ведь нам надо выбрать предыдущие элементы, а не следующие... Тут мы применим нам нашу смекалку и отсортировав элементы списка в обратном порядке применим к ним float: right. В итоге мы получим следующую структуру:

<ol id="rating">
    <li>5</li>
    <li>4</li>
    <li>3</li>
    <li>2</li>
    <li>1</li>
</ol>

Дальше все просто:  

.rating {
  list-style: none;
  margin: 0;
  padding: 0;
  width: 100px;
  height: 20px;
}

.rating li {
  display: block;
  width: 20px;
  height: 20px;
  float: right; /* обтекание по правой стороне как раз необходимо для выстравания элементов  в обратном порядке */
  text-indent: -9999px; /* скрываем текст */
  cursor: pointer;
  background: url("stars.png");
}

Демо 1

Теперь необходимо добавить стили для состояния наведения:

.rating li:hover,
.rating li:hover ~ li {
  background-position: 0 -20px;
}

Вторая строка и меняет фон для всех предыдущих элементов (но последующих по DOM – дереву).

Демо 2

Чтобы отобразить текущий усредненный рейтинг самым простым способом можно округлить среднее значение до целого и добавить класс к необходимому элементу списка:

<ol id="rating">
    <li>5</li>
    <li class=”active”>4</li>
    <li>3</li>
    <li>2</li>
    <li>1</li>
</ol>

Ну и конечно дописать чуть стилей:

.rating li.active,
.rating li.active ~ li {
  background-position: 0 -40px;
}

Демо 3

Но это не сильно информативно, и лучше отображать рейтинг в процентах. Добавим еще один элемент к списку, который будет видно только когда у списка есть определенный класс – в нашем случае .show-current. Окончательная структура списка приобретает следующий вид: 

<ol class="rating show-current">
    <li>5</li>
	<li>4</li>
	<li>3</li>
	<li>2</li>
	<li>1</li>
	<li class="current"><span></span></li>
</ol>

Для того чтобы наши звездочки ожили, приправим все это небольшим количеством JS'a:

var $rating = $('.rating');

$rating.on('mouseover', function() {
    $(this).removeClass('show-current');
}).on('mouseleave', function() {
    $(this).addClass('show-current');
});

$('li', $rating).on('click', function() {
    alert('User selected ' + $(this).text());
    //Тут естественно может (и должен) располагаться более сложный код, например отправка результата голосования с помощью ajax на сервер и последующая отрисовка результата голосования
});

Демо 4

Для использования на продакшене можно конечно еще произвести ряд оптимизаций:

  • картинку можно через data:URL включить в css, тем самым избавившись от лишнего HTTP запроса

  • Для отрисовки текущего рейтинга с некоторой анимацией можно:

    • изначально установить ширину в 0 пикселей

    • добавить css-transition собственно для анимации средствами браузера

    • с некоторой задержкой через js задавать текущую ширину

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

"Половинчатый" рейтинг

Также часто можно увидеть когда звездочки не целые, а поделены на двое. Для создания такого рейтинга можно использовать ту же технику, но прийдется поправить стили с оговоркой на “половинчатость”. Все описывать не буду, просто приведу окончательную структуру и стили с некоторыми комментариями.

Структура рейтинга:

<ol class="rating-half show-current">
    <li>10</li>
	<li>9</li>
	<li>8</li>
	<li>7</li>
	<li>6</li>
	<li>5</li>
	<li>4</li>
	<li>3</li>
	<li>2</li>
	<li>1</li>
	<li class="current"><span></span></li>
</ol>

Стили же уже посложнее:

li {
        display: block;
        width: 10px;
        height: 20px;
        float: right;
        text-indent: -9999px;
        cursor: pointer;
        background: url("img/stars.png") no-repeat;
}
li.current {
    display: none;
}


ul.show-current{
	position: relative;
}
ul.show-current li {
	cursor: default;
}
ul.show-current li.current {
	position: absolute;
	top: 0;
	left: 0;
	display: block;
	width: 0;
	background-position: 0 -60px;
	background-repeat: repeat-x;
}
ul.show-current li.current:hover,
ul.show-current li.current:hover ~ li {
	background-position: 0 -60px;
}

ul.show-current li.current span {
	display: block;
	height: 20px;
	width: 0;
	background: inherit;
	background-position: 0 -40px;
}

/* нечетные элементы */
li:nth-child(odd) {
	background-position: -10px 0;
}

 /* четные при наведении и все предыдущие четные*/
li:nth-child(even):hover,
li:nth-child(even):hover ~  li:nth-child(even){
	background-position: 0 -20px;
}

/* четные при наведении и все предыдущие нечетные*/
li:nth-child(even):hover ~  li:nth-child(odd){
	background-position: -10px -20px;
}

/* нечетные при наведении и все предыдущие нечетные*/
li:nth-child(odd):hover,
li:nth-child(odd):hover ~ li:nth-child(odd) {
	background-position: -10px -20px;
}

/* нечетные при наведении и все предыдущие четные*/
li:nth-child(odd):hover ~ li:nth-child(even) {
	background-position: 0 -20px;
}

Демо 5

Выводы:

Поддержка в браузерах:

Все современные браузеры, плюс:

  • Обычный рейтинг - >= IE8
  • Половинчатый рейтинг - >= IE9 (из-за использования :nth-child)

Ну вот и все пожалуй, что я хотел рассказать. Это естественно не единственный вариант реализации, в сети вы можете найти множество других. Из основных можно выделить 2 способа: основанный на инпутах и вариант с заданием стилей для каждой отдельной звезды, но я думаю и мой вариант имеет право на жизнь (особенно после смерти IE8). До встречи.

ps.репозитории вы можете найти less – версию css'a)

Читайте также:

Изпользование CSS вместе с RequireJS

В нашей разработке мы используем RequireJS. Но часто для использования библиотек необходимо подключить стили. Для этого мы воспользуемся плагинами для RequireJS и стандартным файлом настройки. Для того чтобы подгрузить необходимы библиотеки можете прочитать статью.

Timeago в Symfony2

В проекте необходимо реализовать дату в виде “год назад”, “10 минут назад” и тп. Для этого есть много решений, но мы воспользуемся TwigExtensions.

Менеджеры Js/Css библиотек при работе над Symfony проектом

В процессе разработки, нам необходимо использовать стороние библиотеки, и соответсвенно необходимо централизованно их устанавливать. Прошли те времена когда для того чтобы поставить библиотеку, надо было скачать скрипты и распаковать их себе в проект. Сейчас для этого используют менеджеры библиотек, их очень много например: Сomposer, Bower, Npm, Component и другие. В проектах на Symfony2 используется Composer, но им не очень удобно устанавливать js/css библиотеки, поскольку все библиотеки ставятся в папку которая не должна быть доступна из браузера. Мы рассмотрим какими способами в Symfony2 проекте можно ставить js/css библиотеки, некоторые способы подойдут любому проекту в котором используется Composer.