
Доброго времени суток дамы и господа. С помощью этой статьи я хотел бы познакомиться с вами, а заодно опробовать себя в качестве автора. А для того чтобы это знакомство не проходило зря, мы рассмотрим с вами некоторые варианты создания инструмента для оценки чего либо. В интернете прижился такой визуальный элемент как “звездочки”:
К примеру:
Хотя в последнее время все больше используются оценочные инструменты со сторонних сервисов, например “лайки” от 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)