CSS-live.ru

Новая альтернатива clearfix-у и overflow:hidden

Последнее обновление: 05.10.2012 

Сегодня я проснулся с каким-то странным чувством. Мне как будто казалось, что что-то должно произойти. Я вошёл в скайп и не ошибся! Меня уже поджидал Илья Стрельцын (@SelenIT2) с неожиданным решением одной известной задачи, связанной с очисткой потока.

Я полагаю, многие верстальщики уже догадались — о чём идёт речь, а если нет, то перед тем, как переходить к решению Ильи, давайте немного освежим память и вкратце рассмотрим несколько известных методов, а так же их достоинства и недостатки.

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

Условие задачи

Есть две колонки. Левая с float:left имеет фиксированные ширину и высоту. Нужно, чтобы правая тянулась на всю оставшуюся ширину. При этом необходимо, чтобы из той же правой колонки не «вываливались» margin-ы элементов, а элементы с float: left или right не схлопывали высоту контейнера, и чтобы края колонки не обрезали элементы, которые из неё вылезают.

В общем, в результате должно получиться что-то нечто похожее на следующий пример:

Решения

Надо заметить, что на данный день все существующие полурешения этой задачи имеют определённые недостатки, которые порой доставляют массу неудобств и заставляют отказываться от этих способов или подбирать самый удобный в зависимости от конкретной ситуации.

Перед тем, как переходить к первому варианту, предлагаю сразу посмотреть структуру HTML и часть CSS, которые во всех примерах будут абсолютно одинаковыми. Единственное что будет меняться в HTML — это классы у правой колонки (в зависимости от способа), а в CSS только лишь та часть, которая относится к методу.

<div class="left">
	<h2>Левый float</h2>
</div>
<div class="clearfix">
	<div class="date">12.05.2012</div>
	<h2>Заголовок</h2>
	<p>Ниже идёт картинка с float:left</p>
	<img src="img.jpg" class="float-left">
	<p class="clear-both">А у этого абзаца стоит clear:both;</p>
	<div class="absolute">Absolute</div>
</div>
/* Общие стили */
.left {
	float: left;
	width: 200px;
	background: #FC6;
	height: 200px;
	position: relative;
	z-index: 1;
}

h2 { margin-top: 10px;}
.float-left { float: left;}
.clear-both {
	font-size: 15px;
	clear: both;
	background: #F99;
}
.date {
	font-size: 20px;
	float: right;
	background: #FC0;
	padding: 2px 5px;
	margin: -10px -5px 0 0;
}
.absolute {
	position: absolute;
	bottom: -25px;
	right: 10px;
	width: 50px;
	height: 50px;
	background: white;
	border: 2px solid black;
}

clearfix

Для первого эксперимента возьмём один из самых популярных clearfix-ов, который в своё время доработал Chris Coyier, отказавшись от ненужного свойства font-size, в связи с пустым свойством content. Выглядит он так:

.clearfix:after {
	visibility: hidden;
	display: block;
	content: "";
	clear: both;
	height: 0;
	}
* html .clearfix             { zoom: 1; } /* IE6 */
*:first-child+html .clearfix { zoom: 1; } /* IE7 */

Я применил этот код к правой колонке, добавив ещё несколько свойств чисто для оформления. И вот что у нас получилось:

Как видно по рисунку, из плюсов можно выделить только то, что благодаря clearfix-у правый нижний блок с position: absolute остался целым и невредимым и его вылезающая из колонки часть не обрезалась.

Ну, а в остальном, к сожалению, одни минусы. Их настолько много, что я даже сделал список:

  • Верхние margin-ы у заголовка выпали из контейнера.
  • Левый padding в контейнере провалился под левую колонку и больше не является отступом между элементами в правой колонке и левой с float: left. Оно и понятно, ведь clearfix не создаёт свой собственный контекст, поэтому блочная часть правой колонки фактически оказывается под левой, обтекая её своими инлайновыми боксами.
  • Ну и самое печальное тут то, что последний абзац с clear:both оказался под левой колонкой, растягивая собой правую по высоте. Это опять же из-за того, что левая колонка и плавающая картинка в правой колонке остались в одном и том же контексте форматирования, поэтому clear реагирует и на то, и на другое.

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

overflow: hidden или auto

Ещё один, более достойный и простой способ, на мой взгляд, заключается в том, чтобы повесить на контейнер свойство overflow со значениями hidden или auto, благодаря которым мы создадим новый контекст форматирования и тем самым сможем изолировать наш блок — колонку от внешнего контекста.

