Введение в инлайновый контекст форматирования (ИКФ): основные понятия (2-я публикация цикла «Тайны CSS2.1»)
Поля, отступы и границы в инлайновом форматировании
В блочном контексте форматирования поля (margin), отступы (padding) и границы (border) могут оказывать влияние на любые координаты и расположение элемента, и так же могут влиять на окружающий контекст. Но что происходит в этом плане в инлайновом контексте? Давайте разберёмся.
Спецификация сообщает нам, что в инлайн-форматировании горизонтальные margin, border и padding между инлайн-боксами учитываются, а вот вертикальные не дают никакого эффекта и не могут влиять на окружающий контекст. Да, сразу стоит уточнить, что речь идет о инлайн-боксах, создаваемых обычными элементами с display:inline (читайте первую часть цикла), а не об инлайн-блоках или замещаемых элементах вроде картинок.
Некоторым может показаться, что здесь всё прозрачно, но на самом деле это не так. Поэтому я решил, что нам следует подробнее разобрать эти моменты, и начнём мы, пожалуй, с margin.
Горизонтальные поля
При назначении обычным инлайнам свойства margin, последний будет представлять из себя сдвиг от левого края, либо другого инлайн-бокса, если это margin-left, и отодвигание правого инлайн-бокса (если таковой имеется) при margin-right. Плюс правый инлайн-бокс (опять же, если таковой имеется) может перенестись на следующую строку, в случае нехватки места в текущей.
В общем, начнём с простого примера:
<p>Lorem Ipsum is simply <span>dummy text printing </span> and type setting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s PageMaker including versions of Lorem Ipsum.</p>
body {font-size: 16px;} span { background: #FC3; margin: 500px 180px 500px 40px; border-radius: 5px; }
Итак, я выставил инлайн-элементу <span> левое поле со значением в 40px, а правое в 180px. Что произошло? А произошло следующее. Наш элемент сместился вправо от левого инлайн-бокса (текста) ровно на 40px, а своим правым полем отодвинул следующий за ним инлайн-бокс (текст) на 180px, но так как последний попросту не поместился на эту же строку, он перескочил на новую, а на предыдущей (т.е. на той где наш элемент), в правой части теперь балом правит правый margin <span>-а.
Да, и как вы могли заметить, я специально назначил верхние и нижние поля нашему элементу в 500px, для того, чтобы вы увидели, что такие вещи не дают никаких эффектов и вертикальные margin-ы попросту не работают.
Отрицательные поля
Рассмотренный до этого пример касался положительных значений свойства margin для инлайн-элементов. Но, что, например, произойдёт при отрицательных значениях? Давайте это выясним, сразу же перейдя к наглядному примеру.
<p>Lorem Ipsum is simply <span>dummy text printing </span> <em>and type setting industry.</em> Lorem Ipsum has been the industry's standard dummy text ever since the 1500s PageMaker including versions of Lorem Ipsum.</p>
body {font-size: 16px;} span { background: #FC3; border-radius: 5px; margin-left: -20px; margin-right: -10px; } em { background: #C30; border-radius: 5px; }
В нашей структуре находится два инлайн-элемента: <span> и <em>. Я специально поставил их рядом, чтобы вы могли лучше понять суть отрицательных margin для "инлайн-боксовых инлайнов" Жёлтенькому <span> — у я присвоил левый отрицательный margin-left: -20px;, и сразу же правый отрицательный margin-right: -10px.
Смотрите, что произошло. При margin-left: -20px; <span> сместился влево ровно на 20px, при этом наехав своим боксом на левый текст. Отсюда можно сделать вывод, что при отрицательных левых значениях margin инлайн-элементы сдвигаются в левую сторону, наезжая на своих соседей слева. И не важно, будь то какой-нибудь текст или другой элемент.
А вот при правом отрицательном margin у нас происходит немного другой эффект. В этом случае сдвигается уже следующий за элементом инлайн-бокс, причём сдвиг происходит в левую сторону, на встречу нашему элементу. В нашем случае справа от <span> идёт инлайн-элемент <em>, и как раз таки он перемещается влево ровно на отрицательный margin <span>-а, и выходит на -10px, наезжая при этом поверх <span>-a
.
В принципе, ничего сложного здесь нет, отрицательные margin просты для понимания. Нам всего лишь нужно запомнить пару особенностей их поведения, вот и всё.
Отступы в инлайновом форматировании
Отступы, а они же padding, в инлайновом форматировании, так же как и поля (margin), не могут влиять на соседние отступы по вертикали, но при этом у padding, в отличии от margin, имеется интересный эффект, о котором тоже следовало бы знать. Как обычно, рассмотрим всё на примере.
<p>Lorem Ipsum is simply dummy text printing and type setting industry. Lorem Ipsum has been the industry's standard <span>dummy text printing</span> dummy text ever since the 1500s PageMaker including versions of Lorem Ipsum. Text ever since the 1500s PageMaker including versions of Lorem Ipsum</p>
body {font-size: 16px;} span { background: #FC3; border-radius: 15px; padding: 20px 50px; }
Ну, и конечно же, результат и изображение:
В данном примере я решил объединить сразу оба padding-а, горизонтальный и вертикальный. Но, говорить мы будем именно о последнем (вертикальном), так как горизонтальный padding всего лишь расширяет свой элемент на ширину самого padding-а и, соответственно, такой инлайн-элемент становится просто шире, получая внутренние боковые отступы от своего содержимого. Такое поведение инлайн-элемента можно наблюдать на рисунке.
Но, вот на вертикальных отступах я бы хотел остановиться подробнее. Дело в том, что вертикальные padding-и, с одной стороны, не влияют на поведение своих соседей сверху и снизу, но с другой, могут оказать влияние на высоту и визуальный вид самого элемента. В нашем случае элемент <span> имеет в своих свойствах верхние и нижние padding-и в значении 20px. Но, несмотря на одинаковые значения, поведение этих отступов всё же немного разное. Как можно увидеть на рисунке, за счёт отступов у нашего элемента поменялась не только ширина, но и высота, а точнее у внутреннего содержимого элемента появились верхние и нижние отступы, ровно на значение padding, т.е. выходит 20px. Самое интересное здесь то, что элемент своим верхним отступом залез на своего соседа (текст) сверху, а нижним наоборот подлез под сам текст снизу. Т.е., по сути, вышло, что в вертикальном контексте сам элемент остался на месте, но вот его внутренние отступы заставили его сменить высоту и налезть на и под своих соседей.
Такое поведение может стать полезным во многих случаях. Например, если нам необходимо сделать меню из инлайн-пунктов, которым невозможно прописать ширину и высоту, потому что это не блоки, но при этом необходимо как-то выделить их или назначить необходимый фон. Вот тут-то и смогут помочь вертикальные padding-и.
Границы в инлайновом форматировании
Границы (border) в инлайновом форматировании работают практически так же, как и padding. Но, несмотря на их похожесть, я всё-таки решил сделать тестовый примерчик для наглядности.
<p>Lorem Ipsum is simply dummy text printing and type setting industry. Lorem Ipsum has been the industry's standard <span>dummy text printing</span> dummy text ever since the 1500s PageMaker including versions of Lorem Ipsum. Text ever since the 1500s PageMaker including versions of Lorem Ipsum</p>
body {font-size: 16px;} span { background: #FC3; border-radius: 15px; border: 20px solid red; }
И, конечно же, результат:
Что и следовало доказать. Правые и левые границы отодвинули сам элемент и следующий за ним вправо, а верхние и нижние границы налезли на верхний текст и соответственно под нижний. в общем всё аналогично padding.
Разрыв инлайн-бокса
На самом деле на примере границ и фона я хотел бы показать вам ещё одну очень важную особенность в поведении инлайн-боксов. А точнее, в их разрыве в случаях, когда они не умещаются в один лайн-бокс. Чтобы было понятно, о чём идёт речь, предоставлю код и изображение.
<p>Lorem Ipsum is simply dummy text printing and type setting industry. Lorem Ipsum has been the <span>industry's standard dummy text printing</span> dummy text ever since the 1500s PageMaker including versions of Lorem Ipsum. Text ever since the 1500s PageMaker including versions of Lorem Ipsum</p>
body {font-size: 16px; margin: 0;} span { background: rgba(160,224,160,1); padding: .5em; border: 2px dotted #caa; border-radius: 1em; }
А вот и сам результат и изображение:
В общем ситуация следующая. В параграфе находится всё тот же <span>, но теперь я решил сделать его длиннее, чтобы он не поместился на свою полочку (лайн-бокс). Каждая строка — это отдельный лайн-бокс, а наш инлайн-бокс (<span>) слишком длинный, и поэтому при столкновении с правой границей лайн-бокса его попросту разорвало на две части и та часть, которая не поместилась на текущей строке, перескочила на новую.
Но, постойте, выходит, что у нас теперь в двух лайн-боксах один и тот же инлайн-бокс? — Нет. Дело в том, что если инлайновый бокс не вмещается в свой контейнер и при этом перенос строки не запрещен правилами языка и white-space:nowrap/pre, то такой бокс разрывается на две части, и образует уже две половинки бокса. Но, суть в том, что обе половинки внутри бокса ведут себя по-обычному и никаких особых правил к ним не применяется и мало того, обе части встраиваются в свои лайн-боксы точно так же, как если бы это были два разных инлайн-бокса. Просто заканчивается один лайн-бокс, а за ним начинается другой, первым элементом в котором становится новый инлайн-бокс, содержащий тот текст, что не поместился в первой строке, и имеющий те же CSS-стили.
Да, важная деталь. padding, margin и border не применяются к месту разрыва, и поэтому в конце разорвавшегося инлайн-бокса и в начале нового можно считать значения этих свойств равными нулю.
Для некоторых такое поведение может показаться странным и даже удивительным, но, на самом деле здесь всё вполне логично. Представьте себе, что у вас в строке находится элемент и вы задаёте ему стили: фон, границы, поля. И вдруг он не помещается в один лайн-бокс, рвётся, последняя часть перескакивает на другую строчку и у вашего элемента становится, например, не две боковых границы, а целых четыре. А вот это уже и правда странно, не правда ли? Поэтому не удивляйтесь, а примите это как правильность.
Другие публикации цикла
3. ИКФ: высота строки, часть 1 (3-я публикация цикла “Тайны CSS2.1″).
4. ИКФ: высота строки, часть 2 (4-я публикация цикла “Тайны CSS2.1″).
5. ИКФ: высота строки, часть 3 (5-я публикация цикла “Тайны CSS2.1″).
6. ИКФ: высота строки, часть 4 (6-я публикация цикла “Тайны CSS2.1″).
7. ИКФ: высота строки, часть 5 (7-я публикация цикла “Тайны CSS2.1″).
8. ИКФ: Вертикальное выравнивание в строке, часть 1 (8-я публикация цикла “Тайны CSS2.1″).
9. ИКФ: Вертикальное выравнивание в строке, часть 2 (9-я публикация цикла “Тайны CSS2.1″).
10. ИКФ: Вертикальное выравнивание в строке, часть 3 (10-я публикация цикла “Тайны CSS2.1″).
11. ИКФ: Горизонтальное выравнивание, часть 1 (11-я публикация цикла “Тайны CSS2.1″).
12. ИКФ: Горизонтальное выравнивание, часть 2 (12-я публикация цикла “Тайны CSS2.1″).
P.S. Это тоже может быть интересно:
Очень полезная статья, я думаю она будет понятна как начинающим так и уже опытным верстальщикам. Все довольно подробно расписано и не обязательно глубоко знать CSS, чтобы понять о чем идет речь. Рассматриваются базовые вещи и очень помогает то, что в тексте присутствуют как переводы терминов так и их оригинальные названия. Так как в верстке с этими моментами приходится сталкиваться довольно часто, поэтому полезно знать как это на самом деле работает.
Спасибо за ваше мнение. Но мы настоятельно рекомендуем читать (и перечитывать в последствии) именно все публикации, и начиная с первой! Это поможет намного лучше понять работу инлайнового контекста в CSS, что несомненно повысит опыт верстальщиков любого уровня!
А вот тут http://jsfiddle.net/CWLTD/2/ при увеличении маржинов по горизонтали в Мозилле, Опере и ИЕ9 текст переносится, а вот в Хроме нет. Это такая особенность Хрома или ошибка?
«но так как последний попросту не поместился на эту же строку, он перескочил на новую, а на предыдущей (т.е. на той где наш элемент), в правой части теперь балом правит правый margin -а.» — вот тут, на мой взгляд сформулированно не достаточно полно.
Пришлось скопировать пример в песочницу и подвигать размеры, чтоб понять, что там вообще происходит. А происходит там, действительно, довольно забавные явления…
Казалось бы, если у элемента есть большое правое поле, то это не должно разбивать сам инлайн элемент. И, если поля не хватает на одной строке, то поле попросту должно перепрыгивать на следующую строку, и сдвигать текстовое содержимое другой строки. Ну то есть, по мере уменьшения размеров окна, пока правое поле помещается в текущей строке, оно двигает содержимое на этой строке, а когда место не хватает, то на этой строке поле занимает всё оставшееся место, но по мере уменьшения окна, это место также уменьшается, и весь тот размер поля, который не уместил на текущей строке, переходит на следующую, автоматически сдвигая содержимое. В пределе, когда для поля нет места, всё поле переходит на следующую строку и содержимое сдвигается на величину поля. Ну, наверное, к этому прибавляется отступ для всего инлайнового содержимого блока.
Это, на мой взгляд, наиболее очевидный сценарий, я не говорю что он наиболее правильный…
Но происходит по-другому, по мере уменьшения окна:
В Лисе (Мозиле):
1. Сначала на другую сторону переходит содержимое, сдвигаемое полем (марджином).
2. Потом размер поля уменьшает ровно до того момента, как последнее слово можно перенести на следующую строчку. Когда происходит перенос поля на этой строчке увеличивается до исходных размеров.
3. Как только хотя бы одно слово перенеслось на следующую строчку, к этому слову добавляется правое поле натурального размера. Получается у нас два одинаковых поля справа, на исходной, и следующей строке. Эти поля сохраняются, пока есть место справо.
4. Далее, ситуация, вроде бы, должна повторяться для следующего слова, так почему-то не происходит. На следующей строчке отступ остаётся прежним, пока есть возможность двигать содержимое, когда содержимое всё сдвинуто на третью строчку, отступ просто уменьшается. Также происходит и на первой строчке — отступ просто уменьшается пока вплотную не приблизится к правому краю.
5. Далее оставшиеся слова перетекают на следующую строчку, и, как только самое первое слово нашего инлайна-бокса оказалось на следующей строке, у инлайн-бокса, на этой новой строке, появляется свой левый отступ, заданный в стилях.
До этого, левый отступ был лишь на первой строке, то содержимое инлайн бокса, которое попало на следующую строчку, располагалось в начала новой строки (ну, видимо, с учётом отступов контейнера, в котором они лежат). Отступ слева появляется только когда ВСЁ содердимое инлайн-бокса переместится на следующую строчку.
6. Далее правый отступ сокращается до нуля. При этом отступ слева остаётся таким, каким он задан в стилях. Вообще, последний раз инлайн бокс может полностью разместится в строке, пока позволяет его содержимое плюс ЛЕВЫЙ маржин (поле). И, похоже, минимальный возможный размер строки, до появления полос прокрутки это первое слово инлайн-бокса + левый марджин.
7. Далее последнее слово инлайн бокса перескакивает на следующую строку. При этом, как и следовало ожидать, появляется правый отступ, сдвигающий содержимое на следующую (третью) строку.
8. Далее второе с конца слово смещается вправо до конца и потом перепрыгивает на следующую строку.
9. При этом содержимого слева в строке нет до тех пор, пока ему не позволяет уместится левое поле. По мере того, как слова инлайн-бокса перепрыгивают на следующую строчку, освобождается место слева, и, если его больше левого поля (марджина) нашего инлайн-бокса, то появляется содержимое слева на первой строчке нашего инлайн-бокса.
10. Далее ширина становится меньше натуральной величины нашего инлайн-бокса, без учёта марджинов (полей), и, на верхней строчке работает левый марджин, на нижней — правый марджин. Что логично, поскольку читаем мы слева на право, значит в первой (верхней) строчке наш инлайн бокс от остального инлайн-содержимого отделяет именно левый марджин, а на нижней — правый.
11. Как видно из предыдущего пункта, левый марджин «давит» на содержимое верхней строки и как только поле + первое слово + второе слово в верхней строчке не умещается, второе слово перепрыгивает на вторую строчку.
12. после этого правый отступ у второй строчки сокращается. когда он заканчивается, последнее слово переносится на следующую строчку.
13. на первой строчке если левый отступ + первое слово не помещается, появляется полоса прокрутки.
Ну вот как бы и всё :) Стоит отметить что поведение Лисы несколько не последовательно, поскольку в первых пунктов, оно перенесло сначала одно слово, а дальше это правило не применяется для следующего слова.
Что касается Хрома, то всё немного по-другому.
1. Сначала, понятное дело, содержимое справо двигается правым полем, пока не сдвинет его на следущую строку.
2. Потом правое поле тупо сокращается до нуля.
3. После этого последнее слово инлайн-бокса перепрыгивает на следующую строку. И сдвигает содержимое на правый марджин своего инлайн-бокса.
4. Далее история повторяется для следующего слова.
5. Потом всё содержимое нашего инлайн-бокса оказывается на одной строке. Отметим что с той строки, с которой наш инлайн-бокс сместился, содежимое ещё некоторое время будет «недотягивать» до своего правого края. При уменьшении окна у нашего инлайн-бокса сокращается правый марджин, левый марджин не меняется.
6. Как только свободное место заканчивается, последнее слово переносится, и на следующей строке появляется правый марджин.
7. По мере переноса слов с верхней строки, место слево может оказаться достаточным, чтоб слева присоседилось содержимое. Для этого нужно чтоб это ему позволил левый марджин инлайн-бокса.
8. Далее слова переносятся, сдвигаются, и вообще поведение похоже на поведение в мозиле. Общая логика такая — сверху главное — левый марджин, снизу — правый. Как я уже говорил, вызвано это видимо тем, что читаем мы справа налево, поэтому эти марджину «охраняют» края инлайн-бокса с двух сторон.
При этом, как мне кажется, что хоть для Лисы, хоть для Хрома минимальной шириной является первой слово + левый отступ. Если ширина окна меньше, то появляются полосы прокрутки.
Отличие между Хромом и Мозилой состоит в том, что Лиса более «бережно» относится к своему правому отступу — как только ему не хватает места, переносит последнее слово на следующую строчку (и к этому слову также добавляется отступ). Хром же позволяет отступу уменьшится до нуля, и только потом переносит последнее слово, добавляюю к нему отступ.
Поведение Лисы мне нравится больше, но оно не последовательное, поскольку Лиса не делает аналогичных действий для следующего слова.
Что ближе к спецификации — виднее Вам :-)
Одно могу сказать, хоть можно скопировать Ваш пример в песочницу и посмотреть как «в правой части теперь балом правит правый margin -а», то, каким образом, он правит этот бал, как мне кажется, не совсем очевидно и было бы здорово, если это, в том или ином виде, это было отражено в статье.
Спасибо!
Кстати, «отрицательное» поле возникает и в случае, если инлайн-блок расширился относительно того, чего браузер просчитал изначально:
https://jsfiddle.net/Launder/69cgovz7/2/
Но, кстати, с учётом сказанного, в последнем Вашем выделенном пункте, про разрыв инлайн-бокса, когда разные его части, лежат в разных лайн-боксах, поведения Хрома выглядит более логичным, чем поведение Лисы.
Единственная особенность, когда полосы прокрутки уже появились, из-за верхней строчки (которая, напомню, образуется, по принципу левый марджин + первое слово), то, в нижних строчках, если размер окна позволяет, слова остаются на одной строчке, не смотря на правый марджин. При этом, как только размер окна БЕЗ ПРОКРУТКИ, не помещает очередное слово, оно переносится на следующую строчку.
В результате получается, на первой строчке левый марджин + первое слово, на последующих строчках слова прижаты к правому краю (всё это без учёта паддингов блока).