Frontender Magazine

Когда ID становятся болью в…

С тех пор, как была написана эта статья, я понял, что полный запрет на использование ID имеет смысл. Вы сможете избежать множества потенциальных проблем, отказавшись от использования ID в CSS.

В последнее время я часто встречал публикации, которые пропагандировали отказаться от ID в CSS селекторах. Меня всегда раздражало, когда в статьях скорее приказывают, нежели объясняют суть. В подобных материалах авторы часто забывают углубиться в контекст и необходимость использования ID. Я же хочу рассказать вам, почему не стоит использовать ID и как избежать большинства ошибок.


Cелекторы по ID - прекрасны, валидны и идеальны для стилизации отдельных частей страниц. Это основы HTML/CSS. ID уникален в рамках страницы и позволяет стилизовать элемент по селектору #element.

Кроме того, что ID уникальны, селектор по ID обладает большим весом, чем селектор по классу. Следовательно, даже если мы определим #info первым, он может быть перезаписан следующими за ним селекторами, соответствующими элементу, но этого не произойдет из-за бо́льшего веса, что может сбивать с толку.

Уникальность ID

Уникальность ID на странице — один из основных аргументов против их использования. Довольно неубедительный, на мой взгляд. Смысл ID именно в том, что они уникальны. Хочешь выбрать селектором несколько элементов — используй классы. Разработчики не дураки и прекрасно это понимают, так что запрещать использование ID из-за того, что они обладают уникальным значением, мягко говоря, странно.

Кроме того, не всё на странице может дублироваться. Например, совершенно невозможно дублирование области с контентом.

Непредсказуемость

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

Если вы используете ID в селекторах, то их вес сложно оценить, и зачастую результат будет неожиданным. Это примерно также, как если бы вы добавили !important. Селектор с большим весом перезаписывает селекторы с меньшим. Возможно, вы не всегда этого хотите.

Лучше всего объяснить на конкретном примере.

Например, виджет должен быть в шапке сайта и сразу после статьи. Представление виджета при этом должно оставаться неизменным.

Давайте, посмотрим на этот пример.

Ну, вот, у нас появилась проблема! Селектор #header a обладает большим весом, чем .tweet a, поэтому ссылка внутри виджета наследует цвет ссылок из шапки и становится нечитабельной. Напомню, что представление виджета должно быть неизменным, поэтому это плохое решение.

Мы можем решить проблему добавив #header в селектор .tweet a или, хуже того, использовать !important. Бррр…

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

Лучше добавить вместо (или вместе с) ID класс: http://jsfiddle.net/csswizardry/gTZGq/3/ Использование класса в селекторе понизит его вес и решит проблему. Отказ от использования ID означает, что вам больше не придётся бороться с вами же созданной проблемой определения веса селекторов.

Я уже упоминал, что мы можем добавить класс дополнительно к ID или вместо него. Что именно предпочтительно зависит от контекста…

Удалять ли ID полностью?

Мы рассмотрели пример, когда использование ID может вызвать проблему, но они могут быть использованы не только в селекторах, но и в качестве якоря.

Думаю, вы замечали ссылки «Перейти к меню» или «Перейти к контенту». Они не будут работать без ID, так что его нужно оставить, дополнительно добавив классы, необходимые для CSS-селекторов.

Так что, как и с большинством проблем разработки, решение зависит от контектста. Вы знаете контекст своих задач лучше, чем кто либо еще, так что не стоит позволять кому-то говорить вам что делать. Если вы хотите оставить ID для поддержания целостности или создания навигации внутри страницы — это ваше дело.

Подведём итоги

Да, ID - вовсе не абсолютное зло: они валидны, и безоговорочный запрет на их использование, по меньшей мере - глупость. Чтобы получить наиболее подходящее решение проблемы, принимайте его исходя из контекста задач и вашего опыта.

Используйте ID, но помните, порой это может повлечь за собой множество проблем. Все, кто говорили вам не делать этого, не то чтобы неправы, но определённо ошибаются…

Прим. переводчика: Гэрри Робертс, впоследствии добавил замечание в самом начале этой статьи о том, что не взирая на заключение статьи, спустя какое-то время, он пришёл к выводу, что всё-таки использование ID для стилизации страниц можно исключить полностью.

Если вы заметили ошибку, вы всегда можете отредактировать статью, создать issue или просто написать об этом Антону Немцеву в skype ravencry.

