Frontender Magazine

После… или нет, перед… что-то я запутался

css

Когда старенький JS-разработчик копается в CSS чтобы написать статью вроде этой, это наверное выглядит странно? :)

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

Псевдо-CSS

Доводилось ли вам слышать о псевдоэлементах CSS ::before и ::after? Если вы по какой то причине еще не знакомы с ними, или вообще не слышали, тогда давайте рассмотрим их поближе.

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

С помощью ::after и ::before мы обращаемся к «последнему дочернему элементу» или «первому дочернему элементу» контейнера, для которого применено соответствующее стилевое правило. Но, не к настоящему первому или последнему дочернему элементу, а, скорее, к вымышленному, который существует только если вы прописываете для него стилевые свойства, в противном случае это всего лишь фантом.

Пример:

<style>
#container { background-color:yellow; }
#container::before { content:"!!!!! "; font-style:italic; }
#container::after { content:"!!!!!"; font-weight:bold; }
</style>

<div id="container">В этом контейнере есть не только этот текст</div>

В контейнере есть не только этот текст

Забавно, да?

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

Примечание: Вы можете встретить два вида записи: ::after или :after. С технической точки зрения, для псевдо-элементов и псевдо-селекторов вместо : нужно использовать ::. В большинстве браузеров оба варианта работают. Далее по тексту я буду использовать запись ::, ведь за ней будущее!

После чего «after» то?

Если приглядеться повнимательней, то вы заметите, что у ::before и ::after довольно странная семантика. Если вы следите за моими заметками по теме CSS, тогда, думаю, вы в курсе, что я довольно серьёзно воспринимаю вопрос семантики в CSS.

Видя слова «before» (перед) и «after» (после), большинство людей понимают их буквально, они ожидают, что добавленный контент будет отображаться чётко перед или после элемента с соответствующим стилевым правилом. То есть, стилизованные элементы должны быть на одном уровне с контейнером-родителем.

Однако, на самом деле мы прописываем стилевые правила для дочерних элементов контейнера, а если точнее, для элементов, которые «виртуально» расположены в нём в самом начале и в конце.

Вот именно, в этом случае «after» не значит «после родителя», это значит «в самом конце в родительском контейнере». Глубокий вздох. Еще одна семантическая ошибка. Они не такая уж большая редкость.

В мире JavaScript уже есть устоявшийся прецедент добавления дочерних элементов в начало элемента-контейнера, перед остальными дочерними элементами, или в конец, после всех остальных. И я говорю не только о фреймворках вроде jQuery и прочих, подобное есть даже во встроенном нативном DOM API. Мы вызываем нечто вроде parent.appendChild(child) для обновления списка дочерних элементов в родительском контейнере. Возможно, что с точки зрения семантики было бы лучше, если бы в CSS вместо ::after использовалось ::append или ::append-child.

А что насчёт ::before? Ну что ж, в самом DOM API нет такого метода, как prependChild(), так что нам придётся найти обходной путь, и использовать parent.insertBefore(child,parent.firstChild). Ух. К счастью, в различных JS фреймворках для таких случаев реализован метод prepend(). Я думаю, что и в CSS лучшим решением было бы использовать псевдо-элемент ::prepend или ::prepend-child.

::повторению-ошибок:нет

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

Тем не менее, я считаю, что если мы будем постоянно обращать внимание на подобные ошибки, может быть, со временем, нам удастся уменьшить их количество. Я очень надеюсь на это.

А что думаете об этом вы? Было ли правильным решение назвать этот псевдо-элемент ::after? Или, гораздо лучше было бы использовать более семантически верное обозначение ::append, и отнести раскритикованный вариант в список устаревших? Или же, мне просто стоит ::смириться-и-жить-дальше?

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

Kyle Simpson
Автор:
Kyle Simpson
GitHub:
getify
Twitter:
@getify
Сaйт:
http://getify.me/
Наталья Фадеева
Переводчик:
Наталья Фадеева
вКонтакте:
natatik_l
Twitter:
@very_busy_girl
GitHub:
NatalieF

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

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

Как по мне: до и после КОНТЕНТА. Хм.

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

Просто отключаешь CSS и радуешься, что и сделал автор на http://getify.me/ :)

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

Кстати, при первом нажатии на "участие в комментариях" очень не приятно кидает в верх странницы.

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

Спасибо, поправлю.

Автар пользователя
nin-jin

Что глаголы забыли в css? before - сокращение от before-contents Меня куда больше угнетает, что нельзя писать ::before::before

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

+1 nin-jin Если они реально будут перед и после элемента, то они станут бесполезными.

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

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

nin-jin про более, чем один ::before давно спорят и есть даже черновики спеки для этого. Но вообще это по сути уже почти shadowdom из css. Что кстати тоже было бы очень и очень круто. Но смешало бы представление и структуру, что не очень круто, в общем: вопросы-вопросы.

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

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

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

@SilentImp ну вот да, если позиционировать ::before и ::after абсолютно относительно relative-родителя, то совершенно безболезненно эмулировать нахождение их до и после. На самом деле это все вопрос привычки и я думаю большинство фронт-ендеров давно привыкли к такому поведению, поэтому что-то менять наверное уже поздно. Только если новые псевдо-элементы добавлять.

Спасибо за перевод!

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

Согласен с автором статьи, семантическая ошибка на лицо. Отчетливо помню - когда впервые наткнулся на :before и :after в чужом коде, очень долго не мог въехать, как они работают. Сейчас вот в chrome dev tools они обозначены в DOM и сразу видно, где находятся.

P.S. Текст в форме залезает под плашку "Сказать", поправьте.