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

Тем не менее, я подумал, что такой пример все же будет полезен, потому что наглядно показывает, на какие ухищрения надо идти при использовании неподходящих инструментов. То есть это будет скорее отрицательный пример. Больше того, в этой статье будет пример сразу двух вариантов: один другого кривее :-).

Общий принцип

Сам по себе предыдущий макет, в общем-то, уже сверстан достаточно гибко. Ширины всех колонок заданы в процентах, а единственное, что его держит в рамках — это явно проставленная в пикселах ширина <body>. Если ее убрать, то раскладка должна нормально тянуться.

Но наступлению счастья, к сожалению, мешает фоновая картинка, которая визуально представляет колонки. Она, конечно, тянуться не будет и весь колоночный вид собьет. Поэтому с фоном надо что-то придумать.

Решение усложняется тем, что колонок в макете — три. В случае двух колонок фон достаточно было бы сделать пошире и повесить на странице в нужном месте, как я описывал в комментарии к статье про float'ы. С тремя же колонками картинку эту надо как-то разорвать на две, потому что тянуться она не может.

Итак, приступим...

Растягивание

Начниаем с того, что убираем у <body> его ширину в 700px. А чтобы оно не занимало всю ширину окна, закрывая мой любимый фоновый узор, надо дать margin'ы справа и слева:

body {
  padding:0;
  margin:0 50px;
  min-height:100%;
  position:relative;
}

Значения padding, min-height и position — это не что-то новое, они остались из предыдущей раскладки.

Есть одна проблема, тем не менее. Если подвигать ширину окна в IE, то можно заметить, что колонка новостей хаотично скачет вниз и обратно. Это от того, что при некоторых размерах что-то там не так округляется и 20% + 55% + 25% оказываются больше 100% буквально на 1 пиксел, и колонка не влезает. Чтобы это починить можно, например, добавить центральной колонке справа отрицательный margin, который и будет давать нужный люфт. Для этой колонки как раз уже есть специально для IE правило, исправляющее один из предыдущих багов, добавим margin туда:

* html #main {
  margin-left:10%;
  margin-right:-1px;
}

Теперь все должно двигаться нормально, и можно перейти к фону.

Вариант 1: картинки в колонках

Первый способ разделить картинку — это снять цельную у <body>, разделить на две части, которые находятся непосредственно под колонками, и назначить их самим колонкам:

body {
  background:white;
  color:black;
}

#sections {
  background:url(left-col-bg.png) #A6BDFF right top repeat-y;
}

#news {
  background:url(right-col-bg.png) white left top repeat-y;
  color:#293499;
}

Про картинки тут стоит сказать отдельно. Обе они не бесконечной ширины, вот левая:

... и правая:

Они прижаты к внутренним краям колонок, и когда те становятся шире фона, надо проследить, чтобы цвет, который будет проглядывать из под картинки сливался с ней красиво. Это оттенок голубого с правого края картинки для левой колонки и белый — для правой. Задаются они, как видно, в тех же правилах, что и сам фон.

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

#sections {
  float:left; width:20%; margin-left:-75%;
  padding-bottom:32767px; margin-bottom:-32767px;
}

#news {
  float:right; width:25%;
  padding-bottom:32767px; margin-bottom:-32767px;
}

И вот как это выглядит.

Теперь колонки заканчиваются на одной высоте, но продолжаются не до конца, а обрываются по самой высокой из них. Это происходит потому, что их обрезает там блок #content, который имеет автоматическую высоту и overflow:hidden. Отказаться от этого нельзя, потому что иначе все длинные хвосты колонок будут отображаться и страница приобретет довольно длинный скроллер :-).

Ему также нельзя поставить min-height:100%, потому что у его контейнера (<body>) четкая высота не задана, а, напомню, 100% от незаданного браузер посчитать не может. Да и все равно это бы выглядело плохо, потому что будь #content в точности высоты окна, из-за того, что он начинается не сверху, а под блоком заголовка, он бы заталкивал подвал ниже нижнего края.

В этом состоит минус первого варианта: придется отказаться от эффекта "страница не короче окна". Для этого все правила для позиционирования подвала нужно удалить:

body {
  padding:0;
  margin:0 50px;
  min-height:100%;
  position:relative;
}

