Пример слайдера, управляемого только с помощью CSS3

от автора

Стремительное развитие программного обеспечения, а параллельно с ним ещё более резвый рост производительности компьютерного оборудования, растворяют в себе злободневность полемики об эффективном коде. И вот уже в очередном проекте 2-3-страничного сайта нам проще использовать нечто типа jQuery(‘.spoiler’).show(), чтобы оживить статичный сайт. Ведь за мощью компьютера совсем не заметно расточительство ресурсов, вызванное цепочкой внутри библиотечных действий от такой команды, и только ради организации простейшей бизнес-логики на клиентской стороне.

Нынче в том нет ничего зазорного, ведь эволюция ПО и техники позволяет программисту решать задачу, не заботясь о цене и вникании в тонкости процесса. Однако для повышения кругозора и как замечательный экспонат в кунсткамеру, дам ссылку на пример того, что определённая часть бизнес-логики сайта, посвящённая оживлению статики, в принципе могла быть реализована вообще без применения скриптовых технологий.

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

Всё построено на особенностях двух селекторов:

  • родственного (+) — он указывает на первого соседа справа, то есть на элемент, размещённый в html-разметке следом за опорным элементом, например (сразу извиняюсь за познавательный стиль написания кода, только чтобы понимал и неопытный человек, здесь привлеку вымышленные русские названия вместо тегов и ниже вместо имён классов)
    ОПОРНЫЙ + СОСЕД {     стили, применяемые к соседу справа } 

  • обобщённого родственного (~) — он указывает на всех соседей справа, то есть на всякий элемент, размещённый в html-разметке сразу же после или на некотором отдалении от опорного элемента, но обязательно на том же уровне иерархии (то есть имеющих того же родителя, что и опорный элемент), например
    ОПОРНЫЙ ~ СОСЕД {     стили, применяемые к каждому соседу справа } 

Создаём навигаторы — кнопки, флажки, переключатели, тумблеры

Существует в HTML удобная самоуправляющаяся связка тегов — это <label><input></label>, где опорным элементом для нас выступил бы тег <input>, саму же связку легко стилизовать как под кнопку, так и переключатель. Однако в CSS3 не предусмотрен селектор, который бы указывал, что стили будут применяться не к его концевому элементу, а какому-то предшествующему в строке селектора. Такая особенность появится только в CSS4.

Выйти из положения позволит родственный селектор. Только тег <input> придётся вынести перед тегом <label>, то есть сделать их ближайшими соседями. Это даст хранить состояние воображаемой кнопки за счёт того, что оно уже хранится флажком, и управлять стилями кнопки за счёт того, что она является правым соседом флажка. Причём флажок становится лишь тенью, а потому с помощью стилей вообще скрывается от показа на странице.

Вот как это выглядит в html-разметке (на CSS4 атрибуты id, name, for не понадобились бы, здесь они используются лишь для пометки — что с чем связано и где теневой элемент):

<input id="меткаСвязи" name="меткаТени" type="checkbox" /> <label class="типНавигатора меткаНавигатора" for="меткаСвязи">     текст кнопки </label> 

Так это выглядит в стилях (здесь скрываем теневой элемент и стилизуем кнопку согласно её типу, скажем это могли быть «кнопка», «флажок», «переключатель», «тумблер» и так далее — сколько бы нам понадобилось разных видов навигаторов):

[name="меткаТени"] {     display: none; }  [name="меткаТени"] + .типНавигатора {     стили для кнопки данного типа } 

Создаём общие обработки событий

В отличие от частных обработок, предназначенных для отмеченного навигатора на странице, общие обработки задают стиль всякого навигатора соответствующего типа при наступлении определённого события. Например, курсор над кнопкой. Например, тумблер в положении ВКЛЮЧЕНО. И тому подобное.

[name="меткаТени"] + .типНавигатора:hover {     стили, когда курсор над кнопкой }  [name="меткаТени"]:checked + .типНавигатора {     стили, когда находится в состоянии ВКЛЮЧЕНО }  [name="меткаТени"]:not(:checked) + .типНавигатора {     стили, когда в состоянии ВЫКЛЮЧЕНО }  [name="меткаТени"]:disabled + .типНавигатора {     стили, когда в состоянии ЗАПРЕЩЁН }  [name="меткаТени"]:not(:disabled) + .типНавигатора {     стили, когда в состоянии РАЗРЕЩЁН }  [name="меткаТени"]:indeterminate + .типНавигатора {     стили, когда в неопределённом состоянии } 

Создаём части контента

