Frontender Magazine

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

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

Если честно, я не знал что ответить. Как и Андрею, мне казалось что при отсутствии других элементов с заданным позиционированием, подменю должно было позиционироваться относительно элемента html и отображаться в верхнем левом углу браузера, но на практике все обстояло иначе.

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

Иллюстрация

Содержащие блоки задают контекст позиционирования

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

В своей публикации я описал как можно создать выпадающее меню с помощью шаблона Suckerfish. Я использую Suckerfish уже давно, так же как наверное и вы. По умолчанию подменю располагается далеко за пределами страницы, обычно на -999em слева от нее. При наведении курсора значение для left изменяется на auto и подменю появляется прямо под родительским пунктом меню.

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

По крайней мере, так думали я и Андрей. Наша ошибка состояла в понимании принципа работы значения auto.

Иллюстрация

Как браузеры интерпретируют значение auto?

Мы оба предполагали, что значение auto эквивалентно 0, тоесть запись left:auto равняется left:0. В некоторых случаях значение auto в СSS действительно равняется нулю, но не в этом.

Когда для элемента указано position: absolute, его расположение (и часто размер) регулируются свойствами top, right, bottom и left. Эти свойства указывают отклонение от исходного размещения элемента относительно содержащего блока. Для элементов, прописанных с помощью непарных тегов (<img>, <input>), результат при применении значения auto (для свойства top, right, bottom или left) зависит от того задано ли оно для остальных атрибутов позиционирования.

Последнее является ключевым моментом. По умолчанию auto не эквивалентно 0. Оно может быть равно 0, но это зависит от того какие значения заданы для остальных атрибутов позиционирования.

Выпадающее меню Suckerfish задает и впоследствии изменяет значение свойства left, остальные три свойства остаются auto. Когда мы изначально указываем left: -999em, в действие вступает свойство right и подменю располагается далеко за пределами страницы. При наведении курсора, когда значение свойства left меняется на auto, все четыре позиционирующие атрибуты получают значение auto.

Статья об элементах с абсолютным авто-позиционированием объясняет что именно происходит, так же как и эта статья с Dev.Opera об абсолютном и фиксированном позиционировании. Ниже приведена цитата из последней.

Значением по умолчанию для атрибутов top, right, bottom и left является auto, значит блок с абсолютным позиционированием расположен там где он был бы расположен без позиционирования. Однако, он удален из нормального потока и перекрывает любой элемент который следует за ним.

Вы можете увидеть примеры на изображениях ниже и выше в этой статье. На изображении выше меню появляется при наведении курсора на родительскую ссылку. В этом случае значением для свойства left (а также для top, right и bottom) является auto.

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

Если задуматься, все логично. Если бы по умолчанию auto было равно 0, элемент со значением 0 для всех сторон должен был бы растягиваться до границ своего контейнера. В некоторых случаях мы рассчитываем на то что он так и будет себя вести, например при вертикальном центрирование элементов.

В нашем случае происходит по другому. Когда для всех 4 сторон (или же для двух противоположных сторон) указано значение auto, расположение элемента зависит от интерпретации браузером.

Мы используем такое поведение для выравнивания центрального блока по горизонтали с помощью margin-left: auto и margin-right: auto. В этом случае margin делится на две равные части и элемент центрируется. В случае с выпадающими меню, дочерний список отображается там, где он бы находился без позиционирования.

Иллюстрация

Итог

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

Иногда мы используем те или иные технологии не пытаясь разобраться как они работают. Когда приходится объяснить принцип их работы, мы затрудняемся это сделать. Хотя иногда стоит разобраться в механизме работы используемых приемов.

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

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

Steven Bradley
Автор:
Steven Bradley
Twitter:
@vangogh
GitHub:
stevenbradley
Сaйт:
http://www.vanseodesign.com/
LinkedIn:
vangogh
Наталья Фадеева
Переводчик:
Наталья Фадеева
вКонтакте:
natatik_l
Twitter:
@very_busy_girl
GitHub:
NatalieF

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

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

Читая такие статьи, хочется взять книгу Eric Meyer's "Cascading style sheets the definitive guide" и бить ей по рукам всех, кто приходит во фронтенд не читая классических трудов и спек. Потом-же и вопросы почему clear: right не работает (не останавливает float: left), и потом у нас 23-летние сениоры, которые впервые слышат про контекст форматирования и контекст наложения (stacking context) на собеседовании.

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

Давай будем честными, никто не приходит во фронтенд ПОСЛЕ изучения спецификаций и прочтения учебников. Все происходит параллельно: набор практического опыта и приобретение теоретических знаний. Веб-разработка это не запуск космических кораблей и не строительство ядерных электростанций. Если новичок ошибется — никто не умрет. А к тем областям, где твои действия влияют на что то действительно важное ты будешь пробиваться годами. Я думаю что это хорошо, что люди начинают что то делать едва узнав как. Это позволяет поддерживать в них интерес, давая возможность видеть результаты каждого своего шага в процессе обучения. Со временем люди прочтут спеки и узнают и про то, как работает поток и про расчет веса селекторов, не стоит ругать их только за то, что они еще чего то не знают.

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

Можно прочитать все спеки и классические труды, но к тому времени современные знания и требования уже устареют. Согласен с предыдущим комментатором - изучать современный frontend приходится параллельно работе, хотя это не умаляет значение базовых знаний.

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

Такие статьи надо умещать в твит, максимум в два.

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

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