* html body {
  height:100%;
}

#meta {
  position:absolute; bottom:0;
  height:40px; width:100%;
  padding:1px 0;
}

#content {
  padding-bottom:42px;
}

Получится так.

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

#sections {
  float:left; width:20%; margin-left:-75%;
  padding-bottom:32767px; margin-bottom:-32767px;
  padding-right:1px; margin-right:-1px;
}

#news {
  float:right; width:25%;
  padding-bottom:32767px; margin-bottom:-32767px;
  padding-left:1px; margin-left:-1px;
}

Вот теперь этот вариант готов.

Вариант 2: плавающие фоны

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

Таким контейнером в фиксированной раскладке работал <body>, но сейчас этого не хватит, потому что в один блок в текущей версии CSS можно положить только один фон. А нам их нужно два, которые будут разъезжаться в разные стороны. Поэтому придется добавить еще один блок, чтобы держать вторую часть фона.

Здесь я хочу сделать паузу и для важного замечания. К сожалению, большинство верстальщиков, которые начинают изучать CSS'ную верстку, воспринимают ее как просто другой синтаксис верстки "вместо таблиц", полностью игнорируя сам смысл подхода с разделением структуры содержимого и оформления, о котором я писал в первой статье про "компот и мух". Поэтому идея добавить в HTML пару лишних <div>ов не вызывает даже секундного раздумья.

Нет в CSS-верстке хаков хуже тех, которые изменяют структуру HTML-документа для чисто оформительских целей.

Это, однако, не означает, что ими никогда нельзя пользоваться. Это не догма, а вопрос оценки требований и возможностей. Больше того, несовершенство современного CSS, к сожалению, временами просто вынуждает к таким мерам. Но надо всегда стараться уменьшить их количество.

Ну да ладно. Представим, что у вас дома семья из пяти голодных ребятишек, а заказчик обещает заплатить 3 миллиона долларов за трехколоночный макет с прижатым к низу подвалом. Займемся.

Для начала уберем все предыдущие правила для предыдущего варианта с фоном в колонках и уберем фон у <body>. Получится такая отправная точка.

Теперь дополнительный контейнер. Обычно его вкладывают внутрь существующего на манер матрешки:

<body>

  <div id="container">

    <остальной контент>

  </div>

</body>

Но нам этот способ не подойдет. Наша цель, чтобы он имел ту же высоту, что и <body>, но, как я уже упоминал раньше, для <body> у нас высота не задана, а значит никакие height:100% и min-height:100% работать не будут, внутренний контейнер не будет дотягиваться до низа. Написать <body> четкую высоту тоже, очевидно, нельзя, потому что тогда оно перестанет тянуться вниз, когда текста много. Что делать?

Наверное есть какие-то нерадикальные способы, но я не нашел другого, кроме такого вот... хм... извращения (по-другому и не назвать):

  1. <body> назначается position:absolute и min-height:100%. Оно в этом случае визуально никак не изменится, останется висеть на том же месте.
  2. Дополнительный контейнер, делается просто пустым блоком в начале <body>, не заключающим в себя остальное содержимое. Он тоже позиционируется абсолютно, ему назначаются высота и ширина в 100%. А дальше он перемещается под <body> с помощью отрицательного z-index'а.

Таким образом достигается то, что и <body>, и дополнительный контейнер полностью совпадают геометрически. В коде это выглядит так.

HTML:

<body>

  <div id="body2"></div>

  ...

CSS:

body {
  padding:0;
  margin:0 50px;
  min-height:100%;
  position:absolute; z-index:0;
}

#body2 {
  position:absolute;
  height:100%; width:100%; z-index:-1;
}

Внимательные читатели, у которых к этому месту еще не болит голова, заметят одну странность. Я говорил, что невозможно расположенному внутри <body> контейнеру сделать высоту 100%, потому что у <body> высота не задана. Однако здесь сделано именно так.

Почему? Я не знаю :-). Судя по всему, абсолютно позиционированные блоки ведут в этом отношении себя чуть менее привередливо, чем статические. Я попробую порыться в спецификации CSS, но не уверен, что что-нибудь найду. Возможно, это просто синхронный баг Firefox'а и IE, в других браузерах я не смотрел.

