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

Хотя в последнее время все больше используются оценочные инструменты со сторонних сервисов, например “лайки” от 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");
}

Теперь необходимо добавить стили для состояния наведения:
.rating li:hover,
.rating li:hover ~ li {
background-position: 0 -20px;
}
Вторая строка и меняет фон для всех предыдущих элементов (но последующих по DOM – дереву).

Чтобы отобразить текущий усредненный рейтинг самым простым способом можно округлить среднее значение до целого и добавить класс к необходимому элементу списка:
<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;
}

Но это не сильно информативно, и лучше отображать рейтинг в процентах. Добавим еще один элемент к списку, который будет видно только когда у списка есть определенный класс – в нашем случае .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 на сервер и последующая отрисовка результата голосования
});

Для использования на продакшене можно конечно еще произвести ряд оптимизаций:
-
картинку можно через 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;
}

Выводы:
Поддержка в браузерах:
Все современные браузеры, плюс:
- Обычный рейтинг - >= IE8
- Половинчатый рейтинг - >= IE9 (из-за использования :nth-child)
Ну вот и все пожалуй, что я хотел рассказать. Это естественно не единственный вариант реализации, в сети вы можете найти множество других. Из основных можно выделить 2 способа: основанный на инпутах и вариант с заданием стилей для каждой отдельной звезды, но я думаю и мой вариант имеет право на жизнь (особенно после смерти IE8). До встречи.
ps. (в репозитории вы можете найти less – версию css'a)