.clearfix — который так полюбился большинству верстальщиков, его использование мотивируется простотой и отсутствием других способов работы с float блоками. Хочу представить вашему вниманию другой способ — не менее действенный и не более сложный. Хотя по привычке многие скажут, что .clearfix влепил — и все работает, но его использование не всегда оправдано, а иногда и невозможно, но каким-то образом нужно налаживать сопряжение блоков, что иногда вызывает трудности и увеличивает продолжительность верстки, а чаще всего отладки или добавления новых элементов в окружение.


Для начала рассмотрим, что же происходит с элементами и родительским элементом когда к ним добавляется свойство float: right и left. Рассмотрим небольшой пример:


<!-- Пример 1 - базовый -->
<style>
ul {
  padding: 20px;
  margin: 20px;
}
li {
  float: left;
  list-style: none;
  margin-left: 5px;
  border: 1px solid red;
}
</style>
<ul>
  <li>первый</li>
  <li>второй</li>
  <li>третий</li>
</ul>
<p>Какой-то текст</p>

В примере LI имеют свойство float: left; и, как и следовало ожидать, выстраиваются в ряд один за другим и "Какой-то текст" прицепляется следом. Что же, это классическое поведение float. Но давайте посмотрим на родителя, ведь у нас еще есть UL: куда же делся он? Ему мы float не ставили, и по умолчанию это элемент блочный, почему же после него нет переноса?


А случилось следующее: для отображения float-элементов на странице браузер как бы "вырывает их из общего потока", помещает в нужное место, благодаря чему получается обтекание текстом и другими элементами. Именно для отключения обтекания было введено свойство clear, чтобы отключить его с нужной стороны выбранного блока.


Что же произойдет, если для родительского блока поставить clear: both;? Ровным счетом ничего, ведь, как я уже заметил ранее, float родителю мы не ставили и соответственно его ничто не обтекает, его как бы совсем нет, его содержимое было "вырвано из потока".


Давайте рассмотрим как с этим справится .clearfix:


<!-- Пример 2 - с применением .clearfix -->
<style>
.clearfix:after {
  content: "";
  display: table;
  clear: both;
}
ul {
  padding: 20px;
  margin: 20px;
}
li {
  float: left;
  list-style: none;
  margin-left: 5px;
  border: 1px solid red;
}
</style>
<ul class="clearfix">
  <li>первый</li>
  <li>второй</li>
  <li>третий</li>
</ul>
<p>Какой-то текст</p>

С задачей .clearfix справился. Как же он это делает? Он добавляет после выбранного элемента псевдоэлемент, который огибает все float элементы, расположенные ниже. Ниже именно потому, что родительский элемент в потоке пустой, его почти нет, и поэтому :after помещает элемент физически в самое начало, и браузеру приходится просчитывать, когда же все элементы закончат флоатиться, чтобы остановиться.


А вот вариант без использования .clearfix:


<!-- Пример 3 - без применения .clearfix -->
<style>
ul {
  padding: 20px;
  margin: 20px;
  display: inline-block;
}
li {
  float: left;
  list-style: none;
  margin-left: 5px;
  border: 1px solid red;
}
</style>
<ul class="clearfix">
  <li>первый</li>
  <li>второй</li>
  <li>третий</li>
</ul>
<p>Какой-то текст</p>

Свойство display: inline-block; для родительского элемента. Результат такой же, отличие лишь в том, что .clearfix оставляет родительский элемент блочным, либо таким, каким он был определен ранее. Как этим воспользоваться — решайте сами. И для классических решений эти способы практически равноценны.


Но если сам родительский элемент (и элементы внутри него) флоатится, а относительно него нужно расположить другие блоки, может возникнуть ситуация, когда .clearfix использовать нельзя. Родительский элемент будет занимать 0 размер (содержимое и он сам "вырваны из потока"). И чтобы спозиционировать в его окружении другие элементы, нужно будет либо прописывать фиксированную высоту (а она может быть непостоянной), либо "играться" с position: absolute; (что тоже не всегда можно предусмотреть непостоянством контента). В ряде случаев, добавляя в такие "узлы" на сайте дополнительный контент, может понадобиться пересмотреть всю концепцию верстки и переверстывать весь "узел".


Использование свойства display облегчит диагностику верстки средствами разработки (firebug или другими встроенными в браузеры средствами инспектирования), именно тогда, когда .clearfix применить не получается. Ведь если не сделать эту операцию, то родительский элемент найти инспектором не получится (если у него нет отступов), и под его содержимое не будет отведено место при рендэринге страницы (это хорошо заметно в примере 1 при инспектировании). display: inline-block; как раз и поможет избежать этих проблем.


Варианты верстки доступны по ссылке тут


Давайте еще рассмотрим вот такой вариант.
У clearfix — в родителя div.box не вошел внешний отступ(margin), который прописан для UL, а inline-block все воспринял как нужно все отступы на месте.


А для того, чтобы ширина inline-block стала как у block добавляем width: calc(100% — 80px) — не забываем что отступы внешние и внутренние нужно исключить из ширины.


