Статьи

Особенности вложения CSS — когда CSS плохо себя ведет

Есть много ошибок CSS, о которых можно написать. Некоторых стоит отслеживать, потому что они раздражают и оказывают огромное влияние на нашу повседневную работу, другие интересны, потому что они еще не получили слишком много внимания. Но наиболее интересными являются те, которые позволяют нам затронуть более глубокие, лежащие в основе проблемы. Эта статья будет посвящена проблеме css, которая позволит нам сделать именно это.

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

Основной код

ul li {color:red;}  
ol li {color:blue;}

Вам не нужно много знаний о CSS, чтобы понять заявления выше. Первое правило стилизует неупорядоченный список и делает текст внутри тегов li синим. Второе правило стилизует упорядоченный список и делает текст внутри тегов li красным. Подобные правила довольно распространены, когда вам нужно предоставить стили по умолчанию, которые будут использоваться в областях бесплатного контента (обычно это происходит из cms, где контент вставляется через какой-то форматированный текстовый редактор).

Но если вы предполагаете, что эти два утверждения будут охватывать ваш стиль оформления списка, вас ждет удовольствие. Это не займет много времени, прежде чем редакторы контента начнут вкладывать списки, и часто упускается из виду пропущенная проблема. Когда неупорядоченный список вложен в упорядоченный список, элементы списка обоих списков станут голубыми. Не совсем то, что мы изначально ожидали.

Эта проблема

Так что случилось? Хорошо, когда неупорядоченный список был вложен в упорядоченный список, элемент списка неупорядоченного списка внезапно стал частью упорядоченного списка. Мы использовали комбинатор пробелов в нашем селекторе css для определения упорядоченного списка, что означает, что каждый вложенный элемент li внутри тега ol будет нацелен, независимо от глубины или включения других тегов. Поскольку специфика обоих правил одинакова, учитывается последнее объявление в строке, поэтому элементы списка неупорядоченного списка (которые теперь также являются частью упорядоченного списка) будут окрашены в синий цвет. Это раздражает, но совершенно нормально.

x z {...}  
y z {...}

Приведенный выше код CSS дает небольшую абстракцию проблемы. Эта проблема возникает при выполнении трех основных условий:

  1. оба компонента могут быть вложены друг в друга
  2. два селектора CSS заканчиваются одним и тем же элементом (z), но начинаются с разных элементов (x и y)
  3. оба правила CSS имеют одинаковую специфику

Любые решения?

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

/* solution 1 - nesting declarations */} 
ul li ol li {color:blue;}
ol li ul li {color:red;}

Первое решение — написать правила CSS для всех возможных вложений. Это самое безопасное решение, но оно не дает никакой гибкости. Всякий раз, когда владелец контента решает сделать третье вложение, вам снова не повезло. Чтобы увидеть популярный пример этой техники, вы можете проверить сетки Yahoo, которые имеют похожие проблемы с вложением. Если вы проверите их базовый CSS, вы заметите, что они поддерживают только два уровня вложенности. Кроме того, конечно, вы можете наложить теоретические ограничения на вложения, говоря владельцам контента, что они не могут иметь три вложения. При работе с крупными клиентами это не совсем то, что вы хотели бы сделать.

/* solution 2 - the '>' combinator */}  
ul>li {color:red;}
ol>li {color:blue;}

Есть второе решение, которое, я думаю, было бы предпочтительным решением для тех, кто задумал спецификации CSS. Чтобы предотвратить подобные проблемы, вы должны использовать дочерний комбинатор (‘>’). Используя это, вы нацеливаетесь только на теги li, которые вложены на один уровень ниже тега ul / ol. Это хорошее решение, хотя оно опять мешает гибкости CSS. Он работает нормально со списками, потому что единственный допустимый тег ниже тега ul / ol — это тег li. В более широком смысле, это может действительно испортить дизайн, когда вы вставляете простой div-обертку, что может сделать недействительной целую строку селекторов CSS.

Что еще хуже, IE6 не поддерживает дочерний комбинатор. Так что, пока мы принимаем во внимание IE6, наше второе решение имеет мало реальной ценности.

Так почему же мы так легко влюбились в эту проблему?

Учитывая все это, неудивительно, что мы так легко попадаемся в эту проблему, особенно когда начинаем с css. О комбинаторах часто забывают просто потому, что они практически не поддерживают их. Мы учим CSS на основе «что полезно, а что нет», мы игнорируем все остальное. Глядя на общий синтаксис селекторов CSS, мы видим, что единственный используемый комбинатор — это комбинатор пространства. Итак, сначала мы даже не осознаем, что пространство на самом деле является значимой частью селектора css, который может быть заменен списком альтернатив.

Только когда мы начнем копать глубже и глубже в синтаксис и возможности css, мы узнаем о диапазоне комбинаторов, которые мы имеем в нашем распоряжении. К тому времени идея о том, что селекторы CSS основаны на единственном комбинаторе, настолько укоренилась, что трудно постоянно помнить о побочных эффектах космического комбинатора. Так что время от времени нас одурачивает такая простая проблема, как эта.

Конечно, мы должны поблагодарить за это потрепанную поддержку браузера, поскольку даже сейчас мы не можем безопасно использовать любой другой комбинатор, кроме места. IE6 не поддерживает ни одного интересного, и пока старый монстр находится рядом, у нас останутся такие проблемы, как эти. Когда вы начинаете с css, вы начинаете с доступных инструментов, и, к сожалению, набор комбинаторов не входит в число этих инструментов. Итак, еще раз, это «вина IE6» ситуации.

Смотря дальше

Тем не менее, поддержка браузера не является основной проблемой. Как я уже объяснил со вторым решением, дочерний комбинатор мог бы решить нашу проблему, но это серьезно подорвало бы гибкость нашей CSS. Так о чем же тогда?

То, что на самом деле здесь происходит, это отличие интерпретации от человеческого разума и способа применения css. Это на самом деле следующее маленькое правило, которое запутывает вещи: если два селектора CSS имеют одинаковую специфичность, будет применен последний . Это имеет смысл с логической точки зрения (т.е. самый простой и безопасный способ реализации), но не с человеческой точки зрения.

Оглядываясь назад на наше первое решение, мы разработали упорядоченный список и неупорядоченный список. По крайней мере, мы думаем, что сделали, именно так мы воспринимаем селекторы. На самом деле наш CSS ничего об этом не знает, он только сопоставляет селекторы с HTML. Мы молчаливо предполагаем, что css достаточно умен, чтобы распознавать упорядоченный список внутри неупорядоченного списка, но в этом мы ошибаемся. Человеческий разум мыслит в компонентах, в то время как css просто подбирает селекторы в соответствии с фиксированным набором правил.

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

Вывод

Независимо от того, какое решение вы придумали, если вы действительно хотите заняться этой проблемой, вам нужно либо полностью изменить свое мышление, чтобы приспособиться к тому, как применяется CSS, либо вам необходимо переработать правила CSS, чтобы они соответствовали человеческому уму. Боюсь, что ни одно из этих решений невозможно, поэтому лучше всего быть начеку и избегать подобных проблем, пытаясь вспомнить, что они могут произойти. Это немного, но вы привыкнете к этому через некоторое время.