Во-первых, в самой структуре мы лишь поменяем класс у правой колонки с class="clearfix" на class="overflow", а во-вторых, изменим наш CSS в пользу текущего метода.

/* метод с overflow */
.overflow {
	padding: 0 10px;
	background: #E76D13;
	position: relative;

	/* overflow */
	overflow: hidden;
}

* html .overflow { zoom: 1; } /* IE6 */
*:first-child+html .overflow { zoom: 1; } /* IE7 */

И вот, что у нас вышло:

На вид этот способ явно лучше предыдущего, за исключением того, что вылезающие за края элементы — обрезаются. Дело в том, что overflow: hidden устроен так, что выходящие за края блока элементы — попросту обрезаются, а со значением auto вообще появляется скроллинг. Этот нюанс порой доставляет массу неудобств, заставляя отказываться от этого метода в пользу других. 

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

display:table

В эпоху быстро-уходящих на покой IE6-7 актуальными становятся свойства и значения, которые не имели поодержки в этих недобраузерах. И одна из таких «парочек» имеет название display: table и так же может являться ещё одним методом для решения нашей задачи. display: table, как и overflow: hidden вешается на контейнер, но работает немного иначе.

По традиции причешем наш код…

/* метод с display: table */
.table  {
	padding: 0 10px;
	background: #E76D13;
	position: relative;

	/* display: table */
	display: table;
}

* html .table  { zoom: 1; } /* IE6 */
*:first-child+html .table  { zoom: 1; } /* IE7 */

И посмотрим результат:

Так же, как и overflow: hidden display: table создаёт свой собственный контекст форматирования, изолирует контейнер, и мало того, в отличие от overflow: hidden, избегает проблем с обрезанием вылезающих элементов.

Но, проблема всё же есть, и она кроется в том, что элементы с этим свойством (в нашем случае — это правая колонка) ужимаются по содержимому и поэтому не растягивают контейнер на всю оставшуюся ширину. 

Так что, к сожалению, это решение нам тоже не подходит, поэтому пора переходить к самому вкусному!

Метод Ильи Стрельцына

Вот, наконец, мы и добрались до самого главного способа, который основан на предыдущем варианте с display:table, но имеет очень важное дополнение, которое и делает этот метод полностью рабочим.

Для начала сразу же приведу код, а уже после просмотра результата объясню в чём вся соль.

/* метод Ильи Стрельцына */
.table  {
	
	/* display: table */
	-height: 1%; /*IE6*/
	*zoom: 1; /*IE6-7*/
	display: table;
}
/* Очень важное дополнение! */
.table:after {
    /*content: '1 1';*/  /*Op15+*/
   content: '1 1 1 1 1 1 1 1 1 1 1'; /*Op12+*/
   font: .1px/0 a;
   display: block;
   word-spacing: 99in;
   overflow: hidden; /*IE8-*/
}

И, соответственно, результат:

На изображении можно увидеть, что все перечисленные в задаче пункты — выполнены и задача решена полностью. Правая колонка тянется на оставшуюся ширину, margin-ы элементов не вываливаются наружу, высота контейнера не схлопывается по высоте, и даже выпирающие элементы не обрезаются! В чём же подвох?

На самом деле в решении этой задачи нам помогает тот же трюк с псевдоэлементом, который помогал выравнивать блоки по резиновой сетке в этой статье. Весь приём заключается в двух свойствах и их значениях — это content: ‘. .’; и word-spacing: 99in;. Обратите внимание на две текстовые точки в свойстве content и на значение у word-spacing (99in), которое можно пересчитать в 9504px (с эстетической точки зрения 99in выглядят всё же красивее=)). Но давайте разберёмся более детально.

В общем, по спецификации, для таких вещей, как таблицы, ширина считается по алгоритму shrink-to-fit. Кратко объяснить его суть можно так. Вначале браузер «спрашивает» у контента, сколько бы ему хотелось иметь ширины, чтоб вольготно раскинуться в одну строку, не ломая строки переносами. Если ширина, «запрошенная» контентом, меньше доступной ширины контейнера — именно эта «запрошенная» ширина назначается нашему элементу. Если же нет — назначается такая ширина, чтобы элемент вплотную вписался в доступную ширину контейнера, а контенту волей-неволей приходится втискиваться в эту ширину, перенося строки.

В нашем случае текст псевдоэлемента (те самые 2 точки с пробелом между ними) «запрашивает» почти 10 тыс. пикселей — обычная ширина двух точек с пробелом плюс 9504 пикселей, на которые этот пробел увеличен благодаря word-spacing‘у. То есть в любых реальных условиях этот контент никогда не сможет уместиться в одну строку, и браузеру всегда придется считать ширину нашей таблицы по второму варианту — по контейнеру. А ведь именно этого мы и добивались!

