- HTML, CSS: важен ли порядок названий классов CSS в атрибуте «class» HTML-элементов
- С точки зрения стандарта языка HTML
- Влияние правил языка CSS
- Заключение
- CSS GuideLines, часть 3. Именование классов
- Разделение дефисом
- БЭМ-подобное именование
- Именование в разметке
- JavaScript-хуки
- В продолжение темы.
- Материалы для дополнительного изучения
HTML, CSS: важен ли порядок названий классов CSS в атрибуте «class» HTML-элементов
У HTML-элементов на HTML-странице может быть необязательный глобальный атрибут class . Например:
Здесь у HTML-элемента div есть интересующий нас атрибут class .
С точки зрения стандарта языка HTML
По действующему стандарту языка HTML атрибут class HTML-элементов должен содержать значение, состоящее из набора (множества) названий (токенов) классов CSS, разделенных пробельными символами (символ пробела, символ горизонтальной табуляции, символы новой строки). В примере выше атрибут class содержит значение-строку имя-класса1 имя-класса2 имя-класса3 , представляющее собой набор из трех названий классов CSS, разделенных символом пробела.
Понятие, о котором тут идет речь — набор из слов (токенов), разделенных пробельными символами (по-английски «set of space-separated tokens»). Само это понятие определено в стандарте языка HTML отдельно от определения атрибута class .
В определении понятия «set of space-separated tokens» сказано, что это строка, содержащая ноль или более слов (токенов), разделенных пробельными символами. Из этого я сделал вывод, что атрибут class по действующему стандарту HTML может не содержать ничего (содержать пустую строку). Также не будет нарушением действующего стандарта HTML, если этот атрибут будет содержать только пробельные символы (символ пробела, символ горизонтальной табуляции, символы новой строки).
Действующий стандарт HTML вообще практически не накладывает на набор названий классов CSS в атрибуте class HTML-элементов каких-либо ограничений. Не накладывается ограничение на дублирование названий классов CSS внутри атрибута class . Не накладывается каких-либо ограничений, касающихся порядка перечисления названий классов CSS внутри атрибута class .
То есть с точки зрения действующего стандарта HTML (в отличие от стандарта CSS, о чем речь пойдет далее) название класса, перечисленное в наборе названий классов внутри атрибута class HTML-элемента раньше (левее, выше), ничем не отличается от названия класса, перечисленного в том же наборе названий классов внутри атрибута class HTML-элемента позже (правее, ниже).
В примере выше с точки зрения действующего стандарта HTML названия классов имя-класса1 , имя-класса2 и имя-класса3 в наборе названий имя-класса1 имя-класса2 имя-класса3 полностью равноправны, несмотря на то, что, к примеру, название имя-класса1 перечислено раньше (левее), чем имя-класса2 или имя-класса3 . То же самое касается названия имя-класса2 по сравнению с названием имя-класса3 .
Во что это выливается на практике?
Рассмотрим такой код на языке HTML (многоточием заменен неинтересный для предмета этой статьи код):
. . .имя-класса1 < color: green; >.имя-класса2 < color: red; >.имя-класса3 Какой-то текст1.
Какой-то текст2.
.
В теле этой HTML-страницы есть два параграфа (HTML-элемент p ). Каждый из этих параграфов содержит атрибут class с набором из одних и тех же трех названий классов CSS, только перечисленных в разном порядке.
Описание стилей в классе имя-класса3 не конфликтует с другими классами. А вот описания стилей в классах имя-класса1 и имя-класса2 конфликтуют друг с другом (приписывают разные значения одному и тому же свойству HTML-элемента).
В итоге при любой перестановке названий классов CSS в атрибуте class HTML-элементов p браузер отобразит параграфы одинаково, в данном случае — с синей границей и текстом красного цвета. Это подтверждает наши выводы, сделанные выше из определений действующего стандарта HTML.
Почему же в данном случае в конфликте описаний стилей в классах имя-класса1 и имя-класса2 «победил» класс имя-класса2 (текст в параграфах отразился красным цветом)? Потому что при определении классов CSS внутри HTML-элемента style класс имя-класса2 описан позже (ниже), чем класс имя-класса1 . Внутри HTML-элемента style действуют правила (стандарт) языка CSS. Это действие того самого «каскада», давшего первую букву аббревиатуре CSS (Cascading Style Sheets). Если менять местами определения классов CSS внутри HTML-элемента style (или внутри файла с кодом на языке CSS, если определения стилей вынесены в отдельный файл или файлы), то отображение HTML-страницы в браузере будет изменяться.
Влияние правил языка CSS
Всегда ли порядок названий классов в атрибуте «class» HTML-элемента не влияет на отображение HTML-страницы?
С учетом вышеизложенного хочется ответить на этот вопрос утвердительно, но утвердительный ответ тут будет неправильным.
Дело в том, что в языке CSS можно определить селектор (отборщик) HTML-элементов так, что отбор HTML-элементов селектором будет зависеть от порядка перечисления названий классов CSS в атрибуте class HTML-элементов. Это сделает порядок перечисления названий классов в атрибуте class HTML-элементов значимым для отображения HTML-страницы в браузере.
Это не будет противоречить вышеописанным выводам, сделанным из определений в стандарте языка HTML, потому что в данном случае это будет действие стандарта CSS.
В качестве примера рассмотрим такой код на языке HTML:
. . [class^="имя-класса1 имя-класса2"] < color: green; >[class^="имя-класса2 имя-класса1"] < color: red; >.имя-класса3 Какой-то текст1.
Какой-то текст2.
.
Это тот же код HTML, что и в первом примере, только здесь изменены два первых селектора внутри HTML-элемента style . Даже описания стилей остались нетронутыми. Однако, теперь браузер отображает в первом параграфе текст зеленым цветом, а во втором параграфе — красным цветом.
В этом примере порядок названий классов внутри атрибута class HTML-элементов стал значимым для отображения HTML-страницы в браузере, но это действие не правил языка HTML, а правил языка CSS.
Заключение
Итак, окончательный ответ на вопрос, заданный в заголовке этой статьи, должен звучать следующим образом.
Порядок названий классов CSS в атрибуте class HTML-элементов на HTML-странице может влиять (может быть важным) на отображение этой HTML-страницы в браузере. Но следует понимать, что это влияние определяется не правилами (стандартом) языка HTML, а тем, как описаны стили для данной HTML-страницы с помощью языка CSS (они могут быть описаны в разных местах — внутри HTML-элемента style , внутри внешнего файла (нескольких файлов) и так далее).
CSS GuideLines, часть 3. Именование классов
Соглашения по именованию CSS позволяют писать строгий, чистый и красивый код. При соблюдении правил именования вы всегда будете знать:
- Для чего используется класс;
- Где класс может быть использован;
- С какими другими классами связан этот класс.
Следует отметить, что сами по себе правила именования не дадут особой выгоды при написании CSS; но зато они весьма полезны при просмотре разметки.
Разделение дефисом
Все слова в названиях классов должны быть разделены дефисом:
CamelCase и знак подчеркивания не используются для классов, следующий пример неправилен:
БЭМ-подобное именование
Для более крупных взаимосвязанных частей интерфейса я использую БЭМ-подобное именование классов.
БЭМ, то есть Блок, Элемент, Модификатор, это методология, созданная разработчиками Яндекса. Несмотря на то, что БЭМ — это довольно крупная методология, в данный момент мы заинтересованы только в ее способе именования элементов. Причем, мое соглашение по именованию немного отличается от оригинального БЭМ’a: принципы одни и те же, но синтаксис разный.
- Блок: главный корневой элемент.
- Элемент: часть блока.
- Модификатор: вариант или модификация блока.
.person <> .person__head <> .person--tall <>
В начале класса всегда ставится название блока, для обозначения элемента мы отделяем название блока от названия элемента двумя подчеркиваниями (__), а для обозначения модификатора используем два дефиса (—).
В примере выше мы можем видеть, что .person <> — это блок; у него нет предков. .person__head <> — это элемент, часть блока; наконец, .person—tall — это модификатор, разновидность блока .person <> .
Использование блоков
Блок должен быть логической, самостоятельной единицей. Продолжая наш пример с классом .person <> : мы не можем создать класс .room__person , потому что .room <> — это самостоятельная единица. В таком случае стоит разделять блоки:
.room <> .room__door <> .room--kitchen <> .person <> .person__head <>
Если нам потребуется обозначить человека внутри комнаты, было бы правильнее использовать такой селектор — .room .person , который позволяет не городить кашу из кучи разных непонятных элементов и блоков.
Более реалистичный пример правильного использования блоков может выглядеть следующим образом:
.page <> .content <> .sub-content <> .footer <> .footer__copyright <>
Каждая часть кода представляет свой собственный блок. Неправильный пример использования:
.page <> .page__content <> .page__sub-content <> .page__footer <> .page__copyright <>
Важно уметь различать, где стоит применять БЭМ, а где нет. Как правило, я использую блоки для описания автономных частей пользовательского интерфейса.
Множество слоев
Если бы мы добавили к нашему блоку .person <> еще один элемент, скажем, .person__eye , то нам не нужно было бы при именовании элемента делать шаг назад, добавляя названия предыдущих элементов, вплоть до корневого элемента. То есть, правильно будет писать .person__eye , а не .person__head__eye .
Добавляем модификации элементов
Вам может потребоваться добавлять вариации элементов, это может быть сделано несколькими способами, в зависимости от того, как и почему эти элементы должны быть изменены. Опять же, если человек имеет голубые глаза, то в CSS это может быть описано так:
Однако, в реальных проектах все бывает несколько сложнее. Прощу прощения за такую аналогию, но давайте себе представим человека с красивым лицом. Сам по себе он не особо красив, поэтому лучшим решением будет добавить модификатор к элементу .person__face <> :
Но что делать, если мы хотим описать лицо красивого человека? То есть человек красив сам по себе, в отличие от предыдущего примера, и нам нужно описать его лицо? Это делается следующим образом:
.person--handsome .person__face <>
Это один из немногих случаев, когда мы можем менять элемент в зависимости от модификации блока. При использовании Sass получился бы такой код:
.person <> .person__face < .person--handsome & <>> .person--handsome <>
Заметьте, что мы не добавляем новый элемент .person__face внутрь элемента .person—handsome ; вместо этого мы используем родительский селектор Sass внутри уже существующего селектора .person__face . Это значит, что все правила, связанные с .person__face будут находиться в одном месте, и нам не придется разбрасывать их по всему файлу. Это хорошая практика при работе с вложенным кодом: храните все нужные стили внутри одного контекста (в нашем случае, внутри .person__face ).
Именование в разметке
Как ранее было замечено, соглашение по именованию классов наиболее полезно при работе с разметкой. Взгляните на следующий кусок разметки, не следующий нашему соглашению:
Как классы .box и .profile связаны друг с другом? Как классы .profile и .avatar связаны друг с другом? Связаны ли они вообще? Зависит ли класс .bio от класса .pro-user ? Можно ли использовать класс .avatar вне этой разметки?
При просмотре такой разметки очень сложно ответить на все эти вопросы. Использование соглашения об именовании меняет дело:
Теперь нам сразу видно, какие классы связаны друг с другом и как, а какие нет; мы знаем, какие классы мы не можем использовать вне этой разметки; наконец, мы знаем, какие классы могут быть использованы в любом другом месте.
JavaScript-хуки
Как правило, неразумно привязывать JS- и CSS-код к одному и тому же классу в разметке, потому что удалив или изменив один класс с целью, например, изменения поведения скрипта, вы непременно затронете CSS, и наоборот. Намного чище, прозрачнее и в целом лучше привязывать JS к отдельным классам.
Я сталкивался со случаями, когда удаление каких-то классов с целью переработки стилей, ломало работу всех скриптов на странице, а все потому что разработчик не подумал и привязал стили со скриптами к одному и тому же классу.
Как правило, разработчики используют отдельный класс для js, начинающийся с префикса «js-», например:
Такая разметка позволяет использовать стили .btn в любом другом месте, при этом не затрагивая поведения .js-btn .
data-* атрибуты
Также довольно часто разработчиками используются data-* атрибуты в качестве js-хуков, но это неправильно. data-* атрибуты, согласно спецификации, предназначены для хранения данных, недоступных на странице. data-* атрибуты созданы для хранения данных, а не для для привязки к js.
В продолжение темы.
Как уже было сказано, все правила, представленные выше весьма просты. Я призываю вас не останавливаться на изученном и читать другие материалы по этой теме — это позволит вам получить больше возможностей по именованию классов.