войти зарегистрироваться

1001-ый способ вертикального выравнивания

О вертикальном выравнивании блока неизвестной высоты по центру или низу родителя сказано много. Есть способы, основанные на display:table-cell для хороших браузеров (без кавычек) и expression для IE, способы, основанные на относительном позиционировании (могут плохо работать при переполнении). В этой заметке будет описан способ, работающий на особенностях такого мощного отображения, как встроенный блок (display:inline-block).

screen-capture


Разметка



Сразу оговорюсь, способ имеет недостаток, а именно, лишний элемент. Разметка будет выглядеть так, как показано ниже:
<div class="parent">
    <div class="child">Текст, который заключён во внутренний блок. </div>
    <div class="helper"></div>
</div>


Описываемый метод основан на том, что vertical-align:middle нормально работает для инлайновых элементов. Так как в нашем случае инлайновые элементы не подойдут, используется смешанный тип (display:inline-block) для их эмуляции. Таким образом, если нам удастся представить внутреннее содержимое div.parent как строку, а div.child в ней выровнять по центру, используя vertical-align:middle, то мы достигнем результата.


Стили



Первая идея, которая возникает — задать принудительно высоту строки внутреннего содержимого с помощью line-height в 100% от высоты div.parent — отпадает, т.к. влечёт за собой изменение высоты строки внутри div.child, а переопределение line-height внутри div.child к положительному результату не приводит. На помощь приходит та самая распорка из 90-ых. Добавленный блок Div.helper должен иметь высоту div.parent, тем самым раздвигая нашу строку, как нам нужно. В результате можно выделить значащий CSS:

.child {
    ...
    display:inline-block;
    vertical-align:middle;
}
.helper {
    ...
    display:inline-block;
    vertical-align:middle;
    height:100%;
    width:0px;
}



Боремся с IE



Применяем хак для IE, который позволит использовать display:inline-block для блочных элементов (а также хак для FF2):

.child {
    ...
    display:-moz-inline-box;
    display:inline-block;
    vertical-align:middle;
    zoom:1;
    //display:inline;
}
.helper {
    ...
    display:-moz-inline-box;
    display:inline-block;
    vertical-align:middle;
    height:100%;
    width:0px;
    zoom:1;
    //display:inline;
}



Результат



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

