Продолжая тему небольших полезных CSS-решений, предлагаю вашему вниманию интересную реализацию механизма рейтингов. С виду — всё просто: пять звёздочек, клик по каждой фиксирует рейтинг на позиции от одного до пяти баллов. Если же какой-то рейтинг уже имеется, при наведении он должен сбрасываться, чтобы пользователь имел возможность выразить своё мнение.

Каких только решений не придумано для этой тривиальной задачи — от сомнительного списка элементов <img> до более-менее внятных решений с помощью фоновых изображений. Но одно неизменно — всё это сдобрено хорошей порцией JS-кода, отвечающего за необходимое поведение. Что вполне логично приводит к тому, что без JS, в лучшем случае, не видно всей красоты, а в худшем — вообще нельзя проголосовать. В общем, не слишком хорошо.

Поэтому я предлагаю новое решение: пять звёздочек и его особенности:

  • Вся динамика максимально вынесена в CSS при помощи псевдо-класса :hover, JS применяется только для фиксирования результата голосования и для того, чтобы заставить IE6 понимать указанный псевдо-класс не только для ссылок, за счёт переключения дополнительного класса phover при взаимодествии с контролом.
  • Следовательно, при отключённом JS, во всех браузерах, кроме IE6 и младше, вся красота со звёздочками продолжает работать, а сам процесс голосования становится простым переходом по ссылке.
  • Для отрисовки звёздочек используется только одна картинка, таким образом — только один запрос и контрол уже готов к работе.
  • Потенциально, голосование становится возможно даже при отключённых картинках — в таком случае рейтинг выглядит как прогресс-бар. Очевидность контрола в этом случае ухудшается, но и это уже ценно.

Самая интересная часть CSS-кода:

  1. .voting A.cur,
  2. .voting A:hover,
  3. .voting:hover A.cur:hover,
  4. .voting:hover A:hover {
  5. background:#FC0 url(../i/stars.png) no-repeat;
  6. }
  7. .voting:hover A.cur {
  8. background:none;
  9. }

Признаюсь честно, вещей вроде E:hover E:hover { … } мне до сих пор писать не приходилось.

Также хотелось бы упомянуть про интересное JS-решение для определения версии IE, чтобы для шестой его версии и младше эмулировать псевдо-класс :hover для списков. И вот каким образом я получаю переменную ltIE7 (less than IE7):

  1. var ltIE7 = false
  2. /*@cc_on
  3. @if (@_jscript_version < 5.7)
  4. ltIE7 = true
  5. @end
  6. @*/

Эта пляска с собачками называется «условное выполнение» (Conditional Compilation) и формально вообще не является JavaScript'ом, это JScript. Если я не ошибаюсь, только его IE и понимает, а официальный JavaScript работает только благодаря довольно широкой совместимости этих языков. Что-то до боли знакомое, вы не находите? Прямо условные комментарии (Conditional Comments), только в JS-реинкарнации. Кто знает, если копнуть MSDN поглубже, может быть найдётся что-то похожее и для CSS?

По аналогии с предыдущим примером, можно получить и переменную ltIE6 (less than IE6), вычислив версию JScript, использующуюся в браузере. Для IE6 она равна 5.5:

  1. @if (@_jscript_version < 5.6)
  2. ltIE6 = true
  3. @end

Более подробное описание этой чудо-технологии можно найти в статье «Conditional Compilation Variables». Пятизвёздочных вам рейтингов ;)