Итого: в вашем распоряжении — более одного способа решения данной задачи. Хорошо, когда есть выбор, пользуйтесь на здоровье!

Поделиться с друзьями
-->

Комментарии (33)


  1. vintage
    10.08.2016 00:22
    +3

    А чем display:flex не угодил?


    1. FluffyMan
      10.08.2016 00:33

      Судя по этой таблице flex криво поддерживает ie11.


      1. symbix
        10.08.2016 02:52
        +5

        На практике, единственная существенная проблема с min-height. Она отлично решается кормлением IE height вместо min-height CSS-хаком: в IE11 для флексбоксов height ведет себя как min-height. Прямо как в старые добрые времена с IE6, да-да.


        1. sashabeep
          10.08.2016 09:57

          Пресвятой Tridient… Я в шоке :)


          1. symbix
            10.08.2016 17:58

            У меня вообще есть подозрение, что в IE7 min-height починили хаком, а когда добавляли флексбоксы, забыли добавить еще одно условие в хак :)


        1. Aingis
          10.08.2016 14:25
          +1

          Увы, нет. В IE11 (и только в нём, в IE10 или Edge и Edge-как-IE11 нормально) есть адский баг, который проявляется при сочетании нескольких факторов: элемент с ограниченным размером (напр. max-width) больше чем flex-basis (то есть при flex-grow), так что есть свободное пространство для распределения, и выравнивание, отличающееся от flex-start (в том числе при разных вариантах margin: auto). В этом случае свободное место неправильно рассчитывается и элементы сдвигаются дальше чем должны.

          Баг обойти нельзя, поэтому во flexbugs его не добавили (не спрашивайте меня в чём логика). Можно использовать flex-shrink вместо flex-grow, но это вынуждает задавать минимальный размер вроде min-width, и не даёт воспользоваться плюшками flex-basis с автоматическим минимальным размером в зависимости от содержимого.


          1. symbix
            10.08.2016 17:50
            +1

            А, такое было, я толком и не понял, в чем дело, просто обошел, переверстав чуть иначе.
            Спасибо, после вашего комментария стало понятно.


      1. Shannon
        10.08.2016 10:34

        Не так уж и плохо, у ie10-11 есть некоторые «кривости» при поддержке flexbox, но они известны и собраны в одном месте рядом с простыми решениями

        Список проблем с ie10-11 (и других браузеров) и их решения:
        https://github.com/philipwalton/flexbugs#flexbugs

        Плагин для postcss который сам исправит известные проблемы:
        https://github.com/luisrudge/postcss-flexbugs-fixes
        https://github.com/archana-s/postcss-flexbox


    1. wanick
      10.08.2016 00:44
      -2

      Согласен display:flex подходит, но по сравнению с inline-blockflex относительно новый элемент. По старой школе inline-block — привычнее, он работал еще в IE 5.5 и с тех пор его поведение не менялось, кросс-браузерность гарантирована… А по flex не все так однозначно, тут описания по поддержке, хоть спустя 5 лет после выхода гибрида можно уже и забыть о том, что кто-то там может не поддерживать — но тут чисто старая школа. :)


  1. ARad
    10.08.2016 07:49
    +1

    inline-block все таки меняет верстру, для этого попробуйте закрасить ul желтым цветом. Также иногда полезно вставлять в нужное место пустой div с классом clearfix. Пример здесь https://jsfiddle.net/qzqfadnb/6/.


  1. StrikeBack
    10.08.2016 09:20
    +2

    display: inline-block не всегда хорош для решения этой задачи
    https://jsfiddle.net/cs57yh3z/ по ссылке в примере у первого списка установлен display:inline-block, да его высота становится равна содержимому, но и ширина стала не 100%, так же если блоку установлен display: inline-block, то у этого блока появляется небольшой отступ в низу, который может подпортить верстку.

    clearfix же решает эту задачу отлично.


    1. wanick
      10.08.2016 09:25
      -1

      Читайте внимательнее в статье и написано что .clearfix решает эту задачу и при этом оставляет свойство display без изменений а inline-block, меняет его.


  1. Sulin
    10.08.2016 09:48
    -1

    Если используется display: inline-block; то нужно давать еще два свойства, width: 100%; и vertical-align: top;

    Еще одной альтернативой есть метода overflow: hidden; у родителя, но стоит понимать что этот метод тоже не всегда подходит по понятным причинам.


    1. sashabeep
      10.08.2016 10:15

      Только если после этого элемента будет перенос строки то со 100% width могут быть некоторые заморочки


  1. rusavv
    10.08.2016 10:11

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

    Небольшая поправочка, если позволите — добавляет не после, а внутри элемента с присвоенным классом.


    1. sumanai
      11.08.2016 00:39

      Внутри в конец, если быть совсем точным.


  1. SelenIT3
    10.08.2016 10:41

    Фактически все решения проблемы «containing floats» делятся на 2 класса — собственно clearfix-ы (добавление (псевдо)блока с clear:both в конец родителя) и разные способы создания блочного контекста форматирования. Есть старенькая, но, видимо, местами еще актуальная статья с подробным разбором плюсов, минусов и ограничений каждого варианта (и попыткой найти новый вариант с минимумом ограничений:).

    В теории, стандартным решением второго класса без побочных эффектов должно стать display: block flow-root; из CSS Display 3. На практике, боюсь, это пока нигде не работает:(. Но и сама необходимость всё реже благодаря тем же флексбоксам.


  1. dpr
    10.08.2016 10:41

    Вся суть сводится к тому, что необходимо создать новый поток элементов (контекст форматирования), в котором будут находится плавающие блоки.
    Достичь этого можно разными способами. В частности — изменением свойства display на inline-block, table, inline-table, table-cell, table-caption, flex, inline-flex (гриды, вероятно, тоже будут создавать новые контексты, сам не пробовал); изменением overflow на auto, scroll, hidden; изменением float на left или right; изменением position на absolute или fixed.


    1. SelenIT3
      10.08.2016 11:09
      +1

      С display: *-flex и display: *-grid есть нюанс, что они создают не блочный контекст форматирования, а свой собственный, с другими правилами размещения блоков (в частности, переопределяют поведение самих плавающих блоков). Решается дополнительной оберткой, но это уже далеко не так изящно… Строго говоря, display: *-table тоже создает свой особый контекст (табличный), но в этом табличном контексте внутри *-table автоматически достраиваются анонимные «обертки» с table-row/table-cell, и явная обертка не нужна.


      1. dpr
        10.08.2016 11:39

        Всё верно. Собственно, каждый из этих способов (включая клирфикс) имеет свои нюансы, как положительные, так и отрицательные для решения конкретной задачи, о которых следует знать и применять по обстоятельствам. Одного солюшена для всех ситуаций, увы, пока нет. Хотя где-то в будущем уже маячат гриды, которые могут стать таким универсальным средством.


        1. SelenIT3
          10.08.2016 11:48

          Именно, я просто хотел уточнить, что не во всех контекстах форматирования плавающие блоки останутся плавающими:). А насчет плюсов и минусов — в статье, на которую я сослался чуть выше (одновременно с вашим комментом), есть неплохой наглядный пример разницы в действии:)


  1. vtrushin
    10.08.2016 10:58
    +1

    Когда уже даже намек на клирфикс пропадет (


  1. wanick
    10.08.2016 11:03

    Давайте еще рассмотрим вот такой вариант.
    https://jsfiddle.net/wanick/taojv46v/5/

    специально добавил цвета чтобы видеть как clearfix и inline-block решают эту задачу,
    У clearfix — родителя div.box не вошел внешний отступ(margin) который прописан для UL а inline-block все воспринял как нужно все отступы на месте.

    Также добавил пример как для inline-block сделать width: calc(100% — 80px) — не забываем что отступы внешние и внутренние нужно исключить из ширины.


    1. Dil0ng
      10.08.2016 12:00

      вроде как calc() плохо поддерживается во многих браузерах, в частности на телефонах. (Opera mini и UC Browser)


      1. wanick
        10.08.2016 12:06

        Соглашусь с вами, calc — не самый удачный пример, но это лишь демонстрационный пример, который показывает что ширину выставлять нужно «не забывая» про отступы.


    1. e_juke
      10.08.2016 16:03

      В данном случае в втором примере (с .clearfix) поведение вполне корректное.
      Не найдя внутри контейнера box каких-либо элементов, границ или padding'ов сверху и снизу, браузер рассчитал margin относительно ближайших элементов с границей: заголовка и строки «Какой-то текст» внизу. Таким образом отступы внутри box и не должны были появиться.
      Если добавить, например, свойство border: 1px solid transparent для div.box, то отступы также будут видны, как и в последнем случае.

      В случае же inline-block отступы всегда рассчитываются относительно родительского элемента, поэтому внутрь родителя и вошли внешние отступы ul.


  1. fsanano
    10.08.2016 16:03

    Есть третий способ. Родительскому элементу прописать:

    . clearfix { overflow: hidden; }


  1. Dreyk
    10.08.2016 16:53
    +2

    Я ненавижу inline-block за его нетерпимость к пробелам между тегами. 1 пробел — и вся верстка летит к чертям. и приходится писать из-за этого отвратительный html типа


    <ul>
      <li>asdasd</li><li>
           asdasd</li>
    </ul><ul>
    </ul>


    1. mozyr
      10.08.2016 17:23
      -1

      Достаточно просто указать для обертки font-size:0 и лишние пробелы уйдут.
      А для дочерних элементов проставить размер шрифта заново.


      1. Dreyk
        10.08.2016 17:25
        +2

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


        1. SelenIT3
          10.08.2016 17:32

          В андроид-браузерах, как минимум.


    1. sumanai
      11.08.2016 00:44

      А можно писать красивый и валидный(подходит только для li)

      <ul><li>asdasd
        <li>asdasd
      </ul>
      

      Впрочем, вариантов решения этой проблемы масса.


      1. Dreyk
        11.08.2016 18:25

        вариантов масса, но, как видите, ни один не подходит на 100% поэтому и ненависть =)