439 lines
20 KiB
Markdown
439 lines
20 KiB
Markdown
# Свойство "float"
|
||
|
||
Свойство `float` в CSS занимает особенное место. До его появления расположить два блока один слева от другого можно было лишь при помощи таблиц. Но в его работе есть ряд особенностей. Поэтому его иногда не любят, но при их понимании `float` станет вашим верным другом и помощником.
|
||
|
||
Далее мы рассмотрим, как работает `float`, разберём решения сопутствующих проблем, а также ряд полезных рецептов.
|
||
|
||
[cut]
|
||
|
||
## Как работает float [#float-algorithm]
|
||
|
||
Синтаксис:
|
||
|
||
```css
|
||
float: left | right | none | inherit;
|
||
```
|
||
|
||
При применении этого свойства происходит следующее:
|
||
|
||
<ol>
|
||
<li>Элемент позиционируется как обычно, а затем *вынимается из <strike>документа</strike> потока* и сдвигается влево (для `left`) или вправо (для `right`) до того как коснётся либо границы родителя, либо другого элемента с `float`.</li>
|
||
<li>Если пространства по горизонтали не хватает для того, чтобы вместить элемент, то он сдвигается вниз до тех пор, пока не начнёт помещаться.</li>
|
||
<li>Другие непозиционированные блочные элементы без `float` ведут себя так, как будто элемента с `float` нет, так как он убран из потока.</li>
|
||
<li>Строки (inline-элементы), напротив, "знают" о `float` и обтекают элемент по сторонам.</li>
|
||
</ol>
|
||
|
||
Ещё детали:
|
||
<ol>
|
||
<li>Элемент при наличии `float` получает `display:block`.
|
||
|
||
То есть, указав элементу, у которого `display:inline` свойство `float: left/right`, мы автоматически сделаем его блочным. В частности, для него будут работать `width/height`.
|
||
|
||
Исключением являются некоторые редкие `display` наподобие `inline-table` и `run-in` (см. [Relationships between 'display', 'position', and 'float'](http://www.w3.org/TR/CSS2/visuren.html#dis-pos-flo))</li>
|
||
<li>Ширина `float`-блока определяется по содержимому. (["CSS 2.1, 10.3.5"](http://www.w3.org/TR/CSS2/visudet.html#float-width)).</li>
|
||
<li>Вертикальные отступы `margin` элементов с `float` не сливаются с отступами соседей, в отличие от обычных блочных элементов.</li>
|
||
</ol>
|
||
|
||
Это пока только теория. Далее мы рассмотрим происходящее на примере.
|
||
|
||
### Текст с картинками
|
||
|
||
Одно из первых применений `float`, для которого это свойство когда-то было придумано -- это вёрстка текста с картинками, отжатыми влево или вправо.
|
||
|
||
Например, вот страница о Винни-Пухе с картинками, которым поставлено свойство `float`:
|
||
|
||
<img src="text.png">
|
||
|
||
Её HTML-код ([edit src="winnie"]открыть[/edit]) выглядит примерно так:
|
||
|
||
```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-algorithm), указанному выше.
|
||
|
||
Посмотрим, например, как выглядело бы начало текста без float:
|
||
|
||
[iframe src="winnie-nofloat" height=300 border=1 link edit]
|
||
|
||
|
||
<ol>
|
||
<li>Элемент `IMG` вынимается из <strike>документа</strike> потока. Иначе говоря, последующие блоки начинают вести себя так, как будто его нет, и заполняют освободившееся место (изображение для наглядности полупрозрачно):
|
||
|
||
[iframe src="winnie-nofloat-1" height=250 border=1 link edit]
|
||
|
||
</li>
|
||
<li>Элемент `IMG` сдвигается максимально вправо(при `float:right`):
|
||
|
||
[iframe src="winnie-nofloat-2" height=250 border=1 link edit]
|
||
|
||
</li>
|
||
<li>Строки, в отличие от блочных элементов, "чувствуют" `float` и уступают ему место, обтекая картинку слева:
|
||
|
||
[iframe src="winnie-nofloat-3" height=250 border=1 link edit]
|
||
</li>
|
||
</ol>
|
||
|
||
При `float:left` -- всё то же самое, только `IMG` смещается влево (или не смещается, если он и так у левого края), а строки -- обтекают справа
|
||
|
||
|
||
**Строки и инлайн-элементы смещаются, чтобы уступить место `float`. Обычные блоки -- ведут себя так, как будто элемента нет.**
|
||
|
||
Чтобы это увидеть, добавим параграфам фон и рамку, а также сделаем изображение немного прозрачным:
|
||
|
||
[iframe src="winnie-block-bg" height=300 border=1 link edit]
|
||
|
||
Как видно из рисунка, параграфы проходят "за" `float`. При этом строки в них о `float'ах` знают и обтекают их, поэтому соответствующая часть параграфа пуста.
|
||
|
||
|
||
### Блок с float
|
||
|
||
Свойство `float` можно поставить любому элементу, не обязательно картинке. При этом элемент станет блочным.
|
||
|
||
Посмотрим, как это работает, на конкретной задаче -- сделать рамку с названием вокруг картинки с Винни.
|
||
|
||
HTML будет такой:
|
||
|
||
```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`:
|
||
|
||
```css
|
||
/*+ 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. Добавляем информацию
|
||
|
||
Попробуем просто добавить Сову после Винни-Пуха:
|
||
|
||
```html
|
||
<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]
|
||
|
||
Произошло следующее:
|
||
<ul>
|
||
<li>**Заголовок `<h2>Сова</h2>` не заметил `float`** (он же блочный элемент) и расположился сразу после предыдущего параграфа `<p>..Текст о Винни..</p>`.</li>
|
||
<li>После него идёт `float`-элемент -- картинка "Сова". Он был сдвинут влево. Согласно [алгоритму](#float-algorithm), он двигается до левой границы или до касания с другим `float`-элементом, что и произошло (картинка "Винни-Пух").</li>
|
||
<li>Так как у совы `float:left`, то **последующий текст обтекает её справа**.</li>
|
||
</ul>
|
||
|
||
### Шаг 2. Свойство clear
|
||
|
||
Мы, конечно же, хотели бы расположить заголовок "Сова" и остальную информацию ниже Винни-Пуха.
|
||
|
||
Для решения возникшей проблемы придумано свойство `clear`.
|
||
|
||
Синтаксис:
|
||
|
||
```css
|
||
clear: left | right | both;
|
||
```
|
||
|
||
Применение этого свойства сдвигает элемент вниз до тех пор, пока не закончатся `float'ы` слева/справа/с обеих сторон.
|
||
|
||
Применим его к заголовку `H2`:
|
||
|
||
```css
|
||
h2 {
|
||
clear: left;
|
||
}
|
||
```
|
||
|
||
Результат [edit src="winnie-clear-2"]получившегося кода[/edit] будет ближе к цели, но всё еще не идеален:
|
||
|
||
<img src="float-small-margin.png">
|
||
|
||
Элементы теперь в нужном порядке. Но куда пропал отступ `margin-top` у заголовка "Сова"?
|
||
|
||
Теперь заголовок "Сова" прилегает снизу почти вплотную к картинке, с учётом её `margin-bottom`, но без своего большого отступа `margin-top`.
|
||
|
||
Таково поведение свойства `clear`. Оно сдвинуло элемент `h2` вниз ровно настолько, чтобы элементов `float` не было *сбоку от его верхней границы*.
|
||
|
||
Если посмотреть на элемент заголовка внимательно в инструментах разработчика, то можно заметить отступ `margin-top` у заголовка по-прежнему есть, но он располагается "за" элементом `float` и не учитывается при работе в `clear`.
|
||
|
||
Чтобы исправить ситуацию, можно добавить перед заголовком пустой промежуточный элемент без отступов, с единственным свойством `clear:both`. Тогда уже под ним отступ заголовка будет работать нормально:
|
||
|
||
```html
|
||
<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]
|
||
|
||
<ul>
|
||
<li>Свойство `clear` гарантировало, что `<div style="clear:both">` будет под картинкой с `float`.</li>
|
||
<li>Заголовок `<h2>Сова</h2>` идёт после этого `<div>`. Так что его отступ учитывается.</li>
|
||
</ul>
|
||
|
||
## Заполнение блока-родителя
|
||
|
||
Итак, мы научились располагать другие элементы *под* `float`. Теперь рассмотрим следующую особенность.
|
||
|
||
**Из-за того, что блок с `float` удалён из потока, родитель не выделяет под него места.**
|
||
|
||
Например, выделим для информации о Винни-Пухе красивый элемент-контейнер `<div class="hero">`:
|
||
|
||
```html
|
||
<div class="hero">
|
||
|
||
<h2>Винни-Пух</h2>
|
||
|
||
<div class="left">Картинка</div>
|
||
|
||
<p>Текст.</p>
|
||
</div>
|
||
```
|
||
|
||
Стиль контейнера:
|
||
|
||
```css
|
||
.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` контейнеру всё исправит:
|
||
|
||
```css
|
||
/*+ 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` и сместится вниз.
|
||
|
||
Соответственно, и контейнер вырастет в размере:
|
||
|
||
```html
|
||
<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`.
|
||
|
||
```css
|
||
/*+ 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`, то всё станет хорошо.
|
||
|
||
```css
|
||
/*+ 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]
|
||
|
||
Стиль здесь:
|
||
|
||
```css
|
||
.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` (правая колонка).
|
||
|
||
Чтобы они не ссорились, каждой колонке нужно дополнительно указать ширину:
|
||
|
||
```html
|
||
<div>Шапка</div>
|
||
<div class="column-left">Левая колонка</div>
|
||
<div class="column-right">Правая колонка</div>
|
||
<div class="footer">Низ</div>
|
||
```
|
||
|
||
Стили:
|
||
|
||
```css
|
||
.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`:
|
||
|
||
```css
|
||
.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](http://goodline.spb.ru/III-05-002.html).
|