Комментарии

  1. quard 28 Май 2008 / 19:16

    Хм, интересно. Когда нужно было сделать рейтинг звездочками, я долго искал вариант. Перепробовал по-моему штуки 3-4. У меня все время были проблемы с IE, то звездочки не хотели рисоваться при hover (хотя там был использован какой-то хак), то при результате они не закрашивались. Вобщем намучался :)

    Вопрос: как явно при загрузке указать сколько голосов уже отдано? Что-то парамтер не нашел такой.

  2. pepelsbey 28 Май 2008 / 19:20

    Да, наверное всё-таки стоит добавить в пример предустановленный класс. А вообще: <a href="#" class="cur">

  3. quard 28 Май 2008 / 19:26

    Жаль ещё что нет возможности выставить в процентах рейтинг. В моем варианте кажется было использована ширина закраски расчитанная в скрипте.

    Забыл добавить спасибо :) В новом проекте попробую использовать ;)

  4. pepelsbey 28 Май 2008 / 19:37

    Мне затея с заполненными наполовину звёздочками совсем не нравится — отличить на глаз 2,3 балла от 2,7 баллов я не смогу. Да и голосовать за такое мышкой будет нереально.

  5. quard 28 Май 2008 / 19:46

    Нет нет, голосовать можно только от 1 до 5 и только за один пункт. Просто при выводе будет процент всей суммы рейтинга к кол-ву голосов

  6. Chupa 28 Май 2008 / 22:40

    Если говорить о семантике, то, все-таки, необходимо использовать input type="radio"

  7. pepelsbey 29 Май 2008 / 1:03

    > Если говорить о семантике…

    Вообще, я согласен… Голосование это скорее отправка формы с одним из выбранных значений, чем переход на другую страницу с параметром. Стоит подумать как это дело трансформировать в звёздочки, попутно спрятав кнопку «Голосовать»…

  8. jahson 29 Май 2008 / 6:11

    Ты не ошибаешься, только ИЕ @сс и понимает )

  9. pepelsbey 29 Май 2008 / 9:23

    Олег, я в другом сомневался — что он понимает, JScript, совместимый с JS или непосредственно JavaScript…

  10. vashurin 29 Май 2008 / 9:25

    pepelsbey, спасибо, может и пригодится.
    А можно, одно не принципиальное улучшение? :)

    Мне кажется, что строку:
    this.className = this.className.replace( phover,'');

    Было бы лучше записать так:
    this.className = this.className.replace(/(^| )phover($| )/,'');
    Т.е. регулярным выражением проверять, что phover — это отдельное слово, а не часть другого слова (вдруг, завтра появится у того же элемента еще и класс, к примеру: "phovering").

    А вообще, пять звёздочек — интересная вещь и реализовано хорошо.

  11. Иван 29 Май 2008 / 10:29

    Выглядит красиво, только вот откуда взято свойство xdisplay? Впервые вижу такое:

    .voting SPAN {
    xdisplay:none;
    }

  12. pepelsbey 29 Май 2008 / 12:01

    Упс, это я дебажил так — это всё равно, что закомментировать, только невалидно.
    Удалю, спасибо )

  13. Lynn 29 Май 2008 / 12:46

    Кто знает, если копнуть MSDN поглубже, может быть найдётся что-то похожее и для CSS?
    В блоге IE вроде писали, что нет и не будет.

    Про JS у Dean'а Edwards'а в блоге развлекались.

  14. site-creator 30 Май 2008 / 0:57

    есть решение без js для ie, работает только с помощью hover:
    пример

  15. pepelsbey 30 Май 2008 / 1:06

    > есть решение без js для ie…

    Хм, интересно. Посмотрим…

  16. Vladimir 30 Май 2008 / 22:30

    > есть решение без js для ie

    Три бэкграунда? Тоже вариант, и весьма красивый...

    Свои пять копеек: еще одно решение без JS, плавающих элементов и z-index (я его полгода назад использовал в myfolio.com).

  17. Vladimir 31 Май 2008 / 2:29

    > Если говорить о семантике, то, все-таки, необходимо использовать input type="radio"

    Упаси Боже! Это один из тех примеров, когда семантика конфликтует с usability. Элементы форм должны выглядеть как элементы форм, и именно поэтому браузеры не дадут кардинально изменить внешний вид input type="radio". В противном случае дизайнеры бы уже давно извратились над всеми элементами форм :-)

    > Голосование это скорее отправка формы с одним из выбранных значений, чем переход на другую страницу с параметром.

    ...а отправка формы - это технически тот же переход на другую страницу с параметром.

    В любом случае, если извращаться с формой, то потребуется куда больше JavaScript. Ведь input type="radio" будет прорисовываться поверх любого бэкграунда, следовательно, так или иначе его придется убрать (либо скрыть, либо что-то спозиционировать на него сверху). Если радиобатон убрать, он не сможет получить фокус при клике (хотя если поместить input в label, то может и прокатит, я не пробовал). Но на мой взгляд, заморочек и потенциальных проблем здесь больше, чем преимуществ :-)

  18. pepelsbey 31 Май 2008 / 2:31

    Скрыть радиобатон/чекбокс, оставив их рабочими, не так сложно — найсбокс

  19. Vladimir 31 Май 2008 / 3:31

    Не удержался... Семантический вариант пяти звёздочек.

  20. pepelsbey 31 Май 2008 / 10:09

    Владимир, мне кажется, что используя подобную методику, можно вообще полсайта сверстать на таблицах с атрибутами и спрятать внутрь условных комментариев — чтобы в IE всё было красиво, а вторую половину нормально, со всякими CSS3-трюками.

    В общем, красота красотой, а я не готов: а) мусорить в коде лишними условными комментариями б) вкладывать блочные элементы в строчные, даже внутри условных комментариев.

  21. Vladimir 31 Май 2008 / 14:30

    Вопрос спорный... В данном случае условный комментарий нужен только для того, чтобы не использовать JavaScript для IE6 - никаких других целей (в том числе презентационных) он не несет. В принципе, я считаю, что те два комментария были оправданы, ибо в ТЗ стояло требование, чтобы все работало как с JS, так и без. А IE6 всё еще очень много народа пользуется.

    > можно вообще полсайта сверстать на таблицах с атрибутами и спрятать внутрь условных комментариев

    Возможно, я не пытался использовать такие хаки для презентационных возможностей. Но если так сделать, будет неудобство: нужно будет поддерживать две версии кода.

    > а) мусорить в коде лишними условными комментариями

    Тем не менее, те же условные комментарии, но под другим соусом появляются в JavaScript, а также условные комментарии используются для скармливания своего CSS ишакам меньше 8 версии :-)

    > б) вкладывать блочные элементы в строчные, даже внутри условных комментариев

    На мой взгляд, это то же самое, чито использовать zoom: 1 для hasLayout :-)

    В любом случае, условный комментарий можно убрать и повесить whatever:hover.

  22. Octane 31 Май 2008 / 19:55

    >> скармливания своего CSS ишакам меньше 8 версии

    сомневаюсь, что для 8ой версии не придется писать отдельные стили :D

  23. pepelsbey 31 Май 2008 / 23:03

    Скажу проще, использовать громоздкие условные комментарии с таблицей для каждой группы звёздочек — это безрассудно с т.з. массивности кода и беспринципно с т.з. светлой идеи правильной вёрстки.

    Я всё-таки стараюсь трюковать в отдельных файлах, а документ держать логически правильным и чистым.

    …а если можно убрать и повесить whatever:hover, то смысл?

  24. Vladimir 31 Май 2008 / 23:45

    > а если можно убрать и повесить whatever:hover, то смысл?

    Смысл в том, что whatever:hover - это JScript (htc-файл). Отключение JS отключает всякие htc.

    > это безрассудно с т.з. массивности кода и беспринципно с т.з. светлой идеи правильной вёрстки.

    Возможно... А с точки зрения accessibility? Как программист, занимающийся разработкой, хм, весьма своеобразных :-) сайтов, я обратил внимание, что не все рискуют включать JavaScript. Кто-то по соображениям безопасности (пользователи IE6, привет!), кто-то из-за того, что анонимайзер скрипты режет, кто-то с мобилок трафик экономит... не суть. На мой взгляд, пара десятков байт (которые можно очень сократить) особой роли не играет (тем более, если используется gzip), но в то же время позволяет таким пользователям нормально использовать сайт. Лично для меня accessibility в данном случае перевешивает.

    Я, например, против использования всяких expression в CSS: на мой взгляд, это не comme il faut, особенно, когда дизайн начинает расползаться при выключенном JS.

    > Я всё-таки стараюсь трюковать в отдельных файлах, а документ держать логически правильным и чистым.

    С другой стороны, Vary: user-agent и Content Negotiation тоже никто не отменял, хотя здесь во мне говорит программист.

    PS - про чистоту условно согласен, про логическую правильность - нет: комментарии на логику не влияют :-)

    > что для 8ой версии не придется писать отдельные стили

    В IE8 beta1 приходится - с отрицательными границами там не всё гладко. А также из-за распознавания стилей, предназначенных для IE/Mac (это о вреде хаков).

  25. Макс Лапшин 4 Июнь 2008 / 19:50

    Вадик, хочу тебе сразу сказать, что делать рейтингование ссылкой — очень плохая идея. Дело в том, что есть очень хорошее правило: GET запрос не должен ничего модифицировать. Это не просто прихоть, а реальная головная боль, которую создает, например, Google Page Accelerator, который просто подтаскивает все урлы на странице.

    Т.е. если хочется делать полный fallback механизм, то надо делать форму с map-ом.

  26. sunnybear 12 Июнь 2008 / 22:46

    вставлю свои пять копеек. Вообще, input'ы нужно. Зашел к ваш человек с отключенныим картинками -- что он увидел? ничего... Этот случай-то фиксится через какой-то текст в ссылках. А если еще и с отключенными стилями и скрипты сломались?

    Как вариант -- рисовать звездочки поверх соответствующих input'ов, перекрывая их через normal flow (чтобы с zIndex возни было поменьше, но это уже на любителя).

    Просто на западе достаточно много мусолят тему WCAG / WAI / ARIA, у нас не то, что забивают, обычно об этом просто даже не подозревают.

    P.S. при загрузке
    http://blog.sjinks.org.ua/css/173-five-stars-without-javascript/
    у меня в первый раз не подключились стили -- там с хостингом все ок?

  27. Петр 20 Июнь 2008 / 3:09

    Это сильно!

  28. Юрий 15 Июль 2008 / 15:57

    Интересно, как заставить эти звездочки не быть активными (то есть, когда юзер уже проголосовал, надо высвечивать строго текущий рейтинг и не мигать звездами при наведении мышкой).

    Пробовал оставить только cur-элемент, но почему-то при наведении мышью на ul.voter правее, чем правая граница cur, весь ul.voter закрашивается серыми звездами, хотя стиля для hover тут никакого нет...

  29. pepelsbey 15 Июль 2008 / 16:09

    Чтобы сделать рейтинг неактивным, нужно после клика прописать JS'ом для UL какой-нибудь класс, вроде inactive, а в CSS описать, что с таким классом UL ведёт себя как бревно и ни на что не реагирует.

    Уж простите, это не ultimate-решение, а лишь интересная методика, которую можно развить под себя.

    Примерно так.

  30. Юрий 15 Июль 2008 / 16:33

    Решение оказалось несложным. Если нужно сделать так, чтобы рейтинг оставался фиксированным - надо:
    1. выводить в HTML только элемент с классом cur;
    2. назначить ul дополнительный класс inactive;
    3. дописать в CSS после (именно после!) стиля
    .voting:hover a.cur,
    .voting.phover a.cur {
    background:none
    }
    этот стиль:
    .inactive:hover a.cur{
    background:#FC0 url('../../images/msk/images_ru/stars.png') no-repeat
    }

    Если будут баги - пишите )

  31. pepelsbey 15 Июль 2008 / 16:36

    Спасибо.
    Я наверное обновлю пример подобной ситуацией для большей гибкости.

  32. Юрий 15 Июль 2008 / 16:41

    Или более простой способ:
    можно не удалять лишние элементы списка, но при этом надо дописать в стили после
    .voting:hover a.cur,
    .voting.phover a.cur {
    background:none
    }

    вместо указанного в моем каменте выше вот это:
    .inactive a:hover,
    .inactive:hover a:hover,
    .inactive.phover a:hover {
    background:none
    }
    .inactive:hover a.cur,
    .inactive.phover a.cur:hover{
    background:#FC0 url('../../images/msk/images_ru/stars.png') no-repeat
    }

  33. pepelsbey 15 Июль 2008 / 16:42

    А вот это уже ближе к тому, о чём я вам говорил )

  34. Артур 1 Август 2008 / 18:50

    не пойму для чего нужно voting:hover?
    только что убрал его, работает отлично без никаких глюков, + для ie ничего не нужно придумывать
    + добавил опционально возможность неактивности звездочек после голоса, чтобы нельзя было повторно проголосовать
    + внутри невидимый input туда заносится результат

  35. pepelsbey 1 Август 2008 / 19:28

    Селектор .voting:hover нужен для того, чтобы сбрасывать предустановленное значение рейтинга для пользователя, который хочет проголосовать. Мне кажется, что это весьма удобный механизм, когда происходит не просто клик по звёздочке без отдачи во время выбора, а именно такой динамический ряд.

Разрешённые теги: <a href=""> <strong> <b> <em> <i> <cite> <del>

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

*
*