en.javascript.info/8-css-for-js/4-float/article.md
2015-03-12 10:26:02 +03:00

20 KiB
Raw Blame History

Свойство "float"

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

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

[cut]

Как работает 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"](http://www.w3.org/TR/CSS2/visudet.html#float-width)).
  3. Вертикальные отступы `margin` элементов с `float` не сливаются с отступами соседей, в отличие от обычных блочных элементов.

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

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

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

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

Её HTML-код ([edit src="winnie"]открыть[/edit]) выглядит примерно так:

<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]

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

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

  3. Строки, в отличие от блочных элементов, "чувствуют" `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:

/*+ no-beautify */
.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>

Результат [edit src="winnie-clear-1"]такого кода[/edit] будет странным, но предсказуемым:

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

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

  • **Заголовок `

    Сова

    ` не заметил `float`** (он же блочный элемент) и расположился сразу после предыдущего параграфа `

    ..Текст о Винни..

    `.
  • После него идёт `float`-элемент -- картинка "Сова". Он был сдвинут влево. Согласно [алгоритму](#float-algorithm), он двигается до левой границы или до касания с другим `float`-элементом, что и произошло (картинка "Винни-Пух").
  • Так как у совы `float:left`, то **последующий текст обтекает её справа**.

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

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

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

Синтаксис:

clear: left | right | both;

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

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

h2 {
  clear: left;
}

Результат [edit src="winnie-clear-2"]получившегося кода[/edit] будет ближе к цели, но всё еще не идеален:

Элементы теперь в нужном порядке. Но куда пропал отступ 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>

Результат [edit src="winnie-clear-3"]получившегося кода[/edit]:

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

  • Свойство `clear` гарантировало, что `
    ` будет под картинкой с `float`.
  • Заголовок `

    Сова

    ` идёт после этого `
    `. Так что его отступ учитывается.

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

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

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

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

<div class="hero">

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

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

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

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

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

Результат [edit src="winnie-clear-4"]получившегося кода[/edit]:

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

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

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

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

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

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

/*+ no-beautify */
.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>

Результат -- правильное отображение, как и в примере выше. [edit src="winnie-clearfill-div"]Открыть код[/edit].

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

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

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

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

Добавив этот класс к родителю, получим тот же результат, что и выше. [edit src="winnie-clearfill-clearfix"]Открыть код[/edit].

overflow:auto/hidden

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

/*+ no-beautify */
.hero {
*!*
  overflow: auto;
*/!*
}

Этот метод работает во всех браузерах, [edit src="winnie-clearfill-overflow"]полный код в песочнице[/edit].

Несмотря на внешнюю странность, этот способ не является "хаком". Такое поведение прописано в спецификации 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.