Введение в инлайновый контекст форматирования (ИКФ): основные понятия (1-я публикация цикла «Тайны CSS2.1»)
В основе этой статьи лежит перепевка спецификации CSS 2.1 по телефону Рабиновичем SelenIT-ом. Если что-то вышло криво — я не виноват, это всё он. Конструктивная критика и предложения по улучшению материала приветствуются!
Перед тем как погрузиться в мир инлайнового контекста, мне казалось, что это не очень сложная тема, и что понять её будет не трудно. Но, чем больше я углублялся в процесс изучения, тем больше понимал, что это далеко не простая и даже очень обширная и сложная тема для понимания.
Ещё больше я боялся за то, что не смогу передать на бумаге все тонкости и особенности построения этого механизма нашим читателям. Поэтому каждую статью цикла я буду стараться писать так, чтобы каждая её деталь была написана на доступном и понятном языке.
Ну, как говорится — с Богом!
Лайн-бокс
Зачем вообще нужен лайн-бокс?
Говоря простым языком, лайн-бокс — это удобный виртуальный контейнер, от которого «пляшут» элементы строки (инлайн-боксы). Дело в том, что при построении строки рендерер (механизм отрисовки страниц в браузерах и построения DOM-дерева) должен как-то узнать, какую Y-координату дать следующей порции инлайнового контента (тексту, картинкам и т.п.). Можно сказать, что лайн-бокс — это способ учёта пространства по вертикали, занимаемого инлайном.
Построение лайн-бокса
В инлайновом контексте форматирования инлайн-боксы выкладываются друг за другом горизонтально, начиная от верха контейнера и заполняются слева направо (или справа налево у евреев и арабов). Горизонтальные margin, border и padding между инлайн-боксами учитываются. Инлайн-боксы могут выравниваться по вертикали по-разному: по верхней либо нижней границе, или по базовым линиям текста в них. Прямоугольная область, содержащая инлайн-боксы из одной строки, называется лайн-боксом.
Так, стоп. Понимаю, что многие из вас уже запутались и потеряли нить. Поэтому теперь предлагаю чуть помедленнее, с кодом и картинками.
Вначале рассмотрим простой пример. Код будет таким:
<p> один <em> два </em> три </p>
Вроде бы всё просто, обычный блочный элемент (<p>), содержащий в себе одну строку, в которой есть текст и инлайн-элемент (<em>). Но на самом деле в подноготной самой строки произошёл следующий эффект:
На картинке мы можем видеть параграф, генерирующий блочный бокс, в котором и должны находится лайн-боксы. Этот блочный элемент содержит в себе один лайн-бокс (1), три инлайн-бокса (2a, 2n, 2a), в одном из которых (2n) находится просто текст. А теперь разберём более детально.
Под цифрой "1" изображён лайн-бокс, сгенерированный сразу же после генерации блочного бокса (параграфа). Так как у нас одна строка, то лайн-бокс соответственно тоже один (о двух-строчных чуть позже).
Под цифрами 2a, 2n, 2a находятся инлайн-боксы. На рисунке их ровно три. Они очень похожи друг на друга, но, несмотря на это, между некоторыми из них есть одно важное отличие. Два боковых илайн-бокса (2a) на самом деле являются анонимными, в отличии от среднего (2n), "натурального". Что это значит?
Анонимный инлайн-бокс
Анонимными инлайн-боксами называются те боксы, которые находятся непосредственно в самом лайн-боксе. Те, которые являются его ближайшими текстовыми потомками, не обёрнутыми ни в один инлайн-элемент.
Для чего это нужно? Давайте представим себе ситуацию. Есть такой html:
<p> red <em>green <span>blue</span></em></p>
И вот такие вот стили:
p { color: red } em {color: green } span { color: blue }
В этом примере есть три инлайн-бокса разного цвета. Со вторым и третьим понятно, а что "красит" первый? Лайн-бокс взять на себя работу по его "покраске" не может, он нужен для других вещей, которые я описывал выше. Лайн-бокс — не "физический объект", а скорее геометрическая условность, он является всего лишь "счетчиком", передавая главному "штабу" информацию о своих координатах. В связи с этим нужен тот, кто сможет взять на себя стили, доставшиеся по наследству от блочного родителя, и "распорядиться этим наследством" как следует. И этим "кем-то" является анонимный инлайн-бокс, к которому и применяется CSS.
* Да, стоит уточнить, что в строчном контексте форматирования CSS применяется именно к инлайн-боксам, будь то они анонимные или "натуральные". Ну, и конечно же то, что в самих инлайн-боксах не бывает анонимных инлайн-боксов, они существуют только на верхнем уровне, как я и говорил выше. В самих инлайн-боксах текст является просто текстом.
"Натуральный" инлайн-бокс (т.е. не анонимный)
"Натуральным" инлайн-боксом можно назвать тот бокс, который генерируется самим инлайн-элементом, в нашем случае это элемент <em>. Так же существует ещё ряд "натуральных" инлайн-боксов, таких как картинки, инлайн-блоки и т.д. В общем, все "живые" инлайн-сущности, которые мы можем видеть в структуре, превращаются в инлайн-боксы.
Ну, и чтобы совсем уж было понятно, я попытаюсь изобразить дерево данной структуры.
- * Block (p)
- * line box
- * anonymous inline box (текст: "один")
- * inline box (em)
- * anonymous inline box (текст: "три")
- * line box
Это был самый лёгкий пример, но, пожалуй, рассмотрим более сложную ситуацию, где структура будет такой:
<p> один <em> два <i> три </i> четыре </em> пять шесть <span> семь <i> восемь </i> девять </span></p>
В 99% случаев текст в документе состоит из двух и более строк. Что же происходит в таком случае с лайн-боксом? Нет, он не разрывается, вместо этого у нас появляется уже два лайн-бокса! Да, именно так. В этой ситуации рендерер заканчивает с первой строкой и начинает ренредить новую. Причём рендерер производит абсолютно такую же операцию над следующей строкой, как и с предыдущей, т.е. начинает выкладывать в ней новые кирпичи один за другим, заново.
Структура в таком случае выглядит так:
На рисунке я постарался отметить каждую детальку, обозначив их цифрами. Как можно увидеть, строк стало две, а следовательно, и лайн-боксов на рисунке образовалось ровно два! Но всё-таки, давайте по порядку.
Как и на предыдущем рисунке, под цифрами "1" находятся лайн-боксы, на изображении они отмечены жёлтым цветом. Самое интересное, несмотря на то что контейнер у нас один (<p>), лайн-боксы в нём абсолютно независимы друг от друга. Когда закончилось построение первого, то на "поле" сразу же появился второй лайн-бокс, с новыми координатами по оси Y. Первый закончил своё построение по той причине, что боксы внутри него попросту заняли всё доступное в нём пространство, а последнему не хватило в лайн-боксе места, и он вынужден был переместиться на новую строку. А раз новая строка – значит новый лайн-бокс. Это железное правило и о нём нужно помнить!
Далее идут уже знакомые вам цифры: "2a" и "2n" обозначают анонимные инлайн-боксы (2а) и соответственно "натуральные" (2n). Их отличие я уже объяснял в первом примере, поэтому единственное, что хочу тут отметить, это то, что теперь у нас в структуре появились составные инлайн-боксы.
Составной инлайн-бокс
Если в инлайн-боксе находится от двух и более инлайн-боксов или, например, текст и инлайн-боксы, то такой инлайн-бокс считается составным, потому что состоит из нескольких частей. В нашем случае мы имеем два составных инлайн-бокса, один из них — элемент <em> в первом лайн-боксе, а другой — <span> во втором. Например, <em> состоит из трёх частей: текст ("два"), инлайн-бокс (<i>) и за ним снова текст ("четыре").
Ну, и последнее, на чём стоит заострить наше внимание, это цифры "3", которые указывают на обычный текст в инлайн-боксах. Стоит заметить, что инлайн-боксом для такого текста является их контейнер, а сами они ничего не генерируют, в отличие, например, от текста в самих лайн-боксах (2а). Дело в том, что внутри инлайн-боксов текст является просто текстом, ему нет смысла генерировать лишние инлайн-боксы, так как CSS стили ему может передать его собственный контейнер.
Дерево такой структуры будет выглядеть так:
- * Block (p)
- * line box
- * anonymous inline box (текст: "один")
- * inline box (em)
- * текст: "два"
- * inline box (i)
- * текст: "четыре"
- * anonymous inline box (текст: "пять")
- * line box
- * anonymous inline box (текст: "шесть")
- * inline box (span)
- * текст: "семь"
- * inline box (i)
- * текст: "девять"
- * line box
Кстати, обратите внимание на один интересный момент. А именно, на анонимные инлайн-боксы в конце первого лайн-бокса и в начале второго. Оба они являются анонимными, потому что их родителями, по факту, являются лайн-боксы. А если бы, например структура выглядела бы так:
<p> один <em> два <i> три </i> четыре </em><i> пять шесть </i><span> семь <i> восемь </i> девять </span></p>
То дерево было бы уже таковым:
- * Block (p)
- * line box
- * anonymous inline box (текст: "один")
- * inline box (em)
- * текст: "два"
- * inline box (i)
- * текст: "четыре"
- * inline box (i)
- * line box
- * inline box (i)
- * inline box (span)
- * текст: "семь"
- * inline box (i)
- * текст: "девять"
- * line box
В этом примере элемент <i> как бы разрывается на две части и образует два инлайн-бокса — в двух строках. Часть содержимого элемента <i>, которой хватило места в первом лайн-боксе, образовала первый инлайн-бокс, а сам факт переноса привел к появлению нового лайн-бокса (новой строки), в начале которой разместился второй инлайн-бокс с остатком содержимого элемента.
Пробел — как анонимный инлайн-бокс или текст
Ещё один важный момент в этой части рассказа, который стоило бы разобрать, это пробелы между инлайн-боксами, которые могут превратиться в анонимные инлайн-боксы или в обычный текст. Чтобы было понятно о чём идёт речь, сразу же приведу наглядный пример.
<p> один <em> два <i> три </i> <i> четыре </i> пять </em> <span> шесть </span></p>
Ну и конечно же изображение:
Я не стал расписывать эту структуру по всем частям, здесь суть не в этом. Я лишь прошу вас обратить внимание на два выделенных места, которые помечены цифрами "1" и "2". Дело в том, что это один из важных моментов при строчном форматировании, о котором следует знать. Суть в самих пробелах. Пробелах между инлайн-элементами или попросту "натуральными" инлайн-боксами. Выяснилось, что эти, как мне до этого казалось, обычные "сдвиги координат", могут генерировать анонимные инлайн-боксы, точнее не все из них, а только пробелы верхнего уровня, находящиеся непосредственно в самом лайн-боксе. В нашем случае такой пробел (сгенерированый анонимный инлайн-бокс) пробел находится между элементами <em></em> и <span></span> и обозначается цифрой "2" .
А вот пробелы, которые находятся между инлайн-элементами в самих инлайн-боксах, тоже не пропадают зря, а становятся обычным текстом, как и любой другой текст в инлайн-боксах. Такой пробел вы можете наблюдать под цифрой "1" в нашем примере.
Я полагаю, что это логично, так как пробел всё же тоже является символом, а значит так же, как и другие символы, имеет право на собственный контейнер, не так ли?
Продолжение:
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. Это тоже может быть интересно:
А какие выводы из всего этого? Где-то в стандартах описаны лайн-боксы? Могут ли у верстальщика возникнуть проблемы, например, с бэкграундом, если он случайно сделает анонимный инлайн-бокс?
Почитайте плз вот эту тему.
http://forum.htmlbook.ru/index.php?showtopic=35259
Этот цикл статей — как раз вольный пересказ стандарта CSS2.1 (который является единственным действующим стандартом для визуальной модели, пока не будет утвержден CSS3 Box).
Понимать механизмы визуализации текста полезно, чтобы знать, кто из браузеров прав в случае разногласий, а кому слать багрепорт :). В этом примере фон :first-line все браузеры, кроме Оперы, применяют к анонимному инлайн-боксу, поэтому слово, прижатое к низу лайн-бокса, оказывается написано белым на белом, без знания «анатомии» инлайнового контекста в подобной ситуации можно запутаться и потерять много времени на отладку.
Так кто же прав, Opera или все остальные?
Я полагаю, что все остальные, т.к. действующая спецификация CSS-селекторов говорит, что «the ::first-line pseudo-element is similar to an inline-level element».
Я там еще вот что нашел: «In CSS a ::first-line pseudo-element is similar to an inline-level element if its ‘float’ property is ‘none’; otherwise, it is similar to a floated element» (раздел 7.2.1). Не пойму, то ли это баг в спеке, то ли какая-то мудреная фича.
Круто, реально опечатка в спеке :). По контексту понятно, что речь про ::first-letter, но да, редакторы лажанулись знатно…
Хаа, прикольно)))
SelenIT, спасибо за пример. Вот.. хотелось бы так же развернуто про вертикальную "анатомию" инлайн бокса. :)
Кстати, тебе отдельное спасибо за комментарий оставленное на Хабре вот тут http://habrahabr.ru/post/117109/, это для меня было открытием. :)
> хотелось бы так же развернуто про вертикальную "анатомию" инлайн бокса
Всё будет. В ближайших публикациях этого цикла! :)
Да, это очень интересная и познавательная тема, спору нет, но всему своё время, потерпите, скоро всё будет!
У Вас «в этом примере» очепятка у padding
А можно ли сделать CSS-правило для первого ЛАЙН-бокса?
поясните мне пожалуйста вторую картинку. «пять» находится в первом лайн-боксе, «шесть» — во втором. как я понял, это обусловлено только тем, что из-за ширины родительского для P блока перенос строки произошёл именно в этом месте. если бы всё, что находится в P на экране располагалась бы в одну строку, то каким бы образом произошло разбиение лайн-боксов?
Извиняюсь, но я не понял вопроса. Вы не могли бы показать на примере и объяснить подробнее плз.
Да, второй лайн-бокс появился именно из-за переполнения ширины. Если бы всё влезло в одну строку, лайн-бокс был бы единственным.
Скажите, лайн-бокс это то же самое, что контейнер строки у Мейера?
Именно! Но только более детальный разбор :)
Макс, хотелось бы в конце статьи небольшой — Итого.
Итог, я сделала из статьи:
1. Лайн-бокс- виртуальный контейнер, который нужен для расчета Y координаты.
2. Инлайн бокс — может быть анонимным и «натуральным».
3. Анонимный бокс — гененируется текстом, а «натуральный» инлайн-бокc инлайн тегом?
4. Каждая новая строка — новый лайн бокс.
5. Пробел в самом лайн боксе — анонимный бокс, а на уровне инлайн-боксов — текст.
Вопросы:
1. Кто знает про анонимный бокс? родитель? можно ли к нему обратится? аноноимный бокс- это интерфейс или также виртуальный бокс?
2. Базовая линия не зависимо от количество инлайн-боксов одна на одну строку. Какой инлайн бокс влияет на её формирование?
Правильнее было бы сказать так. Анонимный инлайн-бокс геренируется текстом или пробелом, которые находятся непосредственно в лайн-боксе, т.е. являются его дочерним потомком, так сказать.
Не совсем. Правильнее было бы сказать, любым элементом (НЕ тегом!) с display: inline. Это может быть <div>, может быть и <span> :)
И не только пробел, текст тоже. Читайте предыдущий ответ.
Эта статья и мы чуть-чуть)))
Родителем анонимного инлайн-бокса является лайн-бокс.
Прочитайте внимательно вот эти темы и эти посты плз.
Все они… виртуальные, гады)
Вот здесь я вам рекомендую не торопить события. Это материал наших следующих статей. Пока переварите эти :)
что-то как то запутано:
и тут же почти:
Может всё таки 2а инлайн боксы?
Охх, что-то мы совсем заматались. Спасибо вам большое, очень полезная правка!
и вот это тоже с толку сбивает
Вроде бы начинеатся разъяснение ананимного инлайн-бокса, но тут же обрывается мысль и сразу же заводится речь о ::first-line.
Вот тут я считаю, что всё верно. Сначала объясняется, что представляет из себя анонимный инлайн-бокс, а далее для чего он собственно нужен. Но всё равно подправил для ясности. Спасибо!
Мы бы были вам очень признательны, если бы прочитали все части нашего цикла и выписали все недочёты нам на почту. psywalker@css-live.ru
я их итак уже читаю
долго думал над "Горизонтальные: margin, border и padding между инлайн-боксами учитываются". может удачнее будет "боковые" ?
Чем?
Имхо, "Горизонтальные" подходят намного лучше. А вообще, странно, что это вас смутило, т.к, я, например, не представляю, как Горизонт может быть иным или непонятным. Может что-то конкретнее есть, что мы не понимаем?
Допустим. А как тогда удачнее назвать вертикальные margin'ы, border'ы и padding'и с Вашей точки зрения?
Лично я не вижу ничего необычного в слове "горизонтальные", более того, считаю, что использование этого термина наиболее удачно вписывается в данный контекст, поскольку в самой спецификации в данном контексте используется именно термин "горизонтальные".
> anonimus inline box
"anonymous"
Спасибо! Исправили эти и другие ляпы в статье.
Можно ли считать Вашу последнюю мысль про анонимные пробелы, воспринимать так: «Наиболее правильно, внутри блочного элемента, создавать строчный, и уже в нём писать текст»?
Какой кошмар! Почему нилзья просто взять и написать вещи своими именами ? inline-block например ? Зачем переводить названия свойств ? Кошмар.
Потому что, во-первых, inline-block — не свойство, а значение. Во-вторых, в статье не переводятся названия свойств. В третьих, понятия line box и inline box, о которых идет речь в статье и без которых невозможно понять многие «странности» поведения текста в CSS, не имеют отношения к термину «inline-block» от слова «совсем». И одной из целей статьи было как раз предотвратить эту путаницу.
Ну и статья написана 6 лет назад, когда русская терминология по строчному форматированию в CSS еще не устоялась. Но у нас есть на эту тему и материал поновее.