CSS-live.ru

ИКФ: высота строки, часть 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. К сожалению, браузеры "любят" ее нарушать (даже такие, как Хром и Сафари). О паре таких исключений мы расскажем в следующей главе.

Другие публикации цикла

1. Введение в инлайновый контекст форматирования (ИКФ): основные понятия (1-я публикация цикла “Тайны CSS2.1″).

2Введение в инлайновый контекст форматирования (ИКФ): основные понятия (2-я публикация цикла “Тайны CSS2.1″).

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. Это тоже может быть интересно:

15 комментариев

  1. Если же внутри инлайн-блока пустота, то по внешней базовой равняется нижняя граница самого инлайн-блока или окончательная координата его нижнего поля (margin)

    Или если его overflow не visible.

    Высота инлайн-блока состоит из верхнего и нижнего полей (margin), внутреннего отступа (padding) и общей высоты самих строк

    И еще из border’а.

    Именно низ первой строчки в инлайн-таблице и является её базовой линией, он же и выстраивается по внешней.

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

  2. В разных браузерах (Chrome 20 и Firefox 11) этот пример отображается по разному: http://css-live.ru/Primer/context-formating/publication6/line-height-inline-table.html. Поэтому обобщения в статье — они лишние и, возможно, не совсем верны. Но для общего понимания инлайн-блочности и инлайновости в вёрстке цикл статей полезен.

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

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

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

  4. если можно, у меня вопрос по примеру 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> не оказывает

    1. Какая разница, какой высоты площадки в данной ситуации? Здесь они намного меньше, чем line-height, а значит half-leading'и самого текстового содержимого в абзаце будут упираться именно в него. На сам инлайн-блок line-height не действует, т.к. он действует на его текстовое содержимое, у которого line-height отменён, кстати. Абзац должен быть 152px, включать в себя  line-height150px + бордеры сверху и снизу.

  5. Не вполне понятно поведение Лисы при определении межстрочных расстояний, как раз для инлайн-блоков.

    Иллюстрация:

    http://jsfiddle.net/Launder/e17qxb28/6/

    Для начала прокомментирую пример в целом и поведение Хрома.

    Поскольку метрика шрифта не вполне отражает занимаемое текстовыми площадками место (что именно отражает метрика — большой вопрос, по крайней мере, для меня), то занимаемое текстовыми площадками место, больше, аналогичного по численному размеру, инлайн-блока. Метрика шрифта подразумевает определённую высоту как над, так и под, базовой линией.
    Пустой инлайн-блок — воспринимается строкой как буква, и, соответственно, располагается НА базовой линии. Поэтому весь размер инлайн-блока располагается НАД базовой линией и может занимать больше места, чем высота, выделенная метрикой шрифта НАД базовой линией, поскольку часть высоты площадки метрики шрифта уходит под базовую линиею. И, лежащий на базовой линии, инлайн-блок немного распирает лайн-бокс в котором лежит. Похоже, в нашем примере, распирает на один пиксель.
    Примечание: Не смотря на идентичное отображение в Хроме и Лисе, Лиса изначально сверху выделяет небольшое межстрочное расстояние. Зачем — мне непонятно. Тем не менее, если в Хроме инлайн-блок, расположенный на первой строчке, немного опустил лайн-бокс вниз, то в Лисе, инлайн-блок просто ушёл в уже зарезервированную щель вверху, размером в один пиксель.
    Теперь напишем что-нибудь в инлайн-блоке, например «sm». Или «Sp», Наличие выносных элементов и заглавных букв, ничего, по видимому, не меняет, поскольку площадки для букв определённого шрифта и определённого размера одинаковы (могут ли какие-нибудь экзотичные символы выходит за эти площадки мне неизвестно). Инлайн-блок, наполненный текстовым содержимым, выравнивает по базовой линии свою нижнюю строчку. Строчка в инлайн-блоке у нас одна, её он и выравнивает по базовой линии.
    Поскольку у инлайн-блока появляется «потребность» что-нибудь добавить ПОД базовую линию, то он и добавляет. И добавляет то, что может, а в запасе у него, как мы уже выяснили, только один пиксель (инлайн-блок выше на один пиксель размера текстовой прощадки шрифта, той её части, которая располагается НАД базовой линией).
    Соответственно, инлайн-блок опускается на один пиксель.
    Точно такая же история и на второй строчке — как только мы добавляем текст
    в инлайн-блок, он сползает вниз на один пиксель и перестаёт распирать им
    содержащий его лайн-бокс — строчка уменьшается на один пиксель, а поскольку,
    строки идут одна за другой свеху вниз, то нижняя строчка «подтягивается» к верхней строке на один пиксель.
    Всё, в принципе, более-менее понятно.
    Но вот у Лисы поведение какое-то странное…
    Добавляем текст, например Sp. Ничего не изменилось для верхнего инлайн-блока.
    И инлайн-блок, располагается на базовой линии, и текст на нём также.
    В принципе, такое поведение объяснить можно, если считать инлайн-блок, для
    внешних объектов, буквой. Но как тогда объяснить то, что если мы увеличим высоту инлайн-блока, скажем, до 20 пикселей, то он не «уйдёт» вверх, а опустится вниз?
    То есть, непонятно, откуда, при увеличении размеров блока, «желание» сначала возвысится на один пиксель межстрочного расстояния (я так понимаю его браузер назначает исходя из размеров шрифта), а уже потом опускаться, для того, чтоб «вместить» шрифт.
    Ведь если мы будем увеличивать высоту инлайн-блока без содержания, то он так и будет
    располагаться на базовой линии, и распирать собой лайн-бокс.
    Если же мы уменьшим размер инлайн-блока, до, скажем, 10 пикселей, то, для случая когда он пуст, он, конечно же, будет располагаться на базовой линии, а если мы туда запихнём текст, то он почему-то опять-таки будет «прицеплен» к верху.
    Почему так?
    Второе. Как только мы добавляем содержимое для первой строчки в инлайн-блок…
    Инлайн-блок во второй строчке, «прицепляется» к первой строчке!
    Интересно, что если мы вообще удалим инлайн-блок в первой строчке, то инлайн-блок
    во-второй строчке, также прицепляется к первой строчке.
    Ощущение, что инлайн блок во второй строчке, когда «видит» пустой инлайн-блок
    на первой строчке, считает «мы обе буквы!», и ведут себя адекватно, а когда сверху исчезает пустой инлайн-блок, то считает своим долгом заполнить и межстрочное расстояние как… блок!?
    В общем, логика мозилы в этом примере мне не понятна.
    Далее. При добавлении текстового содержимого во вторую строчку также ничего не меняется — инлайн-блок как был «прибит» к первой строчке, так и остался.
    И только когда мы убираем текстовое содержимое из инлайн-блока верхней строчки,
    нижняя начинает вести себя понятно — появляется межстрочное расстояние, одинаковое и для инлайн-блока, и для текстового бокса, текст в инлайн-блоке располагается по базовой линии и «избыточный» один пиксель высоты опускается ниже базовой линии.
    Когда же мы убираем текстовое содержимое и из второй строчки(исходное состояние), то инлайн-блок второй строчки «поднимается» на один пиксель чтобы встать на базовую линию, расширяя собой строчку и сдвигая базовую линию немного вниз, что сдвигает текстовый бокс на один пиксель.
    Далее, история повторяется, добавляем текстовое содержимое к верхней строчки, инлайн-блок нижней, прижимается вплотную к верхней строчке, переставая растягивать лайн-бокс. Базовая линия поднимается на один пиксель вверх, а за ней и текстовый бокс.
    В результате текстовый бокс, как, видимо, и должен, находится на расстоянии одного пикселя от верхней строчки, а инлайн-блок прижат к верхней строчке.
    Вот этот прижим к верхней строчке инлайн-блока нижней строчки в Лисе мне кажется странным. Можно его как-нибудь объяснить?

    1. Внесу немного ясности в эту простыню :-)

      То есть, непонятно, откуда, при увеличении размеров блока, «желание» сначала возвысится на один пиксель межстрочного расстояния
      (я так понимаю его браузер назначает исходя из размеров шрифта), а уже потом опускаться, для того, чтоб «вместить» шрифт.

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

      Если же мы уменьшим размер инлайн-блока, до, скажем, 10 пикселей, то, для случая когда он пуст, он, конечно же, будет располагаться на базовой линии, а если мы туда запихнём текст, то он почему-то опять-таки будет «прицеплен» к верху.
      Почему так?

      Потому что когда в инлайн-блоке есть текст, то последняя его строка выравнивается по базовой линии родителя (у нас строка единственная). А поскольку для текста инлайн-блок — блок, то содержимое блока может выходить за размеры блока, вот и получается что текст находится на базовой линии, а блок где-то вверху болтается…

      Как только мы добавляем содержимое для первой строчки в инлайн-блок…
      Инлайн-блок во второй строчке, «прицепляется» к первой строчке!

      Он перестаёт «чувствовать» межстрочное расстояние (полосочку между залитыми фонами строчками). Должен ли он на него ориентироваться? Ну если считать strut родителя нулевым, то, возможно, что и не должен. Впрочем, может и должен, если внутри инлайн-блока strut не нулевой, ведь ориентируется же инлайн-блок по базовой линии, своей нижней строчкой…
      А может это должно зависеть от размера инлайн-блока… Хром добавляет и выглядит всё аккуртаненько…
      В общем я думаю, может быть по разному, другой вопрос, почему на поведение инлайн-блока второй строчки, влияет наличие текста в инлайн-блоке в первой строчке.
      Получается двоякая ситуация: когда в верхнем инлайн-блоке появляется текст, браузер тащит все инлайн-блоки наверх, но пустой блок уже не тащит вниз, потому что текста в нём нет :-)

      Впрочем если у нас три строчки:

      http://jsfiddle.net/Launder/e17qxb28/8/

      то Лиса даже пустой блок третьей строчки решила прижать ко второй строке…
      В общем, какое-то странное и запутанное поведение, следов не найдёшь, прямо-таки Лисья хитрость…

  6. Кстати, если у нас несколько инлайн-блоков, и при наведении, совершается нечто, требующее overflow: hidden, то как только у одного из блоков появится это свойство, то его расположение относительно других сразу изменится: базовая линия перестанет «видеть» его нижнюю строчку, и расположит весь инлайн-блок на базовой линии, словно букву, а значит выделяемый инлайн-блок подпрыгнет вверх, а визуально, скорее, соседи опустятся вниз, расширяя строчку.
    Чтоб этого избежать, в первом приближении, нужно всех поставить в одинаковые условия, присвоив всем элементам overflow: hidden. Что, правда, в этом случае, делать, если нужно, чтоб содержимое инлайн-блоков как-то взаимодействовало между собой, скажем выравнивалось по той же базовой линии, в первом приближении, не совсем понятно… Насколько я слышал, выравнивание инлайн-блоков и без базовой линии, не такая простая затея.

    1. Может как-то обыграть с first-line? То есть если (определённый) инлайн-блок лежит на первой строке у него есть марджин(или паддинг), а если не на первой, то у него другие харрактеристики отступов?

  7. Скажите, а можно ли как-то заставить инлайн-блоки схлопываться своими марджинами? (если и на той и на другой строчке они самые высокие).
    Понимаю, что по логике, марджин инлайн-блока влияет на высоту лайн-бокса, в котором лежит и строится каждый лайн-бокс исходя из содержимого, а схлопывание — это вообще блочный контекст, и, тем не менее, мало ли, может есть возможность…

    1. По стандарту у инлайн-блоков, как и у флоатов, ничего не схлопывается. Если я правильно понял задачу, что-то подобное можно будет сделать на флексбоксах, когда в них придут универсальные свойства *-gap (http://css-live.ru/vecssti-s-polej/svojstva-dlya-intervalov-gap-stanovyatsya-universalnymi.html).

      1. Да, согласен, особенно здорово, если эти свойства будут доступны и из «нейтрального» блочного контекста.
        Пробовал изменять свойства дочерних инлайн-блоков через ::first-line (чтоб когда они ещё не перепрыгнули на другую строку у них были одним свойства(отступы), а когда перепрыгнут — другие), но это, похоже, дохлый номер :-(

Оставить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

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