Заключение

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

p.s. Хочется сказать отдельное спасибо Александру Егереву (aka alexriz) за решение, которое помогло исправить неприятный баг в браузере Opera, связанный с непонятным нижним отступом (см. комментарии). Добавил решение в статью. 

Последнее обновление: 05.10.2012 — Снова Александр Егерев (aka alexriz) нашёл-таки решение для бага в Opera под Mac и Linux. Добавил решение в статью. (Ссылка на демо-пример)

P.S. Это тоже может быть интересно:

79 комментариев

  1. psywalker

    Ну и самое печальное тут то, что последний абзац с clear:both оказался под левой колонкой, растягивая собой правую по высоте. Это опять же из-за того, что левая колонка и плавающая картинка в правой колонке остались в одном и том же контексте форматирования, поэтому clear реагирует и на то, и на другое.

    Это какраз то что я писал тебе по поводу различий между overflow и clear:both и лечение указывал c помощью нижнего марджина.
    остальные могут ознакомиться тут http://www.xiper.net/collect/html-and-css-tricks/css-tricks/clearfix.html в коментариях к статье я это тоже описывал.

    короче вот так это можно сделать http://jsfiddle.net/4nAyZ/12/

    по поводу этого:
    .clearfix:after {
    clear: both;
    content: «.»;
    display: block;
    height: 0;
    visibility: hidden;
    }
    Зачем этот лишний код оно давно уже не нужно, вот так проще
    .clearfix:after {
    content: «»;
    display: block;
    clear: both;
    }

      1. А если ниже будет еще один блок (скажем, подвал), и левая колонка будет выше правой?

          1. Вообще по поводу колончной разметки где много вещей интересных решается и ещё некоторых моментов я скинул свои разарботки psywalker’у, сейчас уточню будет ли он статью по ним писать…

      2. С отрицательным маргином остроумно придумано, я даже не сразу вник, в чем именно фокус. Спасибо за идею!

        Но тут цельность правой колонки держится на ее левом margin-е, равном ширине левой колонки. В ситуации, для которой мне понадобился метод из статьи, ширина левой колонки у меня была непредсказуемой (нужно было предусмотреть ситуацию, когда она вообще исчезает и правую надо растянуть на весь контейнер), поэтому вариант с margin-ом мне не подошел в принципе. Ну и ситуация, о которой чуть ниже пишет troll, в моей задаче тоже была вполне вероятна. Поэтому пришлось искать вариант, при котором всё определялось бы фактической доступной шириной — как при overflow, но без его ограничений. И такой вариант нашелся :)

    1. В статье для примера clearfix-а взят вариант отсюда, точки в контенте там уже нет, а visibility и height, видимо, остались «по традиции». Сейчас набирает популярность т.н. «micro clearfix» — c display:table вместо block для псевдика, чтобы заодно и маргины из блока не вываливались.

      Но лично мне с непустым контентом (чтобы и FF3.x поняли) как-то спокойнее :). А в методе из статьи в контенте псевдика вся «соль». Хотя в старых FF и у него может быть проблема (позиционирование из элемента с display:table), но что делать, вёрстка пока не может без компромиссов.

        1. А с чем связанно то что через нулевую таблицу поля не проходят? а то у меня с шиллингом печаль ка. Типа то что внутри табличных элементов (tr, td…) полей не может быть, соответственно они и не могут вылазить?

          1. В спецификации сказано, что поля блока могут проходить его насквозь (т.е. верхнее и нижнее поля самого элемента схлопываются в одно и могут схлопываться с полями соседей или родителя), если у этого блока нет не вырванных из потока детей, его итоговая высота равна нулю и он не создает нового контекста форматирования. У таблицы (даже пустой и нулевой) не выполняется третье условие — контекст форматирования она создает. И это разделяет любые поля сверху и снизу от таблицы, аналогично как если бы у таблицы был бордер.

  2. Что-то в opera 11.64 дикий перекос с этим клирфиксом вышел….
    Все растянулось вширь до 8191px

      1. В опере и хроме, в некоторых ситуациях и не такие большие (в FF не обратил внимания), появляются отступы снизу, под элементом с клирфиксом. Я пытался их убрать с помощью font-size:0; и line-height:0; в ПЭ. Везде прокатило, а оперу перекосило… Вот такая вот неприятная бяка.

          1. Вот пример, что у меня вызвало проблему http://alexriz.kodingen.com/clearfix_by_SelenIT/

            Там кнопочку по нажимайте, с начала стандартный режим, а потом, то что вышло у меня.
            Правда отступ в хроме воспроизвести тоже не удалось. Хотя если в блоке который оборачивает кнопку, убрать padding, то будет не большой отступ от блока сверху до кнопки в стандартном режиме.

            1. пардон у кнопки margin по-умолчанию есть не большой в хроме) Значит не получилось воспроизвести

        1. Спасибо за багрепорт! Отступ в Опере действительно возникает. Лечится заменой display:table на table-cell для самой колонки (в др. браузерах, вроде, хуже не становится, разве что margin-ы для колонки применить уже не получится), визуально устраняется также при font-size: 1px для ПЭ. В Хроме (19-м) воспроизвести проблему не удалось (кстати, clear для ПЭ не нужен, его единственная задача — «распереть» контейнер).

          Ох уж эта шалунья Опера, вечно засовывающая псевдоэлементы при display: table не туда (у нее они всегда чем-то вроде table-row оказываются)… Обидно, конечно. Но это поведение псевдоэлементов — явный оперный баг (а уж поведение при нулевом font-size — и подавно). Будем требовать фикса!

          1. Ну, вот стоило мне написать о проблеме и сделать тестовый пример проблемы, у меня получилось решить проблему в лоб, при чем тем же методом, что я и делал.

            http://alexriz.kodingen.com/clearfix_by_SelenIT_part2/

            Если переключиться на мою редакцию теперь не рассыпается и отступа нет. В основе все тот же клирфикс, что и был.

              1. Макс, добавь в статью пример решений для Оперы от Ильи и Сани, а то не все будут читать по 20 комментариев под статьей.

  3. После добавления первого clearfix'а (через псевдоэлемент .clearfix:after)  у меня ничего не изменилось. Даже скопировал и вставил Ваш код, но никакого эффекта. Если смотреть по рисунку, то похоже что у класса .clearfix установлено свойсто position: relative, так как блок с классом .absolute позиционириуется относительного его (блока .clearfix).
    http://jsfiddle.net/FGLM3/

    1. Position: relative там действительно есть, только задан (в демке) не для .clearfix, а для .center.

    1. C "контекстами форматирования" в спецификации CSS вообще путаница — у них… нет явного определения! Только описание, в каких случаях контекст возникает и как в нем ведут себя боксы. Только в этом году в W3C задумались над определением этих вещей.

      Иван Сагалаев по ссылке пишет, что под "стаканом" подразумевает "содержащий блок", который может задавать новый блочный контекст при опред. условиях (см. п. 9.4.1 спецификации). Но по его описанию, да, это скорее сам контекст и есть. Примерно так же, как "нечто, откуда не вываливаются margin-ы и куда не проникают float-ы", определить блочный контекст предлагает Алекс Могилевский (представитель Микрософта в W3C).

  4. Ой, ошибочка *стакан.
    Или "стакан" пожходит только для позиционируемых элементов?

  5. Спасибо за Ваш здоровский clearfix. Раньше пользовался micro clearfix и частенько возникали проблемы при его использовании, например, в колонках.
    С Вашим пока проблем нет, уже сверстал несколько сайтов — все ок от ie7.
    Но сегодня столкнулся с проблемой при верстке мобильной версии сайта, везде все нормально отображается, а в Opera Mobile по ширине в те самые, наверное, 10 000px выходила. А загвоздка оказалась как раз в хаке для Оперы — для мобильной оперы она лишняя. На всякий случай, информирую :)

  6. Доброй ночи.
    Отличное решение, однако выявилась еще одна маленькая проблема.
    Если внутри плавающего блока есть длинная, непереносимая строка (или элемент с заданным свойством white-space: nowrap), то контейнер растягивается на ширину этой строки, часто за пределы экрана. Что, в общем-то и присуще таблице. Здесь я смоделировал подобную ситуацию: http://jsfiddle.net/DelphinPRO/8ppxR/
    Решение проблемы вижу в задании контейнеру ширины 100% и свойства table-layout:fixed. На скорую руку посмотрел в ff, opera и chrome — помогло.

    1. Странно, что в статье не указано об этом ньюансе. На мой взгляд, чрезвычайно значительном — т.к. вёрстка может просто разваливаться, если внутри блока с display:table находится слишком широкий «монолитный» контент (непереносимая строка или другое). Как уже было сказано — стандартное поведение таблицы — это показать этот контент при любом раскладе, в нашем случае это выходит боком. И решения для этого я пока не вижу и не уверен, что оно вообще есть.

      1. Спасибо за справедливое замечание. Вообще, на мой взгляд, не влезающий в колонку контент — ситуация, нештатная сама по себе, и в любом случае придется выбирать какой-то компромисс (либо возможность просмотреть весь «негабаритный» контент ценой разрушения красоты дизайна, либо частичная недоступность самого контента). Возможно, для какого-то информационного сайта меньшим злом будет как раз широкая таблица с горизонтальным скроллингом…

        Но вообще хорошо, что сейчас появляются новые альтернативы float-колонкам (и связанным с ними хакам) — те же флексбоксы.

  7. В общем, по спецификации, для таких вещей, как таблицы, ширина считается по алгоритму shrink-to-fit. Кратко объяснить его суть можно так. Вначале браузер "спрашивает" у контента, сколько бы ему хотелось иметь ширины, чтоб вольготно раскинуться в одну строку, не ломая строки переносами. Если ширина, "запрошенная" контентом, меньше доступной ширины контейнера — именно эта "запрошенная" ширина назначается нашему элементу. Если же нет — назначается такая ширина, чтобы элемент вплотную вписался в доступную ширину контейнера, а контенту волей-неволей приходится втискиваться в эту ширину, перенося строки.

    За вот это пояснение большое спасибо! 
    Статья очень понравилась, особенно —  пояснения к каждому методу,  недостатки, выводы.  Читала уже, но, возвращаюсь снова. Спасибо. :)
     

  8. Почему-то опера не растягивает clearfix-блок на 100%, ширина только по содержимому.
    Приходится дополнительно прописывать для Оперы 100% ширины, даже в ИЕ7 все ок, отчего так?
    Структура простая, например что-то вроде этого:
    <div class="clearfix"> <!— ну, или .table, если по-вашему —>
    <div style="float:left"></div>
    <div style="float:right"></div>
    </div>
    В остальном отличный хак, спасибо!

    1. Прошу прощения, не совсем точно выразился.
      Не на всю ширину родителя Опера тянет.
      Например, если условно ширина родителя равна 100%, а плавающих блоков внутри clearfix — 30% + 40%, то clearfix получается 70%, а не 100%.
      Вот такая Опера бяка!

  9. Проблема overflow:hidden как я понял, заключается только в блоках с абсолютом? Тогда есть еще вариант. Тока сомневаюсь, что кто-то ответит. Давно никто не писал. Ответьте че-нить.

      1. Вобщето да. Тогда метод внешнего блока с релэйтивом отпадает. Тем более, что по ссылке об этом уже говорилось. Но все равно мозилка остается камешком преткновения.

    1. уже увидел среди комментариев некую вставку со ссылкой на статью, в которой описана эта проблема… было бы здорово, если бы эта "вставка" оказалась прямо вконце статьи…

      1. Я уже несколько раз предлогал дополнить статью, но видимо у них просто руки не доходят. Вот актуальный clearfix:

        /* clearfix */
        .clearfix {
            -height: 1%;
            *zoom: 1;
            display: table;
        }
        .clearfix:after {
            content: '. . . . . . . . . . .';
            font: .13em/0 sans-serif;
            display: block;
            word-spacing: 99in;
            height: 0;
            overflow: hidden;
        }
        /* end clearfix */

        1. Единственное, что я всё-таки решительно и громко возражаю против названия «clearfix» для этого подхода:). Clearfix-ы, имхо, как раз отличаются тем, что не создают BFC (что хорошо видно из примера к статье), а это решение скорее ближе к overflow:hidden и ему подобным. Думаю, можно называть его как-нибудь вроде «floatfix» или, еще лучше, «bfc-fix».

           

          Возможно, я пристрастен и придираюсь, но мне кажется, что подчеркнуть эту разницу важно для понимания сути.

          1. не могу не согласиться с тобой. Это кусок из моего сниппета который я использую, я привык к этому названию просто, оно мне удобно :)

  10. Интересно, а такому способу будут аргументы против? :

    На странице примера выбираем галочку "Ничего"
    затем у блока с классом "clear-both" убираем стиль "clear: both;"
    добавляем ему стиль
    .clear-both:before {
        display: table;
        content: '';
        width: 100%;
    }
    Блоку с классом ".center" добавляем стиль "margin-left: 200px".

    Вроде бы все поставленные задачи решает. 

    1. Дело в том, что, во-первых, display-table создаёт свой контекст, что позволяет всем его вложенным элементам, включая плавающие, оставаться внутри него. Т.е. созданный блочный контекст начинает их "чувствовать". В вашем примере этого не происходит. А, во-вторх, благодаря решению Ильи Стрельцына нам не приходится учитывать ширину левой плавающей колонки. У вас это приходится делать при помощи левого margin'a.

    2. К сожалению, совсем не работает в Firefox (32) — псевдоэлемент-распорка, вместо того, чтобы проваливаться под картинку, просто торчит вправо, вызывая горизонтальный скроллинг. Кроме того, трюк с псевдоэлементом не универсален: вместо абзаца могла быть картинка с float:left; clear:left;, к которой его не приделаешь. Наконец, никак не решается проблема выпадения из контейнера последней плавающей картинки. Ну и хардкод для левого отступа, как верно заметил Максим — что, если левая колонка будет резиновой или скрываемой?

      Постановка задачи предполагала, что контентная колонка — «черный ящик», содержимое которого для нас не всегда предсказуемо (напр. генерируется визивигом). И взаимовлияние этого содержимого и его окружения нужно свести к минимуму, насколько возможно.

      1. Да, согласен, недоглядел.

        >> Firefox (32) — псевдоэлемент-распорка, вместо того, чтобы проваливаться под картинку, просто торчит вправо, вызывая горизонтальный скроллинг
        У меня в том же FF, псевдоэлемент как раз оказался под картинкой, НО ещё и под левой колонкой. Тоже сначала не заметил этого.

    1. С шириной элемента только не всё. А так, да, еще один вариант, близкий к третьему.

  11. Обалденный метод, интересный, но… непонятный :(
    Вот то, что Вы пишите, на уровне идеи, в общем-то, понятно, но в общую картину, к сожалению, не складывается… Опыта маловато.
    Вот мы повесили на правый контейнер display: table превратив его в большую ячейку, размеры который, как я понял, выровнялись по содержимому.
    Далее у нас идёт блочный псевдоэлемент
    1) у него свойство font: .1px/0 a — непонятно.
    2) то за счёт чего он растянулся понятно — за счёт огромного word-spacing
    3) Браузер охренев от такого запроса «назначается такая ширина, чтобы элемент вплотную вписался в доступную ширину контейнера» — КАКОГО ИМЕННО контейнера?
    4) Вы пишите «для таких вещей, как таблицы, ширина считается по алгоритму…» — ну ведь псевдоэлемент у нас блочный? какое влияние на него оказывает исходная «таблица»?
    В общем ключевой, наверное, третий вопрос — в какой контейнер и каким образом наши точки (или единицы? и, кстати, где они?) должны вписаться таким чудным образом, чтоб блок, то есть, извиняюсь таблица, простраивала свои размеры не по содержимому, а по величине исходного блока?
    ЗЫ: извините, если не понимаю элементарных вещей, но Вы в этом уже годы, и какие-то вещи кажутся для Вас сами собой разумеющимися… а я пока к ним только привыкаю, поэтому просьба всё-таки разжевать. Спасибо!
    PSPS: кстати, а зачем у левой колонки установлено относительное позиционирование?
    ЗЫЗЫЗЫ: кстати, фраза: «левая колонка и плавающая картинка в правой колонке остались в одном и том же контексте форматирования, поэтому clear реагирует и на то, и на другое.» — в каком именно контексте форматирования, и на что именно и каким образом реагирует clear? :)

    1. Контексты форматирования — вообще довольно малоизученная область, до совсем недавнего времени в спецификациях CSS не было лучшего определения для них, чем «штука, заставляющая CSS делать раскладку» (серьезно!). Только недавно эти вещи стали более-менее нормально описывать в спецификациях. Так что не понимать их — это не страшно, пока это скорее норма:).

      Попытаюсь ответить на вопросы и объяснить как можно проще. Начну с ответа на ЗЫЗЫЗЫ — именно в нем главный момент, ради которого всё затевалось.

      По умолчанию блоки в CSS выстраиваются друг за другом по одним и тем же правилам: сверху вниз друг за другом в том порядке, в каком они идут в коде, margin-ы между соседними блоками схлопываются, а float-ы «наезжают» на сами блоки, но ужимают их содержимое. Область, в которой действуют эти правила — это и есть блочный контекст форматирования. И свойство clear отменяет у блока обтекание всех предшествующих float-ов во всей этой области. Изначально эта область охватывает всю страницу, но некоторые элементы (ячейки таблиц, инлайн-блоки, сами float-ы, блоки с overflow, флекс-элементы и т.п.) обособлены от нее, в них образуются свои замкнутые «мирки» со своими правилами, а всё, что происходит снаружи, на них не влияет.

      Именно поэтому классический clearfix, основанный на действии clear, не всегда подходит — он «цепляется» не только за плавающее содержимое того контейнера, к которому применен, но и за все предыдущие плавающие элементы на странице. Например, как в примере из статьи, за левую колонку. Поэтому нам нужно обособить правую колонку, чтобы в ней образовался как раз изолированный «мирок» — отдельный, не зависящий от остальной страницы блочный контекст форматирования. Чаще всего для этого используют oveflow, но у него свои ограничения.

      display:table заставляет правую колонку форматироваться как таблицу, а ее содержимое — как ячейку таблицы. Тем самым это содержимое оказывается в своем отдельном контексте форматирования, чего мы и добивались. Но возникает побочный эффект — таблица не всегда занимает всю доступную ширину родителя, если в ней контента меньше, она оказывается уже. С этим эффектом мы и боремся с помощью хака с псевдоэлементом.

      1) font: .1px/0 a — самая короткая запись для того, чтобы текст псевдоэлемента стал невидимым и не занимал места. В одной строке мы задаем font-size меньше пикселя (можно было и 0, но на момент публикации с нулем в некоторых браузерах бывали проблемы), нулевую line-height, и указываем букву «a» вместо имени шрифта для того, чтобы свойство применилось (для чего нужны минимум 3 параметра). Разумеется, браузер скорее всего не найдет шрифта с именем «a» и отобразит точки шрифтом по умолчанию — но нам всё равно, текст всё равно микроскопический, невидимый и роль его чисто вспомогательная.

      2) Всё верно, за счет огромного word-spacing текст растянулся настолько, чтобы с гарантией не поместиться в одну строку и перенестись на следующую. А значит, ширина содержимого нашей «таблицы» уже гарантированно не меньше доступной ширины…

      3) …ее родительского контейнера, в котором находятся обе колонки. В нашем примере это body.

      Благодаря огромному word-spacing-у «таблица» всегда будет вести себя не так, как когда в ней только короткая строка текста, а так, как когда в ней много текста (и она занимает всю доступную ширину родителя — за вычетом padding-ов и предыдущих float-ов, в нашем случае левой колонки — точно так же, как это делают обычные блоки). Тем самым мы получаем блок, в большинстве случаев не отличающийся поведением от обычного блока — но со своим обособленным блочным контекстом форматирования. В общем, псевдоэлемент просто «распирает» «таблицу» настолько, чтобы она всегда занимала всю доступную ширину. В контейнер вписывается не псевдоэлемент, а именно сама «таблица», псевдоэлемент просто не дает ей сжиматься, оставаясь сам невидимым благодаря микроскопическому шрифту и нулевой высоте строки.

      На PSPS: без relative и z-index в примерах «без всего» и с clearfix-ом левая колонка оказалась бы перекрыта правой — как раз потому, что float-ы проникают в границы блоков в том же контексте форматирования (но сдвигают их содержимое).

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

      1. Спасибо Вам огромное! Прочитал весь текст и из-за того, что он так замечательно «разжёван» стало практически всё понятно! Позже, конечно, прочитаю всё ещё более внимательно, поразбираюсь, но в любом случае большое Вам спасибо, за такое подробное объяснение, это здорово и очень приятно, когда так помогают!

      2. Ну вот исходя из написанного и ссылок мне представляется клиеренс — одно из наиболее значимых для базового понимания написанного в статье. Когда понимаешь как работает clear для плавающих элементов, становится понятно к каким эффектам это может приводить и отсюда уже можно искать, как с этим бороться.
        Кстати забавный момент, насколько я понимаю, в спецификации не сказано как таблица должна вести себя рядом с плавающим элементом. Поэтому, она, в принципе, может расположиться и ниже флоата. Спасибо броузерам что они поступают разумно! :)

        1. Да, это действительно забавный момент, где спецификация фактически не обязывает браузеров ни к чему, но они сами на удивление дружно ведут себя разумно:). Причем это справедливо и для таблицы, и для блока с overflow, например.

      3. Отмечу ещё один момент — при отрицательном марджине флоаты располагаются поверх блочных и внизу строчных, что в общем-то логично, в обычном варианте они сдвигают строчные, а когда мы специально задаём отрицательный марджин, то строчные на флоат «налезают».
        И да, с наступившим! :-)

      4. Илья, в догонку.

        «На PSPS: без relative и z-index в примерах «без всего» и с clearfix-ом левая колонка оказалась бы перекрыта правой — как раз потому, что float-ы проникают в границы блоков в том же контексте форматирования (но сдвигают их содержимое).»

        Я не знаю, какие теги можно использовать, поэтому использую (выборочно:) просто правила русского языка, что, в оформительских целях, по-моему, не достаточно :)
        А по поводу процитированного, хорошо бы, наверное, сказать, почему это происходит — потому что правая колонка релейтив, а значит в контексте наложения, она лежит выше, поэтому и будет виден её фон, а не левого флоата. а что фон правой колонки может делать, за её видимыми границами — это Вы уже сказали, объясняя свойства плавающих элементов. А правой колонке присвоили релейтив, для того, чтоб абсолютно позиционируемый элемент можно было относительно неё разместить (а иначе будет относительно окна). Но это, к слову, а то вот перечитываешь, а контекст сходу не понятен :)
        в статье не указаны настройки .clearfix по-умолчанию, или я его в упор не увидел :) хотя бы фон и позишн релэйтив, не плохо бы обозначить.
        Ну и картинку того, что получается без специальных методов (там по-моему клиар: боф работает, который у потомка).
        ЗЫ: ставишь сейчас в клиарфикс кроме фона и релэйтива волшебную строчку column-count: 1; и наслаждаешься…

      5. Сообщения сейчас, так и появляются/исчезают после публикации, задерживаясь в кэше вордпресса? сообщение опубликовал — увидел, а теперь оно куда-то исчезло :)
        Вот пример, того, о чём мы говорили: https://jsfiddle.net/Lyn4c2y2/

  12. Спасибо за интересную и толковую статью. Пока еще обучаюсь верстке. И попробовал рещить данную задачу следующим образом.
    https://jsfiddle.net/0chx00L0/
    Вроде работает. Какие в моем способе недостатки?

    1. Этот способ (как и классический clearfix и др. вариации на его тему) тоже не изолирует контекст форматирования, а значит, блоки с clear внутри правой колонки могут «цепляться» за левые (в обоих смыслах:) float-ы. Что может помешать, например, если левый сайдбар окажется выше.

      И второй недостаток — необходимость дублировать в коде ширину сайдбара, для самой ширины и для отступа. Что может быть неудобно, если эта ширина должна быть динамической.

      В некоторых ситуациях способ имеет право на жизнь, но увы, это тоже компромисс. Так что ждем, когда браузеры начнут поддерживать display: block flow-root и по возможности переходим с float-ов на флексбоксы! :)

      1. Спасибо! Понял свою ошибку. А флексбоксами еще не рано верстать? Старые версии браузеров их поддерживают?

        1. Смотря насколько старые браузеры для вас критичны. Флексбоксы не поддерживаются в IE9- и плохо поддерживаются (без многострочности и с префиксом) в IE10 и старых Андроид-браузерах. Если для этих браузеров никак не обойтись менее красивым, но функциональным резервным вариантом на инлайн-блоках или тех же float-ах — тогда да, придется подождать. Но в среднем по статистике этих браузеров уже не больше 3% в сумме.

          1. Скажите пожалуйста, на данный момент в каких баузерах и версиях имеет смысл проверять верстку, если заказчик не указал это в ТЗ. Если проверять на совместимость с IE-6 и выше, лучше всего ставить виртуальную машину под каждую версию? Для других браузеров нужно ли ставить виртуальную машину?

            1. В IE6 проверять, если это не оговорено в ТЗ (и не оплачено в двойном-тройном размере), сейчас точно не надо:). Обычно ориентируются или на возраст браузеров (скажем, «3 последних версии» или «не старше года») либо на их долю в статистике по целевому региону (скажем, «свыше 2% в Рунете»). На мой взгляд, для всего ниже IE11 сейчас вполне достаточно поддержки уровня «все тексты читаются, все картинки в контенте отображаются, все кнопки нажимаются», «вылизывать» верстку в них и добиваться там в точности такого же отображения, как в новых браузерах, не больше смысла, чем пытаться заставить старый черно-белый телевизор показывать полноцветное FullHD.

              Для полноценного тестирования в IE, со всей интерактивностью, да, нужна виртуалка. Но для беглой проверки, не разваливается ли статичная верстка до нечитаемости, режимов совместимости по F12 зачастую хватает.

    1. Честно говоря, в этом нет особого смысла, поскольку, во-первых, важное дополнение из той статьи, которое решало проблему в Opera 12, уже добавлено в эту статью (см. /* Очень важное дополнение! */ в коде), во-вторых, Opera 12 давно уже не 12, и этого бага скорее всего в новых версиях нет, ну, а в-третьих, сегодня вёрсткой правят флексы, и решение из статьи уже немного не актуально :)

Оставить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Получать новые комментарии по электронной почте. Вы можете подписаться без комментирования.