Теперь дело за малым: повесить плавающие фоны, имитирующие каждую из колонок. Плавающий фон представляет собой очень длинную горизонтальную картинку, такую, чтобы она была шире любых разумных разрешений экрана. Пусть будет 4000 пикселов. Дальше эта картинка делится по ширине в том же отношении, что и ширина колонки: например левая колонка имеет ширину 20%, значит картинка для нее разделится на части 800 пикселов слева и 3200 — справа. Для правой колонки — аналогично.

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

Картинки, кстати, в формате png, хотя считается, что прозрачность в этом формате не поддерживает IE. На самом деле, он не поддерживает только переменную прозрачность. Если же картинки перевести в индексированную палитру из 256 цветов, то все работает.

Теперь прикрепим их в нужные точки:

body {
  background:url(left-body-bg.png) 20% 0 repeat-y;
  color:black;
}

#body2 {
  background:url(right-body-bg.png) 75% 0 repeat-y;
}

... и получим практически то, что надо.

"Секрет" правильного расположения фонов заключается в том, что цифра позиции фона (20% и 75%) означает точку и в ширине контейнера, и в ширине самой картинки. Именно поэтому "0" означает помещение левой стороны картинки к левому краю, "50%" — середины картинки в середину, а "100%" — правой стороны к правому краю. И при сжимании и разжимании окна картинка всегда будет "ездить", прицепившись своими 20% или 75% к тем же позициям.

Остался фон внутри средней колонки, который я специально оставил отдельно. Сейчас два фона лежат один под другим: <body> выше, #body2 ниже. И чтобы достигнуть нужного эффекта, по идее, достаточно было бы закрасить белым то пространство, которое у нижней картинки прозрачное. Если бы не Internet Explorer :-).

Я затрудняюсь это объяснить понятным способом, но получается так, что z-index:-1, назначенный дополнительному контейнеру #body2, действительно уводит его под <body>, но не до конца, а только под содержимое. Фон же <body> остается все равно в самом низу. Таким образом, если картинку "как-бы-нижнего" блока зарисовать белым, то в IE она перекроет картинку левой колонки.

Исправляется это очередным хаком (последним на сегодня). Мы зарисуем просто белым цветом фон <body> для IE, и фон нижнего контейнера — для остальных:

html>body #body2 {
  background-color:white;
}

* html body {
  background-color:white;
}

Хак html>body обладает эффектом, обратным * html. Так сложилось, что IE игнорирует этот селектор, а остальные нормально его применяют.

Готово.

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

Резюме

Помимо того, что я уже много раз сказал о том, что имитация колонок CSS'ом — неблагодарное занятие, я хочу сделать еще одно важное замечание.

Не существует никакого Универсального Способа Трехколоночной Раскладки. Те решения, которые я описал выше, оказались бы непригодными, для, скажем, другого фона или для непроцентного поведения колонок или подвала с нефиксированной высотой. Там не возникло бы описанных трудностей, но возникли бы совершенно другие. Поэтому я все время ставлю ссылки на предыдущие статьи "Учебника" и призываю читать их и понимать (и спрашивать).


Эта статья - часть находящегося в процессе написания цикла под рабочим названием "Учебник". Я рекомендую ознакомиться и с другими статьями, которые можно найти в категории "Учебник", где они сейчас собраны в обратном хронологическом порядке.

