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

439 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Свойство "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).