Harry Roberts
Автор:
Harry Roberts
Сaйт:
http://csswizardry.com/
Twitter:
@csswizardry
GitHub:
csswizardry
Владимир Старков
Переводчик:
Владимир Старков
Сaйт:
http://iamstarkov.com
Twitter:
@iamstarkov
GitHub:
iamstarkov

Комментарии (40 комментариев, если быть точным)

Автар пользователя
gladkih

Всё можно свести к следующему: используйте ID с умом, но только если они действительно необходимы.

Автар пользователя
SilentImp

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

Вот вам в догонку еще про вес селекторов: 1. CSS Specificity: Things You Should Know 2. Specifics on CSS Specificity 3. What happens when conflicts occur?

Автар пользователя
gladkih

Про вес селекторов я в курсе. Сам id не использую очень давно. Работать с шаблонизаторами и препроцессорами без id намного легче. Неоднократно видел советы по использованию id для javascript, но для меня это так же не удобно, т.к. не решает проблему дублирования элемента.

Автар пользователя
SilentImp

В JavaScript вообще кошерно атрибуты вроде data-user-id использовать.

Автар пользователя
gladkih

Ими и пользуюсь )

Автар пользователя
pukhalski

@SilentImp, Антон, использовать data-аттрибуты нужно только для хранения дополнительных данных, связанных с DO. Использовать их как идентификатор не стоит. Или я что-то не так понял?

Автар пользователя
SilentImp

Я думаю, что их можно использовать и для хранения данных и для идентификации. Почему нет? Селекторы вида [data-user-id] отлично работают. IE старше 7. Но даже в 7 были адекватные хаки и частичная поддержка через проприетарный вид этого селектора.

Автар пользователя
pukhalski

То есть, избегать самого быстрого, чистого и простого подхода только потому, что когда-нибудь кто-то захочет еще одну такую кнопку на странице?)

Btw http://calendar.perfplanet.com/2012/efficient-html5-data-attributes/

Автар пользователя
SilentImp

Нет конечно, и все зависит от контекста. Часто id — именно то, что нужно для выборки. Но в общем случае имеет смысл разделить то, что используется для формирования представления (класс и id) и то, что используется для прикрепления логики. В противном случае можно случайно изменив допустим id или класс поломать работу скриптов. Может такое быть, если над проектом работает больше одного человека?

Автар пользователя
pukhalski

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

The id attribute has several roles in HTML: - As a style sheet selector. - As a target anchor for hypertext links. - As a means to reference a particular element from a script. - As the name of a declared OBJECT element. - For general purpose processing by user agents (e.g. for identifying fields when extracting data from HTML pages into a database, translating HTML documents into other formats, etc.).

Source: http://www.w3.org/TR/1999/REC-html401-19991224/struct/global.html#adef-id

Прошу обратить внимание на пункты 3 и 4.

Для class ситуация другая:

The class attribute has several roles in HTML: - As a style sheet selector (when an author wishes to assign style information to a set of elements). - For general purpose processing by user agents.

Автар пользователя
pukhalski

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

Автар пользователя
SilentImp

Я и не отрицаю пункты 3 и 4. Но если id исполняет все возможные роли одновременно, то могут быть проблемы. Можно принять решение и использовать id только в JavaScript и для обеспечения работы форм и меню. Или только для стилизации, а JavaScript навешивать на пользовательские атрибуты. Как вы в комманде договоритесь. Главное что бы не было путаницы.

Но если ты скажем пишешь фреймворк, который будут использовать непонятно кто и непонятно как, то мне кажется атрибут — хорошее решение. Хотя можно опять же предоставить выбор конечному пользователю и позволить вводить во фреймворк селектор.

Автар пользователя
plisovyi

Хочу высказать мысль, о которой, мне кажется, не стоит забывать. Селекторы в javascript обладают разной скоростью работы. И иногда, быстрее расставить при генерации DOM'а пронумерованные id, чем выбирать элементы по data-аттрибутам.

Автар пользователя
SilentImp

Вполне верно. Но еще надо понимать когда оптимизация такого уровня нужна, а когда она преждевременна. Я помню очень не много случаев когда действительно тормозили выборки из DOM.

Автар пользователя
rohozhnikoff

