ИКФ: высота строки, часть 4 (6-я публикация цикла «Тайны CSS2.1»)
line-height для инлайн-блоков и инлайн-таблиц
Несмотря на то, что инлайн-блоки (inline-block), так же как и инлайн-таблицы (inline-table) входят в инлайновое форматирование строк, они имеют свои особенности, которые отличаются от особенностей обычных инлайн-элементов или текста. Это довольно-таки интересно и нам следует о них поговорить.
Эти боксы — особенные: снаружи они ведут себя как неразрывные инлайн-боксы (наподобие отдельных слов текста), но внутри у них — свои собственные, обособленные блочные «миры», блочные контексты форматирования (о них мы расскажем в скором будущем). У инлайн-блока всё его содержимое представляет собой такой «блочный мир» (поэтому он и инлайн-блок), а у инлайн-таблицы отдельный блочный контекст создается каждой ячейкой. И в каждом из этих «маленьких блочных миров» могут находиться собственные лайн-боксы, а в них — инлайн-боксы, как обычно.
Высота инлайн-блоков и инлайн-таблицы определяется по их контенту или по жёстко заданной высоте, как и у обычных блоков. А вот с базовой линией у них всё же есть некие отличия.
Когда инлайн-блок выступает в своей инлайновой роли, у него, как и у площадки шрифта, есть своя базовая линия, по которой он выравнивается относительно окружающего текста. Этой линией становится базовая линия последней строки текста инлайн-блока.
Чтобы не нагонять путаницу, сразу покажу пример:
<p> Внешний текст <span> inline-block inline-block inline-block </span> внешний текст </p>
p { border: 1px solid #000; background: #F90; width: 300px; line-height: 150px; } span { line-height: normal; display: inline-block; border: 1px solid #000; width: 70px; }
Как можно понять из рисунка, инлайн-блок (<span>) встал на общую базовую линию своим самым нижним текстом (своей базовой линией).
Так ведет себя инлайн-блок во всех случаях, кроме двух исключений: если он пуст (не содержит ни символа текста) или у него изменено значение overflow (на любое, кроме visible). В этих случаях по внешней базовой равняется нижняя граница самого инлайн-блока или окончательная координата его нижнего поля (margin).
У инлайн-таблиц тоже есть базовая линия, по которой они выравниваются внутри лайн-бокса. В качестве этой линии берется нижний край ячеек первой строки инлайн-таблицы.
<div> Внешний текст <table> <tr> <td>1</td> <td>2</td> </tr> <tr> <td>3</td> <td>4</td> </tr> </table> внешний текст </div>
div { border: 1px solid #000; background: #F90; width: 300px; padding-left: 20px; line-height: 100px; } table { line-height: normal; display: inline-table; border: 1px solid #000; width: 70px; border-spacing: 4px; } td { padding: 0; border: 1px solid #00f; }
И тут уже результат будет таким:
В нашей инлайн-таблице я сделал пару строк, чтобы было понятно, что вторая и последующая строки не имеют никакого смысла для её базовой линии. Низ первой строки играет решающую роль в этом деле. Именно низ первой строчки в инлайн-таблице и является её базовой линией, он же и выстраивается по внешней.
Система координат для инлайн-блоков и инлайн-таблиц
Если line-height и следовательно, leading может добавить к площадкам шрифта дополнительные "свинцовые" полоски и отодвинуть координату площадки, то с инлайн-блоками и инлайн-таблицами такой номер не пройдёт. Дело в том, что line-height, так же как и leading актуальны только для шрифтов (текста и инлайн-элементов). Поэтому вертикальная координата инлайн-блока и инлайн-таблицы зависит от их высоты.
Можно немного доразбирать наш пример с текстом и инлайн-блоком.
Текст, который находится непосредственно в самом абзаце, имеет достаточно большой line-height (и соответственно leading), чтобы контролировать высоту строки. Сам инлайн-блок состоит из нескольких строк и текста, но line-height не действует на них по той причине, что я нарочно отменил line-height именно для инлайн-блока, вернув его в нормальное состояние. Это можно увидеть в коде (line-height: normal). В противном случае строки внутри инлайн-блока стали бы огромными и растянули бы его самого на соответствующую высоту.
line-height инлайн-блока или инлайн-таблицы применяется к их текстовому содержимому. Если в результате этого часть инлайн-блока (инлайн-таблицы), выступающая над базовой линией или под ней, оказывается больше расстояния между базовой линией и соответствующей границей лайн-бокса, растягиваться начинает уже сам лайн-бокс.
Рассмотрим небольшой пример:
<p> Внешний текст <span> Инлайн-блок Инлайн-блок </span> внешний текст </p>
p { border: 1px solid #000; background: #F90; width: 320px; padding-left: 20px; line-height: 30px; } span { line-height: normal; display: inline-block; border: 1px solid #000; width: 90px; margin: 25px 0; padding: 25px 0; }
Несмотря на то, что высота основных текстовых площадок в абзаце составляет 30px, общая высота строки получается 145px. Происходит это, как вы уже поняли, благодаря нашему инлайн-блоку, который по своей высоте явно превосходит высоту соседних площадок, упираясь краями в верх и низ лайн-бокса и тем самым растягивая его. Высота инлайн-блока состоит из верхнего и нижнего полей (margin), границ (border), внутреннего отступа (padding) и общей высоты всех строк его содержимого.
Заметьте, что общая базовая линия тоже меняет свои координаты, подтягиваясь к базовой линии нашего инлайн-блока. Здесь ситуация напоминает вариант, когда базовая линия выравнивается по самому высокому инлайн-боксу в строке. В нашем случае самым высоким в строке является инлайн-блок, поэтому весь остальной "инлайновый мир" по бокам вынужден подстраиваться именно под него.
Кстати, с точки зрения размещения на странице, логичнее считать неподвижной именно верхнюю границу, потому что она упирается в предыдущий лайн-бокс. А базовая линия и нижняя граница могут гулять от нее вниз. Даже если инлайн-блок или инлайн-таблица растягивает верх лайн-бокса, на самом деле это базовая линия удаляется от верха.
Заключение
В этой статье мы описали то, как инлайн-блоки и инлайн-таблицы должны вести себя по спецификации CSS. К сожалению, браузеры "любят" ее нарушать (даже такие, как Хром и Сафари). О паре таких исключений мы расскажем в следующей главе.
Другие публикации цикла
3. ИКФ: высота строки, часть 1 (3-я публикация цикла “Тайны CSS2.1″).
4. ИКФ: высота строки, часть 2 (4-я публикация цикла “Тайны CSS2.1″).
5. ИКФ: высота строки, часть 3 (5-я публикация цикла “Тайны 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. Это тоже может быть интересно:
Или если его overflow не visible.
И еще из border’а.
Мне кажется, что, если в названии статьи присутствуют слова «Тайны CSS», надо хотя бы вскользь упомянуть и о другом варианте.
troll
Спасибо за ценные замечания!
По первым двум пунктам в статью внесены исправления, а по третьему пункту — см. следующую статью
В разных браузерах (Chrome 20 и Firefox 11) этот пример отображается по разному: http://css-live.ru/Primer/context-formating/publication6/line-height-inline-table.html. Поэтому обобщения в статье — они лишние и, возможно, не совсем верны. Но для общего понимания инлайн-блочности и инлайновости в вёрстке цикл статей полезен.
clavin, это баг Webkit. Его пофиксили совсем недавно, и до Хрома изменения еще не дошли.
И хорошо бы ввсти какие-нибудь нормальные термины, даже пусть они будут только в рамках этого цикла статей. А то всякие "площадки" и "высота строки" уже ни о чём не говорят, и кажется они используются как попало и не к месту.
Дело в том, что к терминам мы подходим очень основательно и перед тем, как писать тот или иной термин, сто раз всё обдумываем. Поэтому предложенные нами термины вполне нормальные, а чтобы лучше понять, что означает любой из них, рекомендуем читать наш цикл с самого начала, тогда и понимание придёт сразу же!
Но, если всё-таки у вас есть какие-то свои предложения, более "нормальных" терминов на ваш взгляд, то пишите, обсудим.
если можно, у меня вопрос по примеру http://css-live.ru/Primer/context-formating/publication6/line-height-inline-block.html
firebug на вкладке "макет" показывает высоту блока <p> равной 152px. я не понимаю откуда эта цифра взялась. дело в том, что размер шрифта по умолчанию задан браузером 13px. это значит что высота кегельной площадки чуть больше, пусть будет 17px например. кроме того блоку <p> вы в css прописали line-height: 150px
то есть общая высота <p> теоретически должна быть примерно 167px. скажите пожалуйста в каком месте расчёта я ошибся?
про инлайн-блок я не упоминал, так как его высота явно меньше 150px и влияние на высоту <p> не оказывает
Какая разница, какой высоты площадки в данной ситуации? Здесь они намного меньше, чем line-height, а значит half-leading'и самого текстового содержимого в абзаце будут упираться именно в него. На сам инлайн-блок line-height не действует, т.к. он действует на его текстовое содержимое, у которого line-height отменён, кстати. Абзац должен быть 152px, включать в себя line-height: 150px + бордеры сверху и снизу.
Не вполне понятно поведение Лисы при определении межстрочных расстояний, как раз для инлайн-блоков.
Иллюстрация:
http://jsfiddle.net/Launder/e17qxb28/6/
Для начала прокомментирую пример в целом и поведение Хрома.
Поскольку метрика шрифта не вполне отражает занимаемое текстовыми площадками место (что именно отражает метрика — большой вопрос, по крайней мере, для меня), то занимаемое текстовыми площадками место, больше, аналогичного по численному размеру, инлайн-блока. Метрика шрифта подразумевает определённую высоту как над, так и под, базовой линией.
Пустой инлайн-блок — воспринимается строкой как буква, и, соответственно, располагается НА базовой линии. Поэтому весь размер инлайн-блока располагается НАД базовой линией и может занимать больше места, чем высота, выделенная метрикой шрифта НАД базовой линией, поскольку часть высоты площадки метрики шрифта уходит под базовую линиею. И, лежащий на базовой линии, инлайн-блок немного распирает лайн-бокс в котором лежит. Похоже, в нашем примере, распирает на один пиксель.
Примечание: Не смотря на идентичное отображение в Хроме и Лисе, Лиса изначально сверху выделяет небольшое межстрочное расстояние. Зачем — мне непонятно. Тем не менее, если в Хроме инлайн-блок, расположенный на первой строчке, немного опустил лайн-бокс вниз, то в Лисе, инлайн-блок просто ушёл в уже зарезервированную щель вверху, размером в один пиксель.
Теперь напишем что-нибудь в инлайн-блоке, например «sm». Или «Sp», Наличие выносных элементов и заглавных букв, ничего, по видимому, не меняет, поскольку площадки для букв определённого шрифта и определённого размера одинаковы (могут ли какие-нибудь экзотичные символы выходит за эти площадки мне неизвестно). Инлайн-блок, наполненный текстовым содержимым, выравнивает по базовой линии свою нижнюю строчку. Строчка в инлайн-блоке у нас одна, её он и выравнивает по базовой линии.
Поскольку у инлайн-блока появляется «потребность» что-нибудь добавить ПОД базовую линию, то он и добавляет. И добавляет то, что может, а в запасе у него, как мы уже выяснили, только один пиксель (инлайн-блок выше на один пиксель размера текстовой прощадки шрифта, той её части, которая располагается НАД базовой линией).
Соответственно, инлайн-блок опускается на один пиксель.
Точно такая же история и на второй строчке — как только мы добавляем текст
в инлайн-блок, он сползает вниз на один пиксель и перестаёт распирать им
содержащий его лайн-бокс — строчка уменьшается на один пиксель, а поскольку,
строки идут одна за другой свеху вниз, то нижняя строчка «подтягивается» к верхней строке на один пиксель.
Всё, в принципе, более-менее понятно.
Но вот у Лисы поведение какое-то странное…
Добавляем текст, например Sp. Ничего не изменилось для верхнего инлайн-блока.
И инлайн-блок, располагается на базовой линии, и текст на нём также.
В принципе, такое поведение объяснить можно, если считать инлайн-блок, для
внешних объектов, буквой. Но как тогда объяснить то, что если мы увеличим высоту инлайн-блока, скажем, до 20 пикселей, то он не «уйдёт» вверх, а опустится вниз?
То есть, непонятно, откуда, при увеличении размеров блока, «желание» сначала возвысится на один пиксель межстрочного расстояния (я так понимаю его браузер назначает исходя из размеров шрифта), а уже потом опускаться, для того, чтоб «вместить» шрифт.
Ведь если мы будем увеличивать высоту инлайн-блока без содержания, то он так и будет
располагаться на базовой линии, и распирать собой лайн-бокс.
Если же мы уменьшим размер инлайн-блока, до, скажем, 10 пикселей, то, для случая когда он пуст, он, конечно же, будет располагаться на базовой линии, а если мы туда запихнём текст, то он почему-то опять-таки будет «прицеплен» к верху.
Почему так?
Второе. Как только мы добавляем содержимое для первой строчки в инлайн-блок…
Инлайн-блок во второй строчке, «прицепляется» к первой строчке!
Интересно, что если мы вообще удалим инлайн-блок в первой строчке, то инлайн-блок
во-второй строчке, также прицепляется к первой строчке.
Ощущение, что инлайн блок во второй строчке, когда «видит» пустой инлайн-блок
на первой строчке, считает «мы обе буквы!», и ведут себя адекватно, а когда сверху исчезает пустой инлайн-блок, то считает своим долгом заполнить и межстрочное расстояние как… блок!?
В общем, логика мозилы в этом примере мне не понятна.
Далее. При добавлении текстового содержимого во вторую строчку также ничего не меняется — инлайн-блок как был «прибит» к первой строчке, так и остался.
И только когда мы убираем текстовое содержимое из инлайн-блока верхней строчки,
нижняя начинает вести себя понятно — появляется межстрочное расстояние, одинаковое и для инлайн-блока, и для текстового бокса, текст в инлайн-блоке располагается по базовой линии и «избыточный» один пиксель высоты опускается ниже базовой линии.
Когда же мы убираем текстовое содержимое и из второй строчки(исходное состояние), то инлайн-блок второй строчки «поднимается» на один пиксель чтобы встать на базовую линию, расширяя собой строчку и сдвигая базовую линию немного вниз, что сдвигает текстовый бокс на один пиксель.
Далее, история повторяется, добавляем текстовое содержимое к верхней строчки, инлайн-блок нижней, прижимается вплотную к верхней строчке, переставая растягивать лайн-бокс. Базовая линия поднимается на один пиксель вверх, а за ней и текстовый бокс.
В результате текстовый бокс, как, видимо, и должен, находится на расстоянии одного пикселя от верхней строчки, а инлайн-блок прижат к верхней строчке.
Вот этот прижим к верхней строчке инлайн-блока нижней строчки в Лисе мне кажется странным. Можно его как-нибудь объяснить?
Внесу немного ясности в эту простыню :-)
желание оттуда, что при определённом размере шрифта этот пиксель межстрочного расстояния добавляет мозила.
А для инлайн-блока, она, видимо, не добавляет, соответственно он и возвышается.
Потому что когда в инлайн-блоке есть текст, то последняя его строка выравнивается по базовой линии родителя (у нас строка единственная). А поскольку для текста инлайн-блок — блок, то содержимое блока может выходить за размеры блока, вот и получается что текст находится на базовой линии, а блок где-то вверху болтается…
Он перестаёт «чувствовать» межстрочное расстояние (полосочку между залитыми фонами строчками). Должен ли он на него ориентироваться? Ну если считать strut родителя нулевым, то, возможно, что и не должен. Впрочем, может и должен, если внутри инлайн-блока strut не нулевой, ведь ориентируется же инлайн-блок по базовой линии, своей нижней строчкой…
А может это должно зависеть от размера инлайн-блока… Хром добавляет и выглядит всё аккуртаненько…
В общем я думаю, может быть по разному, другой вопрос, почему на поведение инлайн-блока второй строчки, влияет наличие текста в инлайн-блоке в первой строчке.
Получается двоякая ситуация: когда в верхнем инлайн-блоке появляется текст, браузер тащит все инлайн-блоки наверх, но пустой блок уже не тащит вниз, потому что текста в нём нет :-)
Впрочем если у нас три строчки:
http://jsfiddle.net/Launder/e17qxb28/8/
то Лиса даже пустой блок третьей строчки решила прижать ко второй строке…
В общем, какое-то странное и запутанное поведение, следов не найдёшь, прямо-таки Лисья хитрость…
Кстати, если у нас несколько инлайн-блоков, и при наведении, совершается нечто, требующее
overflow: hidden
, то как только у одного из блоков появится это свойство, то его расположение относительно других сразу изменится: базовая линия перестанет «видеть» его нижнюю строчку, и расположит весь инлайн-блок на базовой линии, словно букву, а значит выделяемый инлайн-блок подпрыгнет вверх, а визуально, скорее, соседи опустятся вниз, расширяя строчку.Чтоб этого избежать, в первом приближении, нужно всех поставить в одинаковые условия, присвоив всем элементам
overflow: hidden
. Что, правда, в этом случае, делать, если нужно, чтоб содержимое инлайн-блоков как-то взаимодействовало между собой, скажем выравнивалось по той же базовой линии, в первом приближении, не совсем понятно… Насколько я слышал, выравнивание инлайн-блоков и без базовой линии, не такая простая затея.Может как-то обыграть с first-line? То есть если (определённый) инлайн-блок лежит на первой строке у него есть марджин(или паддинг), а если не на первой, то у него другие харрактеристики отступов?
Скажите, а можно ли как-то заставить инлайн-блоки схлопываться своими марджинами? (если и на той и на другой строчке они самые высокие).
Понимаю, что по логике, марджин инлайн-блока влияет на высоту лайн-бокса, в котором лежит и строится каждый лайн-бокс исходя из содержимого, а схлопывание — это вообще блочный контекст, и, тем не менее, мало ли, может есть возможность…
По стандарту у инлайн-блоков, как и у флоатов, ничего не схлопывается. Если я правильно понял задачу, что-то подобное можно будет сделать на флексбоксах, когда в них придут универсальные свойства *-gap (http://css-live.ru/vecssti-s-polej/svojstva-dlya-intervalov-gap-stanovyatsya-universalnymi.html).
Да, согласен, особенно здорово, если эти свойства будут доступны и из «нейтрального» блочного контекста.
Пробовал изменять свойства дочерних инлайн-блоков через ::first-line (чтоб когда они ещё не перепрыгнули на другую строку у них были одним свойства(отступы), а когда перепрыгнут — другие), но это, похоже, дохлый номер :-(