Свойство float
в CSS занимает особенное место. До его появления расположить два блока один слева от другого можно было лишь при помощи таблиц. Но в его работе есть ряд особенностей. Поэтому его иногда не любят, но при их понимании float
станет вашим верным другом и помощником.
Далее мы рассмотрим, как работает float
, разберём решения сопутствующих проблем, а также ряд полезных рецептов.
[cut]
Синтаксис:
float: left | right | none | inherit;
При применении этого свойства происходит следующее:
- Элемент позиционируется как обычно, а затем вынимается из
документапотока и сдвигается влево (дляleft
) или вправо (дляright
) до того как коснётся либо границы родителя, либо другого элемента сfloat
. - Если пространства по горизонтали не хватает для того, чтобы вместить элемент, то он сдвигается вниз до тех пор, пока не начнёт помещаться.
- Другие непозиционированные блочные элементы без
float
ведут себя так, как будто элемента сfloat
нет, так как он убран из потока. - Строки (inline-элементы), напротив, "знают" о
float
и обтекают элемент по сторонам.
Ещё детали:
-
Элемент при наличии
float
получаетdisplay:block
.То есть, указав элементу, у которого
display:inline
свойствоfloat: left/right
, мы автоматически сделаем его блочным. В частности, для него будут работатьwidth/height
.Исключением являются некоторые редкие
display
наподобиеinline-table
иrun-in
(см. Relationships between 'display', 'position', and 'float') -
Ширина
float
-блока определяется по содержимому. ("CSS 2.1, 10.3.5"). -
Вертикальные отступы
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]
- Элемент
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
можно поставить любому элементу, не обязательно картинке. При этом элемент станет блочным.
Посмотрим, как это работает, на конкретной задаче -- сделать рамку с названием вокруг картинки с Винни.
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
.
Для этого выведем персонажей из мультфильма "Винни-Пух". Цель:
[iframe src="winnie-clear-3" height=600 border=1 link edit]
Реализуем её, шаг за шагом.
Попробуем просто добавить Сову после Винни-Пуха:
<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
, то последующий текст обтекает её справа.
Мы, конечно же, хотели бы расположить заголовок "Сова" и остальную информацию ниже Винни-Пуха.
Для решения возникшей проблемы придумано свойство 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
контейнеру всё исправит:
/*+ no-beautify */
.hero {
background: #D2B48C;
border: 1px solid red;
*!*
float: left;
*/!*
}
[iframe src="winnie-clearfill-float" border="1" height=300 link edit]
Разумеется, не всегда можно поставить родителю float
, так что смотрим дальше.
Добавим элемент div style="clear:both"
в самый конец контейнера .hero
.
Он с одной стороны будет "нормальным" элементом, в потоке, и контейнер будет обязан выделить под него пространство, с другой -- он знает о float
и сместится вниз.
Соответственно, и контейнер вырастет в размере:
<div class="hero">
<h2>Винни-Пух</h2>
<div class="left">Картинка</div>
<p>Текст.</p>
*!*
<div style="clear:both"></div>
*/!*
</div>
Результат -- правильное отображение, как и в примере выше. Открыть код.
Единственный недостаток этого метода -- лишний HTML-элемент в разметке.
Чтобы не добавлять в HTML-код лишний элемент, можно задать его через :after
.
/*+ no-beautify */
.clearfix:after {
content: "."; /* добавить содержимое: "." */
display: block; /* сделать блоком, т.к. inline не может иметь clear */
clear: both; /* с обеих сторон clear */
visibility: hidden; /* сделать невидимым, зачем нам точка внизу? */
height: 0; /* сделать высоту 0, чтобы не занимал место */
}
Добавив этот класс к родителю, получим тот же результат, что и выше. Открыть код.
overflow:auto/hidden
Если добавить родителю overflow: hidden
или overflow: auto
, то всё станет хорошо.
/*+ no-beautify */
.hero {
*!*
overflow: auto;
*/!*
}
Этот метод работает во всех браузерах, полный код в песочнице.
Несмотря на внешнюю странность, этот способ не является "хаком". Такое поведение прописано в спецификации CSS.
Однако, установка overflow
может привести к появлению полосы прокрутки, способ с псевдоэлементом :after
более безопасен.
При помощи 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
позволяет делать несколько вертикальных колонок.
Например, для вёрстки в две колонки можно сделать два <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
:
.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.