Безусловно, повышать селектор в определенных случаях очень удобно. И я бы сказал, что это повышает читабельность кода - ты сразу видишь особый приоритет, эту исключительность, которая дана крупному блоку. Но в разработке, особенно если это крупный проект с множеством модификаций и похожести блоков, мы сталкиваемся с тем, что гораздо легче держать все элементы на одном уровне и использовать одни и те же селекторы, правила именования, аббревиатуры и пр. Недавно, например, я поубирал все section, aside, nav и прочий html5 сахарок из своих текущих проектов. Потратил по 20 минут на каждый, зато значительно упростил для себя код. В такие моменты упрощения, всегда вспоминается Оккам с его бритвой.

И иногда, быстрее расставить при генерации DOM'а пронумерованные id, чем выбирать элементы по data-аттрибутам. Паша, а ты не сталкивался, случайно, с проблемами на своих проектах, когда 100500 различных способов разрешения схожих задач в итоге приводят к усложнению поддержки? Может лучше прийти к универсальному методу, и прибегать к подобным хакам только при условии реальной потребности в оптимизации конкретного места? Ведь скорость разрешения задач твоими сотрудниками, гораздо более актуальный KPI, чем скорость селекторов.

Автар пользователя
plisovyi

потребности в оптимизации конкретного места?

Именно.

Автар пользователя
SilentImp

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

Приведение элементов к одному уровню возможно только если у нас огромные классы, которые описывают что это такое за элемент. В противном случае будет практически невозможно понять что это. Или ты вообще имеешь в виду представительские классы? Вроде .red-background? Ты не мог бы привести пример?

Недавно, например, я поубирал все section, aside, nav и прочий html5 сахарок из своих текущих проектов.

И зачем? Кроме того у тебя же sass, compass в проектах. Там вообще сплошной сахар везде. Ты от них отказался?

Расскажи подробнее о своем подходе и его достоинствах, если не затруднит. Из поста сложно представить картину в целом.

правила именования

Без соглашений по именованию будет бардак, но ты как то не к месту их упомянул…

Автар пользователя
rohozhnikoff

Во первых ты говоришь про айдишники, как селекторы к js. А в статье, вроде как, говорится о css-селекторах.

Во-вторых давай разберемся. Зачем нумеруют айди? Чтобы быстро связать определенное действие с конкретным элементом (а не селектором). Зачем тогда вообще вязать это с селектором, особенно если ты генеришь эти элементы на клиенте. Простой find по массиву будет в разы быстрее + ты можешь заполнить его любой другой информацией, не генерить при вставке в DOM, аттрибут id и вообще - не усложнять. С другой стороны, я не понимаю, когда у тебя будет тормозить выборка одного элемента (коим является пронумерованный айдишник).

Автар пользователя
rohozhnikoff

Приводить элементы к одному уровню можно, если стараться избегать повышения селектора. И неважно названия, можно даже двусимвольными классами обходится, лишь бы в селекторах были преимущественно выборки по одному классу.

Автар пользователя
plisovyi

Мурад, я упомянул скорость селекторов в js к комментарию:

В JavaScript вообще кошерно атрибуты вроде data-user-id использовать.

Так что, будем считать оффтопик.

Автар пользователя
rohozhnikoff

В том то и дело. В препроцессорах (я уже на stylus перешел) я не использую выборки по тегам, соответственно эту "псевдосемантику" я вижу только в хтмл. Это псевдоудобство - с одной стороны прикольно, ведь оно придает особый вес в твоих глазах. С другой стороны, только отвлекает. Одно дело, если бы этот сахарок что-то сходу давал, например как в xml, а другое дело когда ты все равно работаешь с классами и тег тебе вообще по барабану.

Автар пользователя
SilentImp

Это актуально для огромного, очень сложного DOMа, когда например у нас есть масса элементов с id уникальными и тебе аяком допустим прислали id элемента с изменениями. Выбрать его по id будет быстрее чем по атрибуту или классу. Но ситуация, когда такой выиграш действительно будет иметь вес — исключительная.

Автар пользователя
SilentImp

Мурад, так никто и не заставляет использовать их в css селекторах. Хочешь — используй только стили. Но когда открываешь простыню с html/haml эти теги немного упрощают понимание кода. К ЧСВ это не имеет никакого отношения и по-моему это дает свой «+» при прочих равных, при том, что ничего не стоит. Ну и еще в теории использование семантически верных тегов упрощает машинную обработку. Но это скорее ты как бывший СЕО'шник мне можешь рассказать.

Автар пользователя
rohozhnikoff

Селекторы по data-аттрибутам вполне оправданы, т.к. они превращают элементы в подобие базы данных с несколькими различными полями для выборки. Врядли подобные выборки имеют перед собой цель "скорость", т.к. кешируются на раз-два.