Комментарии: 28

  1. Kildor

    хм…
    предпоследний вариант работает в опере (9 викль), последний — всё красится белым цветом :-(
    Если изменить

    html>body #body2 {
      background:url(left-body-bg.png) 20% 0 repeat-y white;
    }
    

    то заработает всё.

    Хм… баг однако :-(

  2. Сергей Адаменко

    В опере 8.54 для Windows все примеры выглядят отвратительно :(

  3. BOLK

    2Kildor:

    Opera9 build 8482 (Windows), всё ок на всех примерах.

  4. Kildor

    BOLK, у меня 8432.

  5. fuxx

    Интересный эффект в mozilla 1.7.5: я уменьшил размер окна, а вместе с ним уменьшилась и картинка. Теперь я увеличил размер окна - картинка же осталась на самом маленьком уровне! В MSIE 6 все в порядке.

  6. Battle

    У меня такой вопрос: а вот при уменьшении размеров окна, скажем, до 200x100, является ли нормальным, что все эти "столбцы" "плывут", смещаются как хотят и т.д.? (Осёл №6)
    Или хороший этикет, чтобы появлялись горизонтальные полосы прокрутки, и ничего не "плыло"? Однакое, если предпочтительнее второе, то как этого добиться, если всё задаётся в процентах?

  7. Иван Сагалаев

    Является ли это нормальным, полностью зависит требований к сайту. Но в данном конкретном случае я даже представить себе не могу реальной ситуации, где бы сайту требовалось бы "нормально" отображаться в 200-пикселной ширине. Единственное место — это портативный экран, но для него вообще нужен был бы другой стиль, неколоночный.

    В общем, это не вопрос "этикета". Это вопрос того, чего мы хотим добиться.

  8. Доктор Тюнинг

    Не существует никакого Универсального Способа
    Трехколоночной Раскладки.

    Раскладка это раскладка. Раскладка задает только расположение блоков, а какие доп. стили мы на нее навесим это дело второе.

    То есть раскладка должна только размечать блоки.

    Вот кстати интересная ссылочка:
    http://www.fu2k.org/alex/css/onetruelayout/example/interactive

    PS: За статью спасибо!

  9. Доктор Тюнинг

    Ах, да. Хотел подкинуть задачку посложнее.
    Сделать 3-x колоночную верстку с поочередностью 2-1-3
    труда особого не составляет, если, все колонки в процентном соотношении, а если требуется что бы боковые колонки были фиксированного размера, то начинается гемморой :)

  10. vvy

    Иван, а есть какие-либо идеи, чтобы заставить работать это в ИЕ5?

  11. Иван Сагалаев

    Честно говоря нет. Я бы вообще так вопрос не ставил. Эта раскладка - всего лишь один из вариантов, поэтому подгонять его под то, подо что он не делался, обычно сложнее, чем сделать изначально так, как требует задача. Кроме того, IE5 - старый браузер, с ним вообще чаще всего целесобразней отказаться от каких-то свойств раскладки, чем обвешивать стили толпой хаков.

  12. Loki

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

  13. Иван Сагалаев

    Обычно делается отдельный CSS-файлик с альтернативным вариантом раскладки, где прописываются все правила, нужные для второго варианта. И скриптом они активизируются, то один, то другой.

    Например, если альтернативный вариант - убрать колонку, то файлик может выглядеть как-нибудь так:

    #column1 {
      width: 60%;
    }
    
    #column2 {
      width: 40%;
    }
    
    #column3 {
      display: none;
    }
    

    Скрипт(ик) подключение и отключение можно посмотреть прямо у меня в http://softwaremaniacs.org/js/styleswitcher.js, это слегка адаптированный вариант из статьи на A List Apart

  14. Loki

    Спасибо!
    Но это все равно активное воздействие: что серверным скриптом, что клиентским - принципиальной разницы нет.
    Эх... и зачем мы отказались от таблиц?:)

  15. Stan

    В IE7 все тоже краситься полностью белым. Кстати в нем даже глюки с этой формой добавления комментариев, она прыгает, то вправо, то влево вместе с текстом.

    Спасибо за статьи!

  16. Константин

    Они прижаты к внутренним краям колонок, и когда те становятся шире фона, надо проследить, чтобы цвет, который будет проглядывать из под картинки сливался с ней красиво. Это оттенок голубого с правого края картинки для левой колонки и белый — для правой. Задаются они, как видно, в тех же правилах, что и сам фон.

    Должно быть здесь небольшая опечатка. Наверное "оттенок голубого с левого края картинки для левой колонки".

    Второй вариант с дополнительным контейнером не работает в Опере. Но совсем недавно обнаружил интересное решение – оказывается можно задать второй фон для корневого элемента html. Фон для html распололагается под фоном body, и любые трехколоночные макеты перестают быть проблемой.
    Причем это работает во всех современных браузерах.

  17. даниил

    При добавлении контента в колонку sections, он обрезается, т.е. колонка не тянется по вертикали. С колонками main и news такого не происходит. Нет ли способа изменить такое поведение, чтобы sections также растягивалась по размеру контента?

  18. wechsel

    Иван, я уверен Вы в курсе, что в Опере оба примеры выглядят плохо. Так, к примеру, в первом варианте мы имеем неимоверной длинны скроллер, а второй вариант закрашивает левeю колонку белым цветом. Но это не суть сообщения. Суть состоит в том, что второй вариант не понимается корректно ни одним из браузеров, за исключением Fx. Труд дело благородное, я ценю Ваш труд, по сему очень хотелось бы, чтобы труд стал ценен по истинному. Укажите, пожалуйста, в своей статье этот досадный факт, дабы исключить дальнейших нареканий на эту тему.

  19. Иван Сагалаев

    Совсем не в курсе, если честно :-). Больше того, в комментариях выше говорили, что в Опере все хорошо. Но суть не в этом. Эти примеры никогда не были расчитаны на то, чтобы "работать во всех браузерах". Это именно примеры того, как применяется технология, которую я описываю на протяжении всего Учебника. Моя цель в том, чтобы показать, как это делается, чтобы человек понял, какими инструментальными возможностями он обладает. И некоторые хаки я описываю только для того, чтобы показать, как вообще подходить к багам различных браузеров, чтобы использовать это в своих специфичных случаях. Поэтому я не вижу ровно никакого смысла допиливать эти искусственные примеры до того, чтобы они работали в каких-то определенных версиях. Зачем? В реальной раскладке будут какие-то свои особенности, которые можно будет использовать для того, чтобы что-то куда-то зафиксировать.

  20. andrvm

    почему нельзя использовать html в качестве дополнительного background-а, для трехколоночной верстки, с резиновым центром и фиксированными 2-мя другими колонками (к примеру) оно самое то, например:

    html    {height:100%; background:#e4e4e4 url(background_1.gif) repeat-y right top;}
    
    body    {background: url(background_2.gif) repeat-y left top; position:relative; margin:0 auto;
             min-height:100%; height:auto !important; height:100%; z-index:0;}
    
    html, body
            {width:100%; min-width:1000px; max-width:1280px;}
    

    ??

  21. Иван Сагалаев

    Можно и HTML, конечно. Просто, у меня он уже использован под серую текстуру, поэтому фоны колонок лежат в других элементах.

  22. Яшка

    FF 3.03 - все белое, фонового рисунка в колонках не видно, видимо способ перестал быть работоспособным

  23. Aska

    Доброго времени суток!
    У меня такой вопрос. Колонки у меня реализованы как на примере из ссылки в комментарии Доктора Тюнинга. Но мне нужно, чтобы высота всех трех колонок была одинаковой и устанавливалась по самой длинной.

  24. Ambentos

    При всем уважении к вам, в примере http://softwaremaniacs.org/blog/wp-content/3col-layout/layout.html выставили бы хотя бы кодировку в коде.

  25. Ренат Хайретдинов

    Похоже, что в последнем хаке баг был у Firefox'а, а не у IE. Чтобы верстка стала работоспособной во всех последних версиях браузерах (в том числе IE6,7) нужно вместо последних двух правил добавить цвет фона в общее правило для body:

    {background: url(left-body-bg.png) white 20% 0 repeat-y;}

    Интересное знание, что отрицательный z-index, смещает элемент под контент родителя, но оставляет элемент над фоном родителя.

    Спасибо за классный учебник! Ничего более ценного по верстке в рунете не встречал.

  26. Маниакальный веблог » Резиновая раскладка Здесь я хочу сделать паузу и для важного замечания. К сожалению, большинство верстальщиков, которые начинают изучать CSS'ную верстку, воспринимают ее как просто другой синтаксис верстки "вместо таблиц", полностью игнорируя сам смысл подхода с разделением структуры содержимого и оформления, о котором я писал в первой статье про " компот и мух ". Поэтому идея добавить в HTML пару лишних ов не вызывает даже секундного раздумья.

  27. Маниакальный веблог » Резиновая раскладка

  28. Pasmado

    Ему также нельзя поставить min-height:100%, потому что у его контейнера (<body>) четкая высота не задана, а, напомню, 100% от незаданного браузер посчитать не может.

    Это лечится путем задания html,body{height:100%}. и все будет считаться)) кроме шестерки. но и там лечится, без проблем, с помощью простого коммента .

Добавить комментарий