1 сентября 2009 г.

Ровные строки блоков с непостоянной высотой

Задача


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


Решение


У нас имеется HTML-код:

<div>
    <h3>Прости мне, милый друг</h3>
    Двухлетнее молчанье: Писать тебе посланье мне было недосуг. На тройке пренесенный из ...
</div>
<div>
    <h3>От утра до утра</h3>
    Два года всё кружился без дела в хлопотах зевая, веселился...
</div>
<div>
    <h3>В театре, на пирах</h3>
    Не ведал я покоя, увы! ни на часок, как будто у налоя в великой четверток измученный дьячок ...
</div>
<div>
    <h3>Заботы и печали</h3>
    Которые играли, стыжусь, столь долго мной; и в тишине святой философом ленивым ...
</div>>
<div>
    <h3>С диваном, с камельком</h3>
    Три комнатки простые - в них злата, бронзы нет, и ткани выписные не кроют их паркет...
</div>
<div>
    <h3>Где мне в часы полдневны</h3>
    Березок своды темны прохладну сень дают; где ландыш белоснежный сплелся ...
</div>


Этот код описывает последовательность блоков, которые имеют некоторое содержание. Количество текста в каждом блоке своё. Эти блоки имеют ширину (пусть в нашем случае это будет 33% от родительского элемента). Для того, чтобы их выровнять в одну линию, к ним применяется левое обтекание:

div {width: 33%; float: left}

Это отображается в браузере следующим образом:



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



Для борьбы с ошибками обтекания предлагается следующее решение:

Шаг первый


Представим строчки, которые мы хотим получить в виде обычных текстовых строк. И воспользуемся «чудесным» значением свойства display — inline-block. Применим его к нашим блокам, а для того, чтобы это корректно сработало в IE6, заменим блоки на инлайновые span.

<span>
    <h3>Прости мне, милый друг</h3>
    Двухлетнее молчанье: Писать тебе посланье мне было недосуг. На тройке пренесенный из ...
</span>
<span>

    <h3>От утра до утра</h3>
    Два года всё кружился без дела в хлопотах зевая, веселился...
</span>
<span>

    <h3>В театре, на пирах</h3>>
    Не ведал я покоя, увы! ни на часок, как будто у налоя в великой четверток измученный дьячок ...
</span>
<span>

    <h3>Заботы и печали</h3>
    Которые играли, стыжусь, столь долго мной; и в тишине святой философом ленивым ...
</span>
<span>

    <h3>С диваном, с камельком</h3>
    Три комнатки простые - в них злата, бронзы нет, и ткани выписные не кроют их паркет...
</span>
<span>

    <h3>Где мне в часы полдневны</h3>
    Березок своды темны прохладну сень дают; где ландыш белоснежный сплелся ...
</span>


span {width: 33%; float: left;
display: inline-block;}


Можно заметить, что внутри блоков, например, как у нас, могут находится блочные элементы, а помещать блочный элемент внутри инлайнового, так сказать, некошерно. Но меня это не интересует, т.к. я всегда могу для корректности употреблять только инлайновые элементы, а с помощью CSS задавать им нужный display. А сейчас просто не заморачиваюсь. И вам тоже советую не заморачиваться:-))

Теперь можно увидеть, что блоки выстроены по строкам, но при этом хромает вертикальное выравнивание:


Шаг последний


Выставляем нашим блокам вертикальное выравнивание:

span {width: 33%; float: left;
display: inline-block; vertical-align:top;}


Это даст нам то, что требуется:



Работающий пример можно посмотреть здесь.

Всех с праздником!