Автар пользователя
SilentImp

И расскажи поподробнее про использование одного уровня селекторов в своих проектах. Я заинтригован.

Автар пользователя
rohozhnikoff

Метаформаты упрощают. А по поводу упрощения машинной обработки при использовании HTML5 тегов — не доказано, что это может повлиять на повышения релевантности/объема лота краулера или чего-то, что имеет важность для SEO.

Упрощает код безусловно, но при отсутствии любого другого ООП и прочей модульности. Когда вопрос идет глубже, и использование БЭМа и оных, становится даже не то что оправданным, а жизненно необходимым - весь этот сахар превращается лишь в ностальгирующее самобичевание во имя HTML. Давно пора уже отойти от написания html как такового - его роль лишь создать необходимые объекты-обертки и назвать их определенным образом. Посмотрите на разработчиков других интерфейсов (например мобильных) - они генерят слегка избыточный xml без стеснения и стараются не лезть туда без особых причин, строя логику совсем на других уровнях.

Автар пользователя
rohozhnikoff

Че рассказывать то. Я практически не использую контекстных селекторов (только в часто используемых, всегда одинаковых частях и определенных случаях с контентными блоками, наполняемыми через визуальный редактор) и знаю множество способов легко рефакториться (IDE в помощь). Также по определенному алгоритму расставляю файлы блоков в файле-сборщике, поэтому без стеснения и неудобств фигачу везде классы, будучи уверенным что мне не прийдется использовать неоправданное повышение селектора или импортант.

Автар пользователя
pukhalski

Считаю неуместным сравнение выше. Нельзя сравнивать веб и "другие интерфейсы" ни в коем случае. Так как веб строится на совершенно других принципах и является платформой.

Автар пользователя
rohozhnikoff

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

Автар пользователя
SilentImp

Мурад, правильно. Рассказывать нечего. Примеры! Примеры! Впрочем, ладно. Надо будет тебя подробно расспросить при случай про весь цикл разработки, который ты используешь.

Автар пользователя
pukhalski

Смотреть, конечно, я не смогу запретить. Пока что)

Примером, близким к "избыточному xml", может быть подход Sencha Touch, к слову. И я очень сомневаюсь, насколько он способен к выживанию в текущей ситуации в Вебе.

Автар пользователя
pukhalski

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

Автар пользователя
rohozhnikoff

Какие примеры, Антон, все под NDA =) Но ты приезжай обратно, я тебе покажу свои "простынки" кода и то, как их приходится упрощать, чтобы легче ориентироваться.

Автар пользователя
jmas

Я использую ID для финальной расстановки элементов. Например, у меня имеется список ".people-list", который на главной странице имеет ID #mainPagePeoples. Так же у меня есть страница c общим перечнем людей, там у ID меняется на "#roomPagePeoples".

Второй список от главной отличается только позиционными св-вами, что я и делаю, используя селектор по ID.

Какие либо существенные различия: цвет, текст и другие особенности - тогда лучше применить дополнительный класс-модификатор, например ".people-list.big".

Но да, потом я отказался и от такого применения ID, хотя продолжил применять для статических элементов страницы, таких как Шапка, Футер.

Автар пользователя
anmiles

Используя тот же Less, можно сделать так: .widget { color: red; font-size: 12px; }

header

{ .widget; }

И не переживать, что какие-то стили не применятся. Они просто вложатся.

Автар пользователя
anmiles

Парсер - лох ©

.widget { color: red; font-size: 12px; } # header { .widget; }

Автар пользователя
some-thing

Этот пример "решается" путем нажатия ВСЕГО ОДНОЙ КЛАВИШИ - [P] :

header p a {

color:#f90;

}

Автор статьи походу совсем слаб в CSS.

Автар пользователя
some-thing

Там чет про парсер было выше.

7:# header p a{ color:#f90; }

Автар пользователя
some-thing

Плюс к тому же он . По колхозному решает проблему ВТОРЫМ примером , повторяя один и тот же селектор через запятую : 14: . tweet a, # header . tweet a{ color:#000; }

Когда должно быть просто :

14: # header . tweet a{ color:#000; }

Автар пользователя
some-thing

Еще называет лучшим фиксом то , что он напечатал 7 символов чтобы переписать HTML и CSS , вместо печати 1 символа [p] в CSS . Мега фикс .