• Список блоков с разным вертикальным выравниванием

    Когда я работал над очередным улучшением сайта БигБаззи, возникла задача добавления нового режима просмотра предложений — по три штуки в ряд, более компактными блоками (было по два крупных блока в ряд):

    example

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

    «Классическое» решение проблемы — прописать минимальную высоту блоку с названием, чтобы угадать некую среднюю величину, выше которой вряд ли что-то появится. Но у этого способа есть два недостатка: во-первых, практика показывает, что обязательно появится название, которое не впишется в отведённое пространство и развалит весь ряд. А во-вторых: при слишком коротких названиях будут появляться неприятные дыры между заголовком и блоком с ценой.

    По сути, надо было придумать, как сделать так, чтобы блоки в одном ряду имели разное выравнивание по вертикали:

    scheme

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

    Как ни странно, но решение задачи нашлось, причём довольно элегантное.

    Обычно такие последовательности блоков делаются самым очевидным образом: через float-элементы. Но этот способ нынче не в моде: все правильные ребята уже давно прочитали статью в блоге Мозиллы о том, как делать их через display: inline-block;.

    А что будет, если поставить рядом блоки разной высоты?

    <style type="text/css">
    	.wrap, .h, .f {
    		display: inline-block;
    	}
    
    	.h {
    		background: red;
    		height: 100px;
    		padding: 0 10px;
    	}
    
    	.f {
    		background: blue;
    		height: 20px;
    		padding: 0 10px;
    		color: #fff;
    	}
    </style>
    
    <span class="wrap">
    	<span class="h">header</span>
    	<span class="f">footer</span>
    	<span class="h">header</span>
    	<span class="f">footer</span>
    </span>
    

    Получим вот такой результат:

    ss1

    По умолчанию у всех блоков vertical-align: baseline, то есть вертикальное выравнивание по базовой линии (в данном случае, если проще — по последней строке текста). Если указать для .h{ vertical-align: top; } и для .f { vertical-align: bottom; }, то получим довольно интересное решение:

    ss2

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

    ss3

    Дальше всё просто: у шапки и подвала одинаковая и вполне определённая ширина, поэтому прописываем её каждому блоку, а подвал убираем из потока с помощью отрицательного margin’a:

    ss4

    Чтобы семантически объединить шапку и подвал, можно использовать любой элемент с display: inline. Итоговая версия доступна на тестовой странице.

    Самое интересное, что этот код работает даже в IE6. Помним, что inline-block в этом браузере эмулируется с помощью display: inline и волшебного hasLayout, например, так: display: inline; zoom: 1;.

    В этом решении важно помнить следующее:

    1. Шапка и подвал в примере пересекаются, поэтому шапке нужно снизу давать отступ и подтягивать подвал любым доступным способом.
    2. Между элементами конструкции не должно быть переводов строк и пробелов, иначе блоки визуально будут разделены.

    В данном случае мне очень помогло то, что у подвала вполне предсказуемая высота, поэтому у шапки был добавлен снизу большой фиксированный отступ, чтобы блоки не перекрывались. Это решение полностью решило проблему и отображения, и переключения режимов. Можно пойти дальше и подключить CSS3 Media Queries, чтобы обладатели новых браузеров и больших мониторов могли видеть более трёх предложений в ряду, но это уже другая история. Думаю, читатели смогут найти более творческие и интересные применения этого небольшого трюка 🙂

  • 42 комментария

    1. 6 апреля 2011

      Замечательное решение, как всегда в твоем стиле 😉

      Я тут недавно также, методом экспериментов, нашел способ отображения инлайн-блоков без зазора между ними, который редко кто использует — fs: 0; у блока обертки и возвращение его значения самим инлайн-блокам (правда, минус в том, что приходится указывать значение размера шрифта в пикселях) — пробельное расстояние пропадает и блоки ведут себя как флоаты.

    2. Дима
      6 апреля 2011

      Нарек, самый эффективный способ убрать у инлайн-блока отступ справа — это margin-right: -0.3em. Ещё ни на одном проекте не подвел, хотя чую, что некие допущения в метода вылезут.

      Сергею спасибо за хороший метод. Не задумывался как-то даже о таком.

    3. 6 апреля 2011

      Дима, и про этот способ, и про удаление пробелов в коде между блоками я в курсе. Но, в отличие от метода с font-size, первый зависим от браузера и зума, плюс у крайнего правого блока mr: -0.3em — не есть хорошо, а второй неприятен, т.к. опрятность кода имеет значение.

    4. Сергей
      6 апреля 2011

      >>display: inline и волшебного hasLayout, например, так: display: inline-block; zoom: 1;.
      наверное описка во втором случае тоже должно быть display: inline;

      Нарек, а если так:

      content

      content
      <!—

    5. Сергей
      6 апреля 2011

      Превью для коммента нехватает

      <div class="ib">
      content
      </div><!—
      —><div class="ib">
      content
      </div><!—

    6. 6 апреля 2011

      Сергей, если отступ снизу у «шапки» фиксирован (как и высота «подвала»), то чем данный метод принципиально лучше абсолютно спозиционированного футера?

    7. 6 апреля 2011

      >>> Олег Громов
      То решение, которое без position: absolute — лучшее.
      Так считают гуру 🙂

    8. Денис Нехорошев
      6 апреля 2011

      > Олег Громов
      Решения с абсолютно-позиционированными блоками плохо тем, что такие блоки «вырываются» из контекста и идут своим потоком. А такое поведение не всегда бывает полезным и предсказуемым.

      Сергей, спасибо за решение. Действительно, очень элегантно.

    9. 6 апреля 2011

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

    10. 6 апреля 2011

      Нарек, самый эффективный способ убрать у инлайн-блока отступ справа — это margin-right: -0.3em. Ещё ни на одном проекте не подвел, хотя чую, что некие допущения в метода вылезут.

      Самое верное решение — использовать свойство word-spacing:-.25em (0,25 em — обычная ширина пробела, но в экзотических шрифтах может отличаться). К сожалению Webkit не применяет его в случае с инлайн-блоками, но зато применяет letter-spacing:-.25em аналогичным образом. Оба свойства замечательно работают вместе во всём зоопарке браузеров. Естественно, внутри блоков надо их вернуть к первоначальному значению, то есть к нулю.

    11. Сергей Чикуенок
      6 апреля 2011

      Нарек, я лично не люблю такой способ удаления пробелов, потому что: а) теряется контекст с размером шрифтов (я предпочитаю у <body> выставить размер в пикселях, а внутри использовать относительные единицы) и б) у некоторых браузеров есть ограничение на минимально допустимый размер шрифта, так что пробел всё равно останется.

      Теоретически, можно попробовать сделать шрифт, в котором будет только символ пробела и его ширина будет равна 0, и подключить его для обёртки.

      Олег, с position: absolute получится только в том случае, если весь ряд будет обрамлён контейнером — а этого как раз хотелось бы избежать, потому что есть переключение в двухколоночный режим. Тем более, не получится сделать так, как на тестовой странице.

    12. Ткаченко Артем
      6 апреля 2011

      Здарова Серега.
      На заметку, если добавить свойство «text-align: justify;»
      будет намного интереснее выглядеть верстка и не будут
      справа постоянно образовываться пустоты за счет незаполненных блоков.

    13. 6 апреля 2011

      Сергей, спасибо за объяснение! Читал между строк, и слона-то не приметил 🙂

    14. 6 апреля 2011

      Ткаченко Артем, эх, с justify последняя строка всё равно по левому краю выравнивается. А так было бы здорово.

    15. Дмитрий Шимкин
      6 апреля 2011

      Артём Сапегин, для justify нужно добавить в конце пустой span display: inline-block, width: 100%

    16. Дмитрий Хасанов
      7 апреля 2011

      Предложенный способ с пустым span’ом и justify позволяет растягивать блоки на всю страницу. Однако, если в последней строке содержится меньше блоков, чем в предыдущих, они также будут распределены по всей ширине, несмотря на то, что их меньше. Получаются бОльшие межблоковые расстояния, стройность и красота исчезают.

      Либо отказываться от justify, либо придумывать, как выровнять блоки в последней строке.

      В одном из проектов для кошерного выравнивания блоков, аналогичного указанному способу с justify, но без проблем с последней строкой, использовали js. Некрасиво. Медленно. Кроссбраузерно.

      Буду рад, если кто поделится соображениями по этому поводу.

    17. Ткаченко Артем
      7 апреля 2011

      для всех у кого есть проблема с IE с justify, все уже давным давно решили эти проблемы.
      1) http://forum.htmlbook.ru/index.php?showtopic=20735
      2) что касается последней сточки, я в свое время добавил вниз кучу пустых элементов невидных и показывал их потихоньку по мере того сколько в строчке не хватало. работает все очень-очень быстро, решение не самое красивое но если решить задачу и вариантов нет — 5 строчке jquery и все готово.

    18. akoppela
      9 апреля 2011

      Отличный способ, респект! Давно перешел на верстку без флоатов, инлайн-блоками =) Пришлось отказаться от ие6, зато получить огромную гибкость и простоту в коде. И конечно стабильность работы.

      http://castly.com

    19. 12 апреля 2011

      >я предпочитаю у выставить размер в пикселях, а внутри использовать относительные единицы
      Расскажите пожалуйста, каким образом рассчитываются значения для этих единиц? Каким образом обходится проблема с округлением? Вы применяете относительные единицы только для текста, или для всех элементов?

    20. 12 апреля 2011

      Парсер удалил тег <body>в цитате.

    21. Сергей Чикуенок
      12 апреля 2011

      Максим, насколько я знаю, все браузеры (кроме FF) округляют дробные значения до целых единиц (пункты или пиксели), поэтому относительный размр шрифта подбирается «на глаз». Относительные единицы стараюсь использовать не только для шрифта, но и для отступов.

    22. 12 апреля 2011

      Округляются — да, вопрос только в том, в какую сторону. Где-то, например, 16,4 округляется до 16, а где-то до 17. Не очень кроссбраузерно получается.

    23. Виталий
      12 апреля 2011

      Этот метод использую давно, а вот за подвал — не знал, спасибо большое.

    24. 14 апреля 2011

      я такие вещи ток начинаю учить. спасибо за статью

    25. Олег
      3 мая 2011

      Нарек, я обычно вывожу данные через xsl, так что самое простое – это съедать пробелы и выводить теги бок-о-бок вот этот способ ни разу не подводил =)

    26. Арарат
      13 мая 2011

      Вся красата меркнет на фоне мудацкой ринудительной регистрации.

    27. Роман
      1 июня 2011

      Если используеться Django в качестве фреймворка, то для удаления пробелов я использую теги {% spaceless %} {% endspaceless %}

    28. Сергей
      22 июня 2011

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

    29. 28 июня 2011

      Решение действительно элегантное, он я бы лучше запихнул в обертку и абсолютом, хотя.. сам факт такого решения это круто))

    30. Фархад
      30 июня 2011

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

    31. 30 июня 2011

      @Фархад Не совсем.. но в принципе да 🙂 Можно и таблицами. Но тут не много не то строение ячейки..

    32. Андрей
      1 июля 2011

      Господи, и почему ты заставил их возненавидеть таблицы…

    33. Rencom
      15 июля 2011

      >данном случае мне очень помогло то, что у подвала вполне предсказуемая высота

      а что если у подвала тоже может быть произвольная высота? Есть какие-то мысли как решить такую проблему?

    34. Дмитрий
      26 сентября 2011

      Подскажите пожалуйста, я не совсем понял: как сделать так что бы в ие6 футер располагался под хедером ? заранее спасибо.

    35. 24 ноября 2011

      YUIgrids отступы убираются вот так
      .yui3-g {
      letter-spacing: -0.31em; /* webkit: collapse white-space between units */
      *letter-spacing: normal; /* reset IE < 8 */
      word-spacing: -0.43em; /* IE < 8 && gecko: collapse white-space between units */
      }
      раньше я использовал font-size:0; пока не увидел решение от yahoo. И оно стало моей серебрянной пулей

    36. Dmitry
      5 января 2012

      >Чтобы семантически объединить шапку и подвал, можно использовать любой элемент с display: inline.
      1. Для обертки используется и задается ему display: inline, зачем задавать строчному элементу это свойство?
      2. Почему два строчных элемента(шапка и подвал) чтобы семантически объединить нужно их обертку делать строчным? Почему не семантически будет вот так (без display: inline):

      header
      footer

      ведь в блоки можно вставлять строчные

    37. Dmitry
      5 января 2012

      теги в примере моем съело. мысль была в том чтобы и заменить на divы с теми же классами но без display: inline

    38. S
      29 апреля 2012

      Блин чувачелы мне до вас далеко….ну как минимум пару лет чтобы изучить все, то что знают немногие!!! Литературы валом а толку….я просто незнаю с чего начать, может кто-нибудь поделится опиытом и подскажет с чего начинать надо, в каком порядке html->рhp->java->css->photoshop->flash….??????

      P.s.Я Чайник чайникам чайник)

    39. S
      29 апреля 2012

      Серега… Сайт «БигБизи»охрененно тяжелый, хотя и не очень!!!

    40. Андрей
      30 апреля 2012

      БигБаззи не очень тяжёлый?? Полтора мегабайта отрезают сразу половину мобильных устройств. Большая часть сайта свёрстана профессионалом из 98-го года для таких же профессионалов, полностью забывших про компоновку статики в 1 файл. CSS на классах и инлайн JS? Увольте… Этот сайт делали люди, которым насрать на клиента, в виде посетителей. Этот сайт делали люди, которым не насрать на чудесный эффект уменьшения колонок при изменении разрешения — это очень важно. Да что там, люди думают, что jQuery быстрее грузится с их хоста, нежели чем с Google. Профессионалы, одним словом.

    41. Саша
      5 сентября 2012

      А как быть, если ширина в процентах?

    42. Weeee
      14 сентября 2012

      Да, сайт работает очень медленно. Ширина окна в FireFox меняется крайне не плавно. Это не смешно, процессор i5, а так работает. Это сделано для отсчета времени?