Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
423 lines (269 sloc) 19.2 KB

Свойство "float"

Свойство float в CSS занимает особенное место. До его появления расположить два блока один слева от другого можно было лишь при помощи таблиц. Но в его работе есть ряд особенностей. Поэтому его иногда не любят, но при их понимании float станет вашим верным другом и помощником.

Далее мы рассмотрим, как работает float, разберём решения сопутствующих проблем, а также ряд полезных рецептов.

Как работает float [#float-algorithm]

Синтаксис:

float: left | right | none | inherit;

При применении этого свойства происходит следующее:

  1. Элемент позиционируется как обычно, а затем вынимается из документа потока и сдвигается влево (для left) или вправо (для right) до того как коснётся либо границы родителя, либо другого элемента с float.
  2. Если пространства по горизонтали не хватает для того, чтобы вместить элемент, то он сдвигается вниз до тех пор, пока не начнёт помещаться.
  3. Другие непозиционированные блочные элементы без float ведут себя так, как будто элемента с float нет, так как он убран из потока.
  4. Строки (inline-элементы), напротив, "знают" о float и обтекают элемент по сторонам.

Ещё детали:

  1. Элемент при наличии float получает display:block.

    То есть, указав элементу, у которого display:inline свойство float: left/right, мы автоматически сделаем его блочным. В частности, для него будут работать width/height.

    Исключением являются некоторые редкие display наподобие inline-table и run-in (см. Relationships between 'display', 'position', and 'float')

  2. Ширина float-блока определяется по содержимому. ("CSS 2.1, 10.3.5").

  3. Вертикальные отступы margin элементов с float не сливаются с отступами соседей, в отличие от обычных блочных элементов.

Это пока только теория. Далее мы рассмотрим происходящее на примере.

Текст с картинками

Одно из первых применений float, для которого это свойство когда-то было придумано -- это вёрстка текста с картинками, отжатыми влево или вправо.

Например, вот страница о Винни-Пухе с картинками, которым поставлено свойство float:

Её HTML-код выглядит примерно так:

<img src="1.jpg" style="float:right">
<p>Текст...</p>
<p>Текст...</p>

<img src="2.jpg" style="float:left">
<p>Текст...</p>

<img src="3.jpg" style="float:right">
<p>Текст...</p>

Каждая картинка, у которой есть float, обрабатывается в точности по алгоритму, указанному выше.

Посмотрим, например, как выглядело бы начало текста без float:

[iframe src="winnie-nofloat" height=300 border=1 link edit]

  1. Элемент IMG вынимается из документа потока. Иначе говоря, последующие блоки начинают вести себя так, как будто его нет, и заполняют освободившееся место (изображение для наглядности полупрозрачно):

[iframe src="winnie-nofloat-1" height=250 border=1 link edit]

  1. Элемент IMG сдвигается максимально вправо(при float:right):

[iframe src="winnie-nofloat-2" height=250 border=1 link edit]

  1. Строки, в отличие от блочных элементов, "чувствуют" float и уступают ему место, обтекая картинку слева:

[iframe src="winnie-nofloat-3" height=250 border=1 link edit]

При float:left -- всё то же самое, только IMG смещается влево (или не смещается, если он и так у левого края), а строки -- обтекают справа

Строки и инлайн-элементы смещаются, чтобы уступить место float. Обычные блоки -- ведут себя так, как будто элемента нет.

Чтобы это увидеть, добавим параграфам фон и рамку, а также сделаем изображение немного прозрачным:

[iframe src="winnie-block-bg" height=300 border=1 link edit]

Как видно из рисунка, параграфы проходят "за" float. При этом строки в них о float'ах знают и обтекают их, поэтому соответствующая часть параграфа пуста.

Блок с float

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

Посмотрим, как это работает, на конкретной задаче -- сделать рамку с названием вокруг картинки с Винни.

HTML будет такой:

<h2>Винни-Пух</h2>

*!*
<div class="left-picture">
  <img src="winnie-mult.jpg" width="200" height="150">
  <div>Кадр из советского мультфильма</div>
</div>
*/!*

<p>Текст...</p>

..То есть, div.left-picture включает в себя картинку и заголовок к ней. Добавим стиль с float:

.left-picture {
*!*
  float: left;
*/!*

  /* рамочка и отступ для красоты (не обязательно) */
  margin: 0 10px 5px 0;
  text-align: center;
  border: 1px solid black;
}

Результат:

[iframe src="winnie-block" height=300 border=1 link edit]

Заметим, что блок div.left-picture "обернул" картинку и текст под ней, а не растянулся на всю ширину. Это следствие того, что ширина блока с float определяется по содержимому.

Очистка под float

Разберём еще одну особенность использования свойства float.

Для этого выведем персонажей из мультфильма "Винни-Пух". Цель:

[iframe src="winnie-clear-3" height=600 border=1 link edit]

Реализуем её, шаг за шагом.

Шаг 1. Добавляем информацию

Попробуем просто добавить Сову после Винни-Пуха:

<h2>Винни-Пух</h2>
<div class="left">Картинка</div>
<p>..Текст о Винни..</p>

<h2>Сова</h2>
<div class="left">Картинка</div>
<p>..Текст о Сове..</p>

Результат такого кода будет странным, но предсказуемым:

[iframe src="winnie-clear-1" border="1" height=500 link edit]

Произошло следующее:

  • Заголовок <h2>Сова</h2> не заметил float (он же блочный элемент) и расположился сразу после предыдущего параграфа <p>..Текст о Винни..</p>.
  • После него идёт float-элемент -- картинка "Сова". Он был сдвинут влево. Согласно алгоритму, он двигается до левой границы или до касания с другим float-элементом, что и произошло (картинка "Винни-Пух").
  • Так как у совы float:left, то последующий текст обтекает её справа.

Шаг 2. Свойство clear

Мы, конечно же, хотели бы расположить заголовок "Сова" и остальную информацию ниже Винни-Пуха.

Для решения возникшей проблемы придумано свойство clear.

Синтаксис:

clear: left | right | both;

Применение этого свойства сдвигает элемент вниз до тех пор, пока не закончатся float'ы слева/справа/с обеих сторон.

Применим его к заголовку H2:

h2 {
  clear: left;
}

Результат получившегося кода будет ближе к цели, но всё еще не идеален:

Элементы теперь в нужном порядке. Но куда пропал отступ margin-top у заголовка "Сова"?

Теперь заголовок "Сова" прилегает снизу почти вплотную к картинке, с учётом её margin-bottom, но без своего большого отступа margin-top.

Таково поведение свойства clear. Оно сдвинуло элемент h2 вниз ровно настолько, чтобы элементов float не было сбоку от его верхней границы.

Если посмотреть на элемент заголовка внимательно в инструментах разработчика, то можно заметить отступ margin-top у заголовка по-прежнему есть, но он располагается "за" элементом float и не учитывается при работе в clear.

Чтобы исправить ситуацию, можно добавить перед заголовком пустой промежуточный элемент без отступов, с единственным свойством clear:both. Тогда уже под ним отступ заголовка будет работать нормально:

<h2>Винни-Пух</h2>
<div class="left">Картинка</div>
<p>Текст</p>

*!*
<div style="clear:both"></div>
*/!*

<h2>Сова</h2>
<div class="left">Картинка</div>
<p>Текст</p>

Результат получившегося кода:

[iframe src="winnie-clear-3" border="1" height=600 link edit]

  • Свойство clear гарантировало, что <div style="clear:both"> будет под картинкой с float.
  • Заголовок <h2>Сова</h2> идёт после этого <div>. Так что его отступ учитывается.

Заполнение блока-родителя

Итак, мы научились располагать другие элементы под float. Теперь рассмотрим следующую особенность.

Из-за того, что блок с float удалён из потока, родитель не выделяет под него места.

Например, выделим для информации о Винни-Пухе красивый элемент-контейнер <div class="hero">:

<div class="hero">

  <h2>Винни-Пух</h2>

  <div class="left">Картинка</div>

  <p>Текст.</p>
</div>

Стиль контейнера:

.hero {
  background: #D2B48C;
  border: 1px solid red;
}

Результат получившегося кода:

[iframe src="winnie-clear-4" border="1" height=300 link edit]

Элемент с float оказался выпавшим за границу родителя .hero.

Чтобы этого не происходило, используют одну из следующих техник.

Поставить родителю float

Элемент с float обязан расшириться, чтобы вместить вложенные float.

Поэтому, если это допустимо, то установка float контейнеру всё исправит:

.hero {
  background: #D2B48C;
  border: 1px solid red;
  *!*
  float: left;
  */!*
}

[iframe src="winnie-clearfill-float" border="1" height=300 link edit]

Разумеется, не всегда можно поставить родителю float, так что смотрим дальше.

Добавить в родителя элемент с clear

Добавим элемент div style="clear:both" в самый конец контейнера .hero.

Он с одной стороны будет "нормальным" элементом, в потоке, и контейнер будет обязан выделить под него пространство, с другой -- он знает о float и сместится вниз.

Соответственно, и контейнер вырастет в размере:

<div class="hero">

  <h2>Винни-Пух</h2>

  <div class="left">Картинка</div>

  <p>Текст.</p>

*!*
  <div style="clear:both"></div>
*/!*
</div>

Результат -- правильное отображение, как и в примере выше. Открыть код.

Единственный недостаток этого метода -- лишний HTML-элемент в разметке.

Универсальный класс clearfix

Чтобы не добавлять в HTML-код лишний элемент, можно задать его через :after.

.clearfix:after {
  content: "."; /* добавить содержимое: "." */
  display: block;  /* сделать блоком, т.к. inline не может иметь clear */
  clear: both;  /* с обеих сторон clear */
  visibility: hidden; /* сделать невидимым, зачем нам точка внизу? */
  height: 0;  /* сделать высоту 0, чтобы не занимал место */
}

Добавив этот класс к родителю, получим тот же результат, что и выше. Открыть код.

overflow:auto/hidden

Если добавить родителю overflow: hidden или overflow: auto, то всё станет хорошо.

.hero {
*!*
  overflow: auto;
*/!*
}

Этот метод работает во всех браузерах, полный код в песочнице.

Несмотря на внешнюю странность, этот способ не является "хаком". Такое поведение прописано в спецификации CSS.

Однако, установка overflow может привести к появлению полосы прокрутки, способ с псевдоэлементом :after более безопасен.

float вместо display:inline-block

При помощи float можно размещать блочные элементы в строке, похоже на display: inline-block:

[codetabs src="gallery-float" border="1" height=550 link edit]

Стиль здесь:

.gallery li {
  float: left;
  width: 130px;
  list-style: none;
}

Элементы float:left двигаются влево, а если это невозможно, то вниз, автоматически адаптируясь под ширину контейнера, получается эффект, аналогичный display: inline-block, но с особенностями float.

Вёрстка в несколько колонок

Свойство float позволяет делать несколько вертикальных колонок.

float:left + float:right

Например, для вёрстки в две колонки можно сделать два <div>. Первому указать float:left (левая колонка), а второму -- float:right (правая колонка).

Чтобы они не ссорились, каждой колонке нужно дополнительно указать ширину:

<div>Шапка</div>
<div class="column-left">Левая колонка</div>
<div class="column-right">Правая колонка</div>
<div class="footer">Низ</div>

Стили:

.column-left {
  float: left;
  width: 30%;
}

.column-right {
  float: left;
  width: 70%;
}

.footer {
  clear: both;
}

Результат (добавлены краски):

[codetabs src="two-columns" border="1" height=440]

В эту структуру легко добавить больше колонок с разной шириной. Правой колонке можно было бы указать и float:right.

float + margin

Ещё вариант -- сделать float для левой колонки, а правую оставить в потоке, но с отбивкой через margin:

.column-left {
  float: left;
  width: 30%;
}

.column-right {
  margin-left: 30%;
}

.footer {
  clear: both;
}

Результат (добавлены краски):

[codetabs src="two-columns-2" border="1" height=440]

В примере выше -- показана небольшая проблема. Колонки не растягиваются до одинаковой высоты. Конечно, это не имеет значения, если фон одинаковый, но что, если он разный?

В современных браузерах (кроме IE10-) эту же задачу лучше решает flexbox.

Для старых есть различные обходы и трюки, которые позволяют обойти проблему в ряде ситуаций, но они выходят за рамки нашего обсуждения. Если интересно -- посмотрите, например, Faux Columns.