Pull to refresh

Семантика для CSS селекторов и комбинаторов

Reading time 5 min
Views 47K
Original author: Chris Sealey
Синтаксис CSS несложен, и для понимания его совсем не нужно иметь степень доктора в области IT. Однако, это один из немногих популярных языков, который не является логичным в самом прямом смысле этого слова. В отличие от других языков веб-программирования, таких как JavaScript и PHP, в CSS проблемы не решаются с помощью обычной логики. Алгоритмы типа «если X, то сделать Y, в противном случае сделать Z» или «выбрать все Y, затем сделать с ними X» не могут быть осуществлены в таком языке, как CSS. Проще говоря, это язык, созданный для оформления, язык для дизайнеров, а не девелоперов. Некоторые из опытных программистов, с которыми я работал, именно по этой причине тратили много усилий на то, чтобы освоить CSS.

Обучение CSS начинается с классов и ID, а также с использования . и # для непосредственного обозначения элементов. Этого достаточно чтобы построить полнофункциональный веб-сайт, но это не достаточно гибкое решение в случае полной смены дизайна. Давайте взглянем на альтернативный подход к управлению такими труднодоступными элементами.

Соседний родственный комбинатор

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

 h1 + p 

Это выделяет следующий p-элемент, расположенный сразу после h1-элемента в DOM. Типографическая теория предполагает, что мы должны использовать отступы в параграфах текста, но только если они следуют за другим параграфом. На практике это может быть использовано, чтобы сделать отступы во всех параграфах, кроме первого:
    p + p {
         text-indent: 1em;
    }

Это гораздо удобней, чем выделять первый параграф с помощью class=«first». Три строки, никаких классов и полная поддержка браузеров. Если вы располагаете тэги img, относящиеся к наполнению сайта, внутри тэгов p (как, собственно, и следует делать), можно просто отодвинуть их левые поля обратно с помощью негативного значения -1em:
  
    p + p img {
       margin-left: -1em;
    }

Довольно просто, правда? А что, если нам захочется выделить первую строку всех параграфов, которые стоят сразу после заголовков, не изменяя всех остальных параграфов? Опять-таки мы можем использовать класс представления. Простой селектор, сделанный из соседнего составного комбинатора, и псевдо-элемент справятся с задачей:
  
h1 + p::first-line {
    font-variant: small-caps;
    }

Примечание: псевдо-элемент :first-line принят в CSS 2.1, в CSS 3 используется запись ::, с целью установить различие между псевдо-классами и псевдо-элементами.

Наследственный комбинатор

Обычный протокол разметки – это помещение разделов в как-либо названном элементе в #page или #wrap:
  
<div id="page">
    <header></header>
    <article>
        <section id="main"></section>
        <aside></aside>
    </article>
    <footer></footer>
</div>

Вне зависимости от того, используете ли вы синтакис HTML 5 или XHTML 1.1, этот основной формат должен выглядеть для вас знакомо. Если ваш документ имеет фиксированную ширину в 960px, выровнен по центру и каждый элемент расположен горизонтально, ваш CSS скорее всего напоминает:
  
#page {
    width: 960px;
    margin: 0 auto;
    }
header,
article,
footer { width: 100%; }

Возможно, вы более точны в своей работе, и чтобы избежать нежелательных изменений в элементах верхнего уровня, используете:
  
#page header,
#page article,
#page footer { width: 100%; }

Есть способ получше. Мы все знакомы с универсальным селектором *. Если комбинировать его употребление с наследственным селектором, можно выбрать все элементы, являющиеся прямыми потомками #page, без какого-либо влияния на «внучатые» элементы:
  
#page > * { width: 100%; }

В будущем это поможет вам добавлять или удалять элементы верхнего уровня в документе. Возвращаясь к нашей начальной схеме разметки, это повлияет на элементы header, article и footer, но никак не коснется #main и всего остального внутри элемента article.

Селекторы атрибутов строки и подстроки

Селекторы атрибутов – одни из самых действенных. Они также существовали в CSS 2.1 и обычно использовались в форме input[type=«text»] или [href="#top"]. Но CSS3 предлагает более глубокий уровень контроля в форме строк и подстрок.

Примечание: до сих пор все, что мы обсуждали, относилось к стандарту CSS 2.1, но теперь мы вступаем на территорию CSS3.

Существует четыре основных селектора атрибутов строки, где ‘v’ = значение, ‘a’ = атрибут.
v – одно из списка значений, разделенных пробелом: element[a~=«v»]
a начинается с v: element[a^=«v»]
a заканчивается на v: element[a$=«v»]
a содержит значение: element[a*=«v»]

Потенциал селекторов атрибута строки фактически бесконечен, но отличным примером являются иконки. Скажем, у вас есть неупорядоченный список ссылок на профайлы в соцсетях:
  
<ul id="social">
    <li><a href="http://facebook.com/designfestival">Like on Facebook</a></li>
    <li><a href="http://twitter.com/designfestival">Follow on Twitter</a></li>
    <li><a href="http://feeds.feedburner.com/designfestival">RSS</a></li>
</ul>

Привести их в порядок так же просто, как сделать запрос через их href атрибут, чтобы найти ключевое слово. Мы можем расположить их таким образом:
  
#social li a::before {
    content: '';
    background: left 50% no-repeat;
    width: 16px;
    height: 16px;
    }
#social li a[href*="facebook"]::before {
    background-image: url(images/icon-facebook.png);
    }
#social li a[href*="twitter"]::before {
    background-image: url(images/icon-twitter.png);
    }
#social li a[href*="feedburner"]::before {
    background-image: url(images/icon-feedburner.png);
    }

Аналогичным образом, можно выбрать все ссылки в PDF документах селектором атрибута Suffix:
  
a[href$=".pdf"]::before {
    background-image: url(images/icon-pdf.png);
    }

Браузеры, не поддерживающие атрибуты подстрок в CSS3, не покажут эти иконки, но это не так важно – они всего лишь красивое дополнение, без особой функциональности.

Структурные псевдо-классы

Наконец, мне хочется обрисовать выгодность использования псевдо-классов (не путать с псевдо-элементами и псевдо-классами link и state). Мы можем использовать их для выделения элементов по их позиции в DOM. Хорошим примером использования структурного псевдо-класса является выделение первого (или последнего) элемента в дереве элементов, или выбор между четными и нечетными элементами.
  
<ul>
    <li>List Item 1</li>
    <li>List Item 2</li>
    <li>List Item 3</li>
    <li>List Item 4</li>
    <li>List Item 5</li>
    <li>List Item 6</li>
</ul>
ul li { border-top: 1px solid #DDD; }
ul li:last-child { border-bottom: 1px solid #DDD; }
ul li:nth-child(even) { background: #EEE; }

Примечание: единственный доступный в CSS 2.1 псевдо-элемент — :first-child. Все остальные псевдо-элементы, включая :last-child, относятся к стандарту CSS3.

Однако необходимо знать, когда НЕ надо использовать структурные псевдо-элементы. Они должны быть использованы исключительно для выделения элемента по его позиции, а не по содержанию. Если требуется провести какое-либо действие над элементом вне зависимости от его расположения в DOM, используйте более многозначные семантические селекторы, например, class, ID или string.

Возможно, вы уже используете некоторые из вышеуказанных комбинаторов и селекторов – может, правильно, а может и нет, — но лишнее напоминание, в каких случаях они удобнее, чем class или ID, не повредит. Даже лучшие из нас часто ошибаются в подобных вещах.
Tags:
Hubs:
+60
Comments 47
Comments Comments 47

Articles