28 комментариев:

  1. Так не проще ли сделать так:






    тем более любой шаблонизатор понимает остаток от деления на 3.

    ОтветитьУдалить
  2. Нет, не проще, т.к. число колонок может быть произвольным.
    + шаблонизатор (что это вообще такое?) применим не в любом проекте

    ОтветитьУдалить
  3. Да, жаль что нет предупреждения о вырезаемых тегах.
    Для всех поясню, что способ, мною предлагаемый таков
    -div
    --div float:left;/
    --div float:left;/
    --div float:left;/
    -/div

    Когда див закрывать - тогда собственно и срабатывает шаблонизатор.
    В принципе, если знать количество колонок, то можно не парясь сделать таблицей.

    А вот плюс Вашего решения - то что верстка получается на произвольную ширину.

    ОтветитьУдалить
  4. Ну, не совсем...
    1. Плюс как раз именно в произвольной высоте блоков) это более ценно
    2. в вашем решении для того, чтобы вставить блок в самое начало (а остальные должны сдвинуться), предётся переделать всё разбиение по тройкам.
    понимаете?

    ОтветитьУдалить
  5. Ваш способ сработал везде, кроме Seamonkey

    ОтветитьУдалить
  6. Николай, это как понять? Хорошо или плохо? ;))

    ОтветитьУдалить
  7. Артемий Ланц12 сентября 2009 г., 2:02

    помнится наткнулся на habrahabr и сейчас активно пользуюсь этим методом.
    http://tjkdesign.com/articles/css-layout/

    ОтветитьУдалить
  8. Андрей Никишаев12 сентября 2009 г., 2:05

    А вы таблицу юзать не пробовали?
    Не нужно привязываться к глупой философии.
    Табличные данные верстаются таблицами. Юзать для этого блоки калечно и дико.

    ОтветитьУдалить
  9. Андрей Никишаев12 сентября 2009 г., 2:12

    дивы сьело.. вот вообщем они:


    div id="block" /div
    div id="block" /div
    div id="block" /div

    div id="separator" /div

    div id="block" /div
    div id="block" /div
    div id="block" /div

    ОтветитьУдалить
  10. Андрей Никишаев12 сентября 2009 г., 2:12

    Но если вам уж так сильно хочется блоками то вот пожалуйста:

    body {
    width:100%;
    }
    #block {
    float:left;
    width:300px;
    clear:none;
    }
    #separator {
    float:left;
    clear:both;
    width:100%;
    height:20px;
    }

    ОтветитьУдалить
  11. Андрей Никишаев12 сентября 2009 г., 2:18

    Кста.. вы юзали span. Это не совсем верно. Так как он используется для других целей, и юзать его в качестве блока не совсем верно с логической точки зрения.

    ОтветитьУдалить
  12. Для пользователей Seamonkey плохо, для остальных хорошо. Я отношусь к остальным. Так что все в порядке :)

    ОтветитьУдалить
  13. немного не потеме, но у вас в инета магазе можно заказать отрицательное количество товаров :(, на сайте графити проверил

    ОтветитьУдалить
  14. Андрей Никишаев12 сентября 2009 г., 18:14

    Кто таки эти вездесущие пользователи Seamonkey?)

    ОтветитьУдалить
  15. Алексей Кузьмин19 сентября 2009 г., 5:13

    Пожалуй поделюсь своим решением.
    Семантично, без лишних элементов, кроссбраузерно (проверено в IE6+, FF2+).

    HTML:
    <ul><!--
    --><li>
    <h3>Прости мне, милый друг</h3>
    <p>Двухлетнее молчанье: Писать тебе посланье мне было недосуг. На тройке пренесенный из ...</p>
    </li><!--
    --><li>
    <h3>От утра до утрa</h3>
    <p>Два года всё кружился без дела в хлопотах зевая, веселился...</p>
    </li><!--
    --><li>
    <h3>В театре, на пирах</h3>
    <p>Не ведал я покоя, увы! ни на часок, как будто у налоя в великой четверток измученный дьячок ...</p>
    </li><!--
    --><li>
    <h3>Заботы и печали</h3>
    <p>Которые играли, стыжусь, столь долго мной; и в тишине святой философом ленивым ...</p>
    </li><!--
    --><li>
    <h3>С диваном, с камельком</h3>
    <p>Три комнатки простые - в них злата, бронзы нет, и ткани выписные не кроют их паркет...</p>
    </li><!--
    --></ul>

    CSS:
    li {
    width: 33%;
    vertical-align: top;
    display: -moz-inline-stack; /* for Fx2*/
    display: inline-block; /* normal browsers */
    zoom: 1; /* for IE (hasLayout true) */
    *display: inline; /* for IE */
    }

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

    Так как наш список фактически текст, то пробельные символы между элементами будут отображены браузером. Из-за этого на на небольших ширинах контейнера эти два пробела в строке в сумме могут оказаться больше оставшегося 1% и последний элемент упадёт на следующую строку. Есть два решения:
    1) Не ставить пробелов между элементами списка в коде.
    2) Заключить пробелы в комментарии.
    Я использую второй способ.

    В стилях никаких float'ов не нужно - inline-block относительно родителя ведёт себя как инлайновый элемент.
    FireFox 2 не поддерживает display: inline-block, но у него есть проприетарное display: -moz-inline-stack, которое полностью повторяет нужное поведение.
    Для IE не обязательно использовать инлайновый элемент для того чтобы заработал inline-block, достаточно после указания display: inline-block; переопределить display значением inline.

    ОтветитьУдалить
  16. Сколько геморроя, чтобы воспроизвести функционал уже существующей и поддерживаемой во всех броузерах таблицы :(

    Вашу бы энергию да в мирных целях использовать...

    2Алексей Кузьмин:
    > Почему не таблица? Можно сказать что это несемантично

    Ага, а заголовки в список помещать это видать семантично

    ОтветитьУдалить
  17. Далеко не так просто. У таблиц много «против», и они объективны.

    ОтветитьУдалить
  18. Ну перечислите "против", хоть знать буду

    ОтветитьУдалить
  19. 1. больше тегов и их вложенности, а значит медленнее работа JS
    2. меньше гибкости, когда надо быстро поменять что-то местами
    и т.д.

    ОтветитьУдалить
  20. > 1. больше тегов и их вложенности, а значит медленнее работа JS

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

    + обращение по ID независит от уровня вложенности.

    > 2. меньше гибкости, когда надо быстро поменять что-то местами

    Читать (и понимать!) табличный код проще. В Вашем же случае в коде будет лента DIVов и прежде чем менять местами, придётся ещё разобраться, что тут к чему.

    > и т.д.

    Вообщем, коллега Никишаев в своём сообщении от 11 сентября был прав, тут в основном религиозная убеждённость, не требующая объяснений.

    ЗЫ

    +1 Вам за то что удержались от обычного аргумента ДИВных верстальщиков "W3C не рекоммендует!", который я ждал :)

    ОтветитьУдалить
  21. 1. чтобы отрисовать таблицу, браузер должен произвести достаточно много вычислений (т.к. размер всех ячеек может зависеть от контента). Не вижу принципиальной разницы с дивами. Кстати, посмотрите внимательнее, речь шла о JS, который работает на порядок медленнее рендера
    2. ну как вы можете это утверждать, когда как правило это не так. Говорю из моего опыта и опыта тысяч верстальщиков. Когда 2-3 вложенные таблицы - это ужас. Особенно если ещё форматирование плохое.
    ЗЫ
    возьмите плюс себе, мне он не нужен, т.к. я имею достаточный опыт вёрстки, чтобы считать ваши доводы некорректными!

    ОтветитьУдалить
  22. > чтобы отрисовать таблицу, браузер должен произвести достаточно много вычислений (т.к. размер всех ячеек может зависеть от контента). Не вижу принципиальной разницы с дивами.

    При фиксированной ширине колонок, броузер заранее имеет представление, что его ждёт. А в случае с ДИВами не имеет никакого представления вообще.

    > Когда 2-3 вложенные таблицы – это ужас

    А эмуляция 2-3 вложенных таблиц с помощью ДИВов - это букет фиалок, надо полагать? :)

    Не думаю также, что Вы имеете право за тысячи верстальщиков высказываться.

    > возьмите плюс себе, мне он не нужен, т.к. я имею достаточный опыт вёрстки, чтобы считать ваши доводы некорректными!

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

    ОтветитьУдалить
  23. > Кстати, посмотрите внимательнее, речь шла о JS, который работает на порядок медленнее рендера

    Посмотрите внимательнее, обращение по ID избавляет от необходимости обходить иерархию вообще!

    ОтветитьУдалить
  24. (Не всегда можно использовать ID, чтобы было удобно работать с js)

    ОтветитьУдалить
  25. > 1. Бред. 2. Бред. 3. Может быть.

    Оке, я всё понял!

    ОтветитьУдалить
  26. > Бред.

    Ну хватит, хватит, я всё понял уже!

    ОтветитьУдалить