Очистка float-элементов посредством создания нового блочного контекста форматирования

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

Задача

Существует некий элемент-контейнер, в котором находится элемент со значением свойства float, установленным как left. Для наглядности у контейнера установлены границы и фон.

5e6fefbabc1ece9ae99d864c601ee5fc
jsFiddle

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

Почему так происходит?

Дело в том, что если значение свойства height незамещаемого блочного элемента (т.е. контейнера в нашем примере) установлено в auto (а это значение установлено по умолчанию), то его высота определяется исходя из высоты элементов-потомков; при этом в расчет берутся только элементы, участвующие в нормальном потоке. Таким образом, float-элементы и элементы, спозиционированные абсолютно, игнорируются при определении высоты контейнера (ссылка на спецификацию).

Решение

Существует несколько вариантов решения этой задачи.

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

Более продвинутым (и, возможно, лучшим из всех) решением является внедрение класса clearfix, популяризованного уже нетвиттеровским Bootstrap’ом и позволяющего добиться того же эффекта без нарушения семантики документа.
Но есть еще одно решение. Оно состоит в том, чтобы задать контейнеру overflow:hidden.

178e16f2d22657fe4296044b8ec13486
jsFiddle

В самом деле, все становится на свои места, контейнер принимает высоту внутреннего элемента, но как это работает?

Объяснение

Прежде чем рассмотреть механизм данного явления, отметим, что не только overflow:hidden решает проблему. Контейнер примет высоту float-элемента, если ему задать абсолютную позицию, установить display: inline-block и в некоторых других случаях. Что же происходит на самом деле?

Дело в том, что во всех этих случаях внутри контейнера создается новый блочный контекст форматирования.
Блочный контекст форматирования — как коробка, в которой лежат вещи — если рядом лежит другая такая же коробка, то вещи из первой не могут повлиять на положение вещей во второй.

982dfecc007259f47e78fc0bfa2e4d70

Если вернуться к верстке, то блочный контекст форматирования — среда, в которой блоки размещаются в привычном для блоков порядке — сверху вниз, расстояние между ними определяется отступами (margins), отступы соседних блоков схлопываются (ссылка на спецификацию). Элементы из разных блочных контекстов форматирования никак не могут повлиять на положение друг друга на странице.

Если вы впервые сталкиваетесь с понятием блочного контекста форматирования, попробуйте решить следующую практическую задачу: jsFiddle.

Итак, с понятием контекста разобрались. Теперь ответим на следующий вопрос — почему при создании блочного контекста форматирования float-элементы учитываются для определения высоты контейнера?

Дело в том, что этот случай отдельно оговорен в спецификации. К блокам, создающим новый блочный контекст форматирования и имеющим height:auto (напомню еще раз, что это значение установлено по умолчанию), применяется следующее правило (ссылка на спецификацию):

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

Короче говоря, при определении высоты элемента, создающего новый блочный контекст форматирования в расчет берется высота непосредственно дочерних float-элементов (ну и конечно же, как обычно, элементов, участвующих в нормальном потоке).

Когда создается новый блочный контекст форматирования

В этой главе перечислены случаи, в которых создается новый контекст форматирования. В контексте проблематики данной статьи этот перечень — список случаев, при которых блок будет подстраиваться по высоте под дочерние float-элементы. Итак, блочный контекст форматирования создается:

  • float-элементами;
  • абсолютно спозиционированными элементами;
  • фиксированными элементами (т.к. position:fixed — разновидность абсолютного позиционирования, ссылка на спецификацию);
  • элементами, представляющими собой блочный контейнер внутри, но имеющими другую структуру снаружи (такие как inline-block, table-cell, table-caption);
  • блоками с overflow установленным в любое значение, кроме visible;
  • элементом fieldset (в большинстве браузеров).

Итого

В статье рассмотрен вопрос подстраивания высоты элемента, создающего новый блочный контекст форматирования под размеры дочерних float-элементов. Этот вопрос — лишь часть механизма работы блочного контекста форматирования, но надеюсь, что данная статья помогла вам разобраться в новых аспектах CSS-спецификации.
Спасибо за внимание.