комментарии (42)

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

    только одно не понял: зачем в примере margin-left: -1px у helper?
    • Ой, это глюк, сейчас уберу
  • И работает способ только тогда, когда указана высота парента. А в IE при ширине содержимого больше парента имеем неприятность наблюдать увеличение высоты в два раза, так как съезжает хелпер.
    • Извините, а когда высота парента не указана, данная проблема вообще стоит? ;)
      • Стоит. Например в многоколоночной вёрстке, где общая высота зависит от высоты каждой колонки. На таблицах элементарно, а таким приёмом не воспользуешься.
        • Бывают случаи, когда самое время воспользоваться таблицами. Мне кажется, это один из них.
          • А бывает, флоаты влияют на высоту. И таблица не удовлетворяет требованиям.
          • Тогда уж display: table, так как таблицы 1) принуждают к определенному порядку данных в исходнике 2) нельзя сделать версию для печати/КПК 3) уродливы и устарели 4) несемантичны 5) непонятно, как разделить в css презентационные таблицы и используемые для верстки, т.е. задаешь например border и padding — и он применяется ко всем таблицам сразу :((

            Правда, с display: table-cell тоже есть подвох: не работает свойтво margin. Я вообще не понимаю, как на сегодняшних убогих и плохо поддерживаемых стандартах типа css вообще можно что-то верстать, кроме простого текста из нескольких абзацев. HTML — по прежнему язык для верстки научных отчетов CERN, ничего не поделаешь ((

            p.s Простой пример: у страницы есть шапка (допустим, фиксированный размер в em), а в нижней, пустой части страницы ровно посередине пустого пространства надо разместить блок (пусть даже фиксированной высоты для простоты). И как это сделаешь? Да никак, без извратов типа яваскрипта (таблицы не годятся, так как шапка должна в коде идти после контента). Ах да. когда используешь яваскрпт — должен ставить обработчик onresize, так как в процессе отображения страницы может измениться размер шрифта или ширина окна :((
            • Я бы поспорил с Вами насчет display: table.
              1) Это — реальный минус таблиц. Порядок данных в исходнике действительно становится сложнее менять.
              2) С этой частью не совсем соглашусь. Как минимум версию для печати можно сделать без особенных проблем. В самом худшем и редком случае часть данных придется дублировать.
              3) Это не аргумент
              4) Тэг, выделенный специально под табличные элементы на порядок семантичнее, чем слой с табличными CSS свойствами. Особенно не стоит забывать, что другого (предусмотренного стандартом!) способа сделать колонки равной высоты попросту нет.
              5) Чем вас не устраивает разделение элементов классами? Пусть у layout-таблицы будет соответствующие классы на table и td элементах.

              Кстати, насчет margin слышу впервые. Это действительно так? Это во всех браузерах? Что на этот счет говорят стандарты?
              • 3) Это еще какой аргумент. В шаблонах все эти table, tr, особенно, разнесенные по отдельным файлам, выглядят уродливо. Я не могу с таким кодом работать (( Я как-то столкнулся с шаблоном (Astra CMS, админка :) ), где было 4(!) вложенных таблицы для верстки. И да, кто-то убрал выравнивание в исходнике :) Представьте себе мое возмущение!

                4) Нет, не семантичнее. table — это таблица в контенте, и никак иначе. Для layout я использую div.

                5) Тем, что эти классы надо прописать руками у каждого (!) элемента table, tr, td, более того, у layout таблицы надо в css сбросить те стили (border, padding), что прописаны для обычных таблиц — это же вообще плохо.

                > Кстати, насчет margin слышу впервые. Это действительно так? Это во всех браузерах? Что на этот счет говорят стандарты?

                Что у ячеек, рядов, колонок и их групп (и соотв-но элементов с display:table*) нет margin (зато есть padding). В принципе это логично, нельзя выдрать куда-то ячейку из таблицы, но при попытке вставить в существующую верстку display:table здорово обламывает.
                • По четвертому пункту вопрос: как именно вы с помощью div делаете колонки? Позиционированием, через JS или float?
                  • В основном флоатами, есть разные техники, напрример через padding-bottom: 32000px/margin-bottom; -32000, faux columns (не пользовался), и ниже в комментах ссылка на хороший способ: chikuyonok.ru/2009/06/float-columns/
            • А что мешает использовать абсолютное позиционирование в примере с шапкой?
              • Вероятнее всего, проблема там именно в том, чтобы расположить по центру оставшегося пространства блок с контентом (даже фиксированной высоты). Потребуется какой-то контейнер, автоматом растягивающися от нижней границы шапки (фиксированной высоты) до нижней кромки экрана. Как вариант — позиционировать блок с контентом просто по центру экрана, а уже после этого сдвигать его содержимое вниз относительным позиционированием на величину равной высоте шапки, чтобы в результате блок отображался именно по центру оставшегося пространства.
                В целом, я частично соглашусь с egorinsk — CSS нередко не в состоянии предложить простой и интуитивный способ решения некоторых естественных задач.
                • Вот, здравое решение, это правда старая задача (решал вроде яваскриптом :) ), но все же.
              • Тот факт, что центрированный таким способом контент выпадает из потока, и не расталкивает страницу (и футер, который я не упомянул, но который там очевидно есть, кстати прибитый к низу :) ) вниз, если он большой по высоте.
            • Сказали как отрезали!
              1) display:table точно так же принуждает к определённому порядку как и таблицы;
              2) Вообще-то можно (display:block, voila!), хотя без таблиц, конечно, удобней;
              3) Таблицы как были так и есть, без них таблицы толком не сверстаешь;
              4) Для таблиц семантичны. Дивы особой семантичностью тоже не страдают. Да и приведите пример, где это вам понадобилось.
              5) Вы верстать умеете? Классы и id вас в помощь: www.w3.org/TR/CSS21/selector.html#class-html

              А вот с display:table-cell есть подвох, что без display:table-row в файрфоксе всё поедет, что случается время от времени.

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

              «…таблицы не годятся, так как шапка должна в коде идти после контента» — почитали бы тот же хабр хотя бы: habrahabr.ru/blogs/css/67493/
              • > 2) Вообще-то можно (display:block, voila!), хотя без таблиц, конечно, удобней;

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

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

                Эх, еще бы поддержку :after и :before в ИЕ6/7 — так как из-за всеми любимого браузера постоянно приходится городить лишние элементы ((

                > 3) Таблицы как были так и есть, без них таблицы толком не сверстаешь;

                Я, что против табличных данных? Я против использования таблиц для layout :) Конечно, таблица выпадения осадков за месяц верстается через table :)

                > «…таблицы не годятся, так как шапка должна в коде идти после контента» — почитали бы тот же хабр хотя бы: habrahabr.ru/blogs/css/67493/

                О, вот это интересный пример, спасибо :)

                p.s. А вот еще один пример дебильности современного css и поддержки в браузерах: представьте себе, что на странице есть див (min-height: 100%, overflow: hidden), в нем второй див с контентом и padding-bottom: 1000px/margin-bottom: -1000px (чтобы фон в этом диве тянулся до конца страницы). Представьте, что во втором диве в контенте есть анкор (элемент с определенным id). Что будет, если открыть страницу с ссылкой вида page#id?

                Логичный ответ — страница (точнее, viewport) прокручивается до элемента id. А в реальности? Никогда не поверите. В Chrome и Firefox прокручивается первый (внешний) див с overflow:hidden, причем назад прокрутить его никак нельзя, и часть контента (над элементом id) становится невидима.

                Победить средствами css —невозможно. Причина поведения — то ли подгон css 2.1 под поведение IE, то ли подгон браузеров под Acid 2, подробности тут: www.positioniseverything.net/articles/onetruelayout/appendix/equalheightproblems#linking

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

                  Про прокручивание верю, наблюдал неоднократно, но зачем городить padding-bottom: 1000px/margin-bottom: -1000px? chikuyonok.ru/2009/06/float-columns/

                  В вашем случае всё типично: ругаете CSS, не умея пользоваться, то есть по сути на своё неумение. Научитесь мыслить конструктивно.
                  • Да в моем примере проще, там одна колонка, просто надо внутренний див растянуть на 100% высоты родительского (точнее, чтобы фон и border left/right внутреннего элемента тянулись на всю страницу по высоте), и тут городить схему с флоатами — имхо больше проблем несет, чем тупо добавить яваскрипт из 2 строчек (или вообще, плюнуть на все и нарисовать бордеры внутреннего элемента фоновой картинкой на внешнем элементе).

                    Ах да, display:table-cell не катит, т к не действуют маргины, а они там используются и на внешнем, и на внутреннем элементах (как и border, и паддинг), и паддингом не заменяются.

                    В общем, вот пример: egor.000space.com/hate-opera/test.html#p3

                    Без #p3 — текст виден целиком.
                    • Вы внимательно читали? Там главная идея именно во вложенных элементах, а флоаты — объект применения.

                      Насчёт вашего примера: вы слышали про схлопывание отступов? www.w3.org/TR/CSS21/box.html#collapsing-margins
                      Подсказка: #inner{overflow:hidden}, ну и body{margin:0;padding:0} не помешает.
                      • > Насчёт вашего примера: вы слышали про схлопывание отступов?

                        Да конечно, слышал, я обычно ставлю padding-top: 1px/margin-top: -1px или что-нибудь вроде этого, в примере для упрощения решил забить на это.

                        > По поводу 100% высоты, почему не повесить оформление на #outer со min-height:100% или вообще даже на body?

                        Есть ограничение на max-width для контента, но кроме того, должен быть маргин минимум 57px, border, серый фон и padding 57px — вот для того тут и 2 дива, одним не обойтись. Соотвественно, внешний див (outer) нужен для ограничения ширины, задания минимальной высоты и прибитого футера (в примере нет для простоты), а внутренний — задает отступы и фон с бордером.

                        Без внутреннего дива, если ширина экрана < 800px, справа и слева от внешнего дива нет маргинов (т к там margin: 0 auto). Ставить паддинги/маргины на body/html нельзя, т к в исходной верстке (пример упрощен) есть элементы с шириной 100%.

                        Делать фон картинкой на #outer — не очень хочется имитировать border лишней картинкой, их и так в оригинальной верстке уже штук 10, даже с учетом спрайтов :((

                        Пока решение — тупо яваскриптом ставить на #outer scrollTop = 0 :(( Или же добавлять еще пару дивов и display:table-cell/height: 100%.
                        • > Ставить паддинги/маргины на body/html нельзя, т к в исходной верстке (пример упрощен) есть элементы с шириной 100%.
                          Не понял. На 100% body? А как же тогда футер прибивается? Если нет, то что мешает? Доктайп стоит.
                        • Более того, кажется вы забыли что мы обсуждаем: можно сделать
                          body { text-align: center }
                          #outer {
                          display: inline-block;
                          height:100%;
                          max-width: 800px;
                          всякое оформление с краями, границами и отступами
                          text-align:left;
                          }
                          На Firefox 2 тоже в принципе можно уже забить developer.yahoo.com/yui/articles/gbs/
                          • Гм, inline-block не рассматривал, подумаю, хотя конечно извратище то еще :)

                            >> Ставить паддинги/маргины на body/html нельзя, т к в исходной верстке (пример упрощен) есть элементы с шириной 100%.
                            > Не понял. На 100% body? А как же тогда футер прибивается? Если нет, то что мешает? Доктайп стоит.

                            Гм, я про ширину а не высоту. Контент —ограничен по ширине, а вот например футер или шапка (котрых в примере нет) наследуют 100% ширины body, я про это. Футер прибивается установкой отрицательной margin-top, он идет после #outer (у которого 100% min-height).

                            В любом случае, спасибо, теперь у меня намного больший выбор всяких вариантов :)
                            • «Контент —ограничен по ширине, а вот например футер или шапка (котрых в примере нет) наследуют 100% ширины body»
                              Про это и спрашивал. Но нельзя разве поставить падинг слева и справа у body, а у шапки с футером отрицательный маржин на тот же размер? А на outer тогда вешается и максимальная ширина и оформление.
                              • Ну да, вообще это вариант, хотя конечно как-то странно стаивть паддинг, чтобы потом у детей его отменять назад. Надо будет подумать :)

                                И все это, лишь бы избежать необходимость делать вложенный див! Вот уж язык, изврат на изврате.
                    • По поводу 100% высоты, почему не повесить оформление на #outer со min-height:100% или вообще даже на body? (На неподдержку IE5 уже можно забить.)
            • «Правда, с display: table-cell тоже есть подвох: не работает свойство margin» — конечно не работает: у ячеек таблицы его просто не может быть. Если он «работает» где-то без флоата (у которого приоритет выше чем у любых display кроме none), то это баг.
      • помимо уже приведенных примеров, у parent может быть position: absolute; top: 0; bottom: 0;
        • Да, ваш пример имеет место. Но, имхо, он не совсем типичен и для него можно найти свой способ выровнять по вертикали.
  • Поздравляю. Даже не знаю, нужно ли что-то добавлять. Хотя добавлю, что во втором фф и в ие6 достигнутый вами результат не работает при переносе строк.
    • Да) Способы очень похожи, но всё-таки не совсем идеинтичны. Так что :-)
      • Ну да, не идентичны, там стили в другом порядке, здесь другой способ придания inline-block эксплореру. Совсем разные.
  • раскрыть комментарий
  • Подскажите, как можно выровнять div по центру страницы вертикально и горизонтально?
    Пока решил эту проблему с указанием отрицательных отступов в половину длины и высоты в пикселях, но хочу найти универсальное решение.
    • а чем предложенный вариант не подходит? ;)
      • Как задать высоту и длину резиновой странице?
        • ну… 100%, например ;)
          • Не помогает, думаете я не пробовал?)
            • Вобщем, надо смотреть макет и по ситуации верстать. Так универсально не скажешь
  • Данный способ показался оптимальным, однако столкнулся с проблемой при размещении блока яндекс директа. Проблема в google chrome

    Посмотреть живьём можно тут: bruneta.ru (на шапке справа)

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