Здесь всё как обычно — тривиальные блоки html-разметки, в которых располагаем контент как нам удобно. Только части, какие будут управляться навигаторами, необходимо снабдить какой-нибудь уникальной меткой, чтобы к этим частям можно было сослаться. Например

<form>     ля-ля-ля      <div class="управляемыйЭлемент меткаСпойлера1">         некое уточнение     </div>      <div>         ля-ля-ля          <div class="управляемыйЭлемент меткаУведомления1">             Не заполнили имя!         </div>          ля-ля-ля          <div class="управляемыйЭлемент меткаУведомления2">             Не заполнили емейл!         </div>          ля-ля-ля     </div>      <div class="управляемыйЭлемент изначальноВидимый меткаПанели1">         выдвигающаяся панель     </div>      ля-ля-ля </form>  <div class="управляемыйЭлемент изначальноВидимый меткаУведомления3">     Заполните предложенную форму! </div> 

И в стилях прописать внешний вид управляемых элементов. Например они изначально не видны, кроме явно помеченных, и раскрашены цветами.

.управляемыйЭлемент {     display: none; }  .управляемыйЭлемент.изначальноВидимый {     display: block; }  .меткаУведомления1, .меткаУведомления2 {     color: red; }  .меткаУведомления3 {     color: green; }  .меткаПанели1 {     width: 20px; } 

Необходимо учесть, что стилизационный доступ к управляемым частям будет происходить с помощью обобщённого родственного селектора, следовательно такие части не могут располагаться в html-разметке выше навигатора, со стороны которого инициируется доступ к управляемому элементу.

Кроме того, корневые узлы DOM-веток, в которых размещены управляемые элементы, должны быть одноуровневыми соседями навигаторов.

Создаём частные обработки событий

Эти обработки похожи на общие, только задаются в отношении конкретного навигатора и с применением обобщённого родственного селектора. Например по включению Кнопки1 раздвинем Панель1.

[name="меткаТени"]:checked + .меткаКнопки1 ~ .меткаПанели1, [name="меткаТени"]:checked + .меткаКнопки1 ~ * .меткаПанели1 {     width: 300px; } 

Двойная запись здесь означает, что панель либо размещена по соседству с кнопкой, либо в каком-то из соседей кнопки.

Ещё пример — по включению Кнопки1 покажем Спойлер1.

[name="меткаТени"]:checked + .меткаКнопки1 ~ .меткаСпойлера1, [name="меткаТени"]:checked + .меткаКнопки1 ~ * .меткаСпойлера1 {     display: block; } 

Ещё пример — по выключению Флажка1 скроем Уведомление3.

[name="меткаТени"]:not(:checked) + .меткаФлажка1 ~ .меткаУведомления3, [name="меткаТени"]:not(:checked) + .меткаФлажка1 ~ * .меткаУведомления3 {     display: none; } 

Иногда придётся прибегнуть к !important, чтобы действие одних обработок не перекрыло стилизацию логически более важных обработок. Ведь порядок обработки стилей подчиняется собственным правилам.

Очевидные недостатки:

  • особенности обобщённого родственного селектора вынуждают располагать навигатор в html-разметке ранее управляемой части контента;
  • те же особенности селектора не дают размещать навигатор в глубине другой DOM-ветки, чтобы он не имел прямого соседства с DOM-веткой управляемого контента (это появится в CSS4);
  • отсутствие селектора прямого родителя вынуждает выносить теневой флажок перед кнопкой в html-разметке и добавлять во флажок и кнопку лишние атрибуты, только чтобы указать их связанность, а также порождает лишние конструкции в стилях (это появится в CSS4);
  • проблема разрозненности теневого флажка и кнопки может быть решена и в CSS3 за счёт отказа от кнопки и превращения флажка в неё (более точно, кнопку подменит псевдо элемент :before или :after), однако не все браузеры поддерживают такое превращение, чтобы не вмешиваться в нашу стилизацию (отдельные атрибуты оказываются не перекрываемыми, например -moz-appearance: none не действует на <input type="checkbox"> в Firefox).

От автора:

  • в примере я обошёл тему анимации слайдера, она не являлась целью примера, потому сделана простая — показать / скрыть элемент, попробуйте поиграть свойством transition или эффектами из animate.css, если вам это интересно;
  • вы можете делать бесплатные или коммерческие модули, управляемые чисто на CSS? — с удовольствием размещу информацию о них на своей странице модулей.

Скриншот примера в полный размер:

ссылка на оригинал статьи http://habrahabr.ru/post/194090/