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

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

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

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

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

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

     

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

     

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

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

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

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

<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? — с удовольствием размещу информацию о них на своей странице модулей.

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

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