CSS-live.ru

Равномерное выравнивание блоков по ширине

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

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

Как это работает?

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

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

Цифрой 1 на картинке отмечены обычные инлайн-боксы, т.е. попросту текст или инлайн элементы, такие, например, как < span> или <em>.
Под цифрой 2 у нас находится элемент строчно-блочного уровня, т.е inline-block. Как можно заменить, алгоритм отступов внутри него рассчитывается заново. Причина в том, что внутри себя inline-block генерирует свой собственный контекст форматирования. Что нельзя сказать об обычном inline элементе, внутри которого межсловное расстояние распределялось бы, по общему, внешнему алгоритму.
Цифрой 3 отмечена обычная картинка. Так же, как и остальные, она является строчным, целым элементом.
Для нашей строки все эти вещи представляют из себя отдельные сущности, неразделимые слова, единые целые. А расстояния между ними как раз и регулируется нашим механизмом, под названием text-align: justify
*Последняя же строка не попадает в поле зрения justify, так как он работает только для целиком заполненных строк, а в последней строке пробелы всегда остаются своего обычного размера.

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

Отсюда можно сделать вывод, что сейчас мы имеем общую сумму всех пробельных зон в строке, которая равна 116px.

Этап третий — завершающий
Третьим и последним этапом алгоритма будет деление полученной цифры (в данном случае 116) на количество пробелов в строке (в нашей строке их 7). Из полученного результата (16.571) вычитается ширина одного пробела и уже это значение добавляется к каждому из них. Что в итоге даёт равномерное распределение отступов во всей строке.

Итог
Как мы можем видеть, алгоритм работы text-align: justify не так уж и сложен на самом деле, всё вроде бы понятно и логично. Я уверен, что каждый из нас, решая подобную задачу, высчитывал бы результат, по точно такому же сценарию. Сложность заключается только в том, чтобы написать хорошее, красивое, а главное качественное решение, затратив при этом минимум материала. Ведь существует много подводных камней, в виде последней (не попадающей под алгоритм) строки, которую нужно прятать, или каким либо образом выравнивать точно так же, как и все остальные. Ну и естественно нельзя забывать об интерпретации кода, таких браузеров, как Opera, IE6-7, которые любят преподносить неожиданные сюрпризы.

Наша задача

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

Вариант 1

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

<ul>
	<li class="left">
		<div class="content"></div>
	</li>
	<li class="left">
		<div class="content"></div>
	</li>
	<li class="right">
		<div class="content"></div>
	</li>
	<li class="right">
		<div class="content"></div>
	</li>
	<li class="center">
		<div class="content"></div>
	</li>
</ul>

А CSS таким

ul {
	font: 14px Verdana, Geneva, sans-serif;
	overflow: hidden;
}
	ul li {

	}
	ul li.left {
		width: 20%;
		float: left;
	}
	ul li.right {
		width: 20%;
		float: right;
		text-align: right;
	}
	ul li.center {
		text-align: center;
	}
	ul li .content {
		background: #E76D13;
		width: 98px;
		height: 98px;
		display: inline-block;
		text-align: left;
		border: 1px solid #000;

		/* эмуляция inline-block для IE6-7*/
		//display : inline;
		//zoom : 1;
	}

Что мы собственно сделали? А сделали мы следующее. Два левых и два правых пункта мы раскидали по разным сторонам с помощью float : left и float : right соответственно, при этом назначив им по 20% ширины каждому, что в сумме дало 80% от всей ширины контейнера. Последний пункт, я решил не делать обтекаемым, так как его содержимое само по себе занимает всё оставшиеся пространство после float-блоков.
Соответственно для такого решения пришлось пожертвовать разметкой, а точнее дополнительными классами + внутренними контейнерами для контента. Как вы могли заметить, каждый из этих контейнеров я сделал строчно-блочным, повесив на них display : inline-block, благодаря чему мой контент смог выравниваться в любую сторону с помощью text-align у родителя. Эти "жертвы" с большой натяжкой можно было бы отнести к хорошему решению, если бы не некоторые, весомые "но".
Во-первых, как видно из скриншотов, равномерные отступы имеют только боковые пункты, а между средним и остальными есть явная разница. И чем больше ширина контейнера, тем заметнее это становится.
Во-вторых, ради эксперимента, я назначил второму пункту ширину, равную 200px.

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

Вариант 2

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

<ul>
	<li class="one">
		<div class="content">1</div>
	</li>
	<li class="two">
		<ul>
			<li>
				<div class="content">2</div>
			</li>
			<li>
				<div class="content">3</div>
			</li>
			<li>
				<div class="content">4</div>
			</li>
			<li>
				<div class="content">5</div>
			</li>
		</ul>
	</li>
</ul>
ul {
	font: 14px Verdana, Geneva, sans-serif;
	overflow: hidden;
}
	ul li.one {
		float: left;
	}
	ul li.two {
		overflow : hidden;
		float : none;
	}
	ul li.two li {
		width: 25%;
		text-align: right;
		float: left;

		/* Лекарство для IE6-7*/
		//width: 24.9%;
	}

	ul li .content {
		background: #E76D13;
		width: 98px;
		height: 98px;
		display: inline-block;
		text-align: left;
		border: 1px solid #000;

		/* эмуляция inline-block для IE6-7*/
		//display : inline;
		//zoom : 1;
	}

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

Левая колонка прижата к левому краю и содержит в себе самый первый, одиночный блок. Это нужно для того, чтобы правая часть прижималась вплотную с правой стороны и растягивалась на всё оставшееся место. Растяжка правой части происходит благодаря overflow : hidden, который создаёт свой контекст форматирования, т.е. по сути свой собственный, независимый контейнер. Для всех дочерних элементов этого контейнера его ширина составляет 100% и поэтому блоки внутри него мы растягиваем в соответствии с этим правилом. Четыре блока имеют ширину 25%, что в сумме даёт 100. На изображении можно увидеть, как расположены эти блоки. Видно, что строчно-блочные элементы с контентом внутри них выровнены по правому краю с помощью text-align : right, благодаря чему самый правый блок прижат к своей боковой стенке, так же как и левый.

Благодаря таким "жертвам" с двумя колонками, мы получили отличный результат, и как видно из первых рисунков, при разных разрешениях, расстояние отступов между блоками остаётся абсолютно одинаковым. Это происходит за счёт равнозначных блоков в правой части. Ширина каждого из них составляет 25%, а элемента внутри них — 100px. Что и даёт равные отступы слева от каждого "кирпичика" в отдельности.
На данный момент можно смело заявить, что при фиксированной ширине блоков у нас получилось именно то, что мы хотели.

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

ul li.one .content { width: 150px;}

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

Эх, а вот этот эксперимент потерпел неудачу. Правый отступ у самого левого блока оказался меньше, чем все остальные. Всё легко объясняется тем, что контент первого элемента в правой части, стал больше на 50px, а так как он выровнен по правой стороне, то ширина визуально прибавилась к его левой части и соответственно стала отличаться от ширины соседних блоков. Ну и естественно поменялся отступ, который стал меньше ровно на 50px.

Из всего этого можно сделать вывод, что данный метод явно лучше, чем его предшественник и то, что он имеет право на жизнь. Единственный его недостаток связан с тем, что мы не может задать разную ширину блокам, и должны придерживаться одинаковой. Так что, если у вас есть список, в котором все пункты имеют равную ширину, то вы вполне можете применять этот подход. Конечно же, нужно понимать, что без двухколоночной структуры тут не обойтись + нужна обязательная минимальная ширина у контейнера (min-width), иначе при маленькой ширине экрана блоки будут наезжать друг на друга.
* Кстати, у себя в примере, в правом контейнере я использовал четыре блока по 25%, и их общая сумма составила 100%. Поэтому нужно помнить, что если блоков будет, ну скажем 6, то ширина каждого из них, будет равна 16.666, что говорит о дробных процентах, которые, например, не поддерживаются в браузере Opera.
Соответственно результат в этом случае будет немного не тем, чем вам хотелось бы.

Вариант с двумя колонками

Вариант 3 — text-align: justify

Стоит отметить, что до этого момента, ни в одном примере, мы ни разу не воспользовались text-align: justify, даже не смотря на то, что именно на основе его алгоритма и строятся все наши решения. Я предлагаю нарушить эту традицию и, наконец, пустить уже в дело этого зверя.

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

<ul>
	<li>1</li>
	<li>2</li>
	<li>3</li>
	<li>4</li>
	<li>5</li>
	<li class="helper"></li>
</ul>
ul {
	font: 14px Verdana, Geneva, sans-serif;
	text-align: justify;
}

	ul li  {
		background: #E76D13;
		width: 98px;
		height: 98px;
		display: inline-block;
		text-align: left;
		border: 1px solid #000;

		/* эмуляция inline-block для IE6-7*/
		//display : inline;
		//zoom : 1;
	}
	ul li.helper {
		width: 100%;
		height: 0;
		visibility: hidden;
	}

Из кода ясно, что мы создали список с пятью основными и одним элементом — помощником. text-align: justify на главном элементе-родителе (ul), заставляет своих потомков подчиняться своему алгоритму. Ведь, как известно, это свойство работает с текстом, а так как наши строчно-блочные (display: inline-block) пункты, по сути и являются неразрывными словами в строке, то это поведение вполне закономерно. Кстати, стоит учесть, что text-align: justify наследуемое свойство, поэтому text-align: left на ближайших потомках — необходимая мера. Этим самым мы как бы возвращаем выравнивание содержимого наших блоков в прежнее состояние.
В первой части статьи я упоминал, что наш алгоритм не распространяется на последнюю строку, а работает со всеми строками кроме неё. Поэтому я добавил в конец ещё один элемент, пустышку, и растянул его на 100% по ширине, тем самым заставив его растянуться на самую последнюю строчку в списке. С помощью нехитрых приёмов (height: 0, visibility: hidden) я, можно сказать, почти что спрятал его. Почему я сказал  "Почти что"? Об этом чуть позже, для начала давайте взглянем на то, что у нас вышло.

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

<ul>
	<li class="first">1</li>
	<li>2</li>
	<li class="third">3</li>
	<li>4</li>
	<li>5</li>
	<li class="helper"></li>
</ul>

Добавим для них свои правила.

.first { width: 150px;}
.third { width: 200px;}

Проверяем.

Я специально увеличил ширину экрана, чтобы при маленькой ширине блоки не перескакивали на другую строку, как обычные слова в тексте. Но, если посмотреть на результаты алгоритма, то он работает! Пробелы между элементами остаются равнозначными, даже не смотря на то, что у двоих из них, мы увеличили ширину.
Т.е. можно с лёгкостью заявлять, что этот метод, с дополнительным элементом в конце — даёт отличный результат и, если ограничить экран по ширине, то его применение на практике нас не разочарует.
Так, ну а теперь, пожалуй, настало время подлить ложку дёгтя.
Во-первых, как вы могли заметить, во всех примерах я перечислял все браузеры, кроме IE6-7.
Во-вторых, опять же, как вы, наверное могли видеть на рисунках, внизу нашей строки, где дополнительный элемент, есть большие, непонятные отступы.
Давайте разбираться по порядку.

Первая проблема — это проблема IE6-7. Не приводя скриншоты, скажу сразу, что наш метод там не работает. Пункты в этих браузерах прижаты друг к другу вплотную и не растягиваются в строке. Дело в том, что парсер IE6-7 полностью игнорирует закрывающие теги у элементов <li>. А нет тегов — значит нет пробелов между ними, и, следовательно text-align: justify пропускает нашу строку, состоящую по сути из одних "строчно-блочных" слов, прижатых друг к другу.

За решением данной проблемы мы отправляемся на самую высокую гору… на MSDN. Мда… найти что либо на этом сайте, представляет большую трудность. Но, всё же повозившись, некоторое время, решение было найдено! text-justify: newspaper — свойство для увеличения или уменьшения интервалов между буквами и между словами. MSDN заявляет, что эта вещь "Самая навороченная форма выравнивания для латинского алфавита", а вот в этой статье ещё есть и дополнение, что для арабских текстов это свойство вытягивает и сами буквы.
Отлично, нам как раз нужно что-нибудь наподобие. Давайте-ка проверим их слова на деле.

ul {
	font: 14px Verdana, Geneva, sans-serif;
	text-align: justify;

	/* Лекарство для IE6-7*/
	text-justify: newspaper;
}

	ul li  {
		background: #E76D13;
		width: 98px;
		height: 98px;
		display: inline-block;
		text-align: left;
		border: 1px solid #000;

		/* эмуляция inline-block для IE6-7*/
		//display : inline;
		//zoom : 1;
	}
	ul li.helper {
		width: 100%;
		height: 0;
		visibility: hidden;
	}
	.first { width: 150px;}
	.third { width: 200px;}

А вот и результат.

Победа! IE6-7 были побеждены их же оружием. Здорово. Теперь во всех браузерах, начиная с IE6, наш способ работает идеально. Выходит, что MSDN не подвели и их слова подтвердились на деле. Так что text-align: justify выручил нас, поэтому берём его на заметку и переходим к решению второй проблемы.

Вторая проблема связана с непонятным отступом между низом списка и нашей строкой с элементами. В чём же дело? А дело в том, что всё вполне закономерно и этими странными отступами являются ни кто иные, как межстрочный интервал (line-height) и размер шрифта (font-size), которые априори присутствуют у строк и букв в тексте. Наши элементы — блочные только внутри, а строчные снаружи, поэтому они и попадают под эти правила.
Как быть? А легко! Мы можем повесить на контейнер обнуление этих стилей, а уже у потомков восстановить их в прежнее состояние. Пробуем.

ul {
	font: 14px Verdana, Geneva, sans-serif;
	text-align: justify;

	/* Обнуляем для родителя*/
	line-height: 0;
	font-size: 1px; /* 1px для Opera */

	/* Лекарство для IE6-7*/
	text-justify: newspaper;
}

	ul li  {
		background: #E76D13;
		width: 98px;
		height: 98px;
		display: inline-block;
		text-align: left;
		border: 1px solid #000;

		/* Востанавливаем у потомков, кроме последнего*/
		line-height: normal;
		font-size: 14px;

		/* Без него в Opera будет отступ под элементами */
		vertical-align: top;

		/* эмуляция inline-block для IE6-7*/
		//display : inline;
		//zoom : 1;
	}
	ul li.helper {
		width: 100%;
		height: 0px;
		visibility: hidden;
		overflow: hidden;
	}
	.first { width: 150px;}
	.third { width: 200px;}

Результат.

До обнуления

После обнуления

Отлично! Всё получилось. Обнуление стилей у главного контейнера помогло нам. Единственное, что теперь стоит помнить, это то, что в связи с обнулением размера шрифта мы не сможем задать нашим пунктам шрифт в таких единицах длины, как em, а так же % для <body> не принесут желаемого результата. Но, а целом, наш метод работает идеально, так что можно подводить итоги и идти дальше, ведь нам же мало одного решения, нам же нужно больше и лучше, не правда ли?

Подводя промежуточные итоги, скажу, что данный подход пока что лидер среди присутствующих до сей поры решений, и что, я лично, не вижу в нём ни одного изъяна, правда, кроме… Кроме дополнительного элемента в конце строки, плюс, как мне кажется, неактуальные проблемы с % и em. Но, эти натянутые минусы, никак не могут быть причиной отказа от представленного варианта. Так что смотрим результат и идём дальше.
Вариант дополнительным элементом

Вариант 4 — Избавление от дополнительного элемента

В предыдущем варианте для нашей цели мы использовали дополнительный элемент, ставя его в конец списка. С одной стороны, конечно же, этот маневр принес свои плоды, но с другой… но с другой стороны создал некие неудобства. Например, при динамическом добавлении пунктов, вспомогательный блок будет только мешать, да и потом этот "хвост" портит нашу структуру, засоряя её. В этом варианте, я предлагаю избавиться от него, не испортив при этом решение.
В CSS2.1 уже давно есть такие вещи, как псевдоэлементы. Это такие абстрактные сущности, которые могут быть сгенерированы каким нибудь элементом и вставлены в его начало или конец. Почему бы не заменить таким псевдоэлементом наш лишний вспомогательный блок? Давайте попробуем…

<ul>
	<li>1</li>
	<li>2</li>
	<li>3</li>
	<li>4</li>
	<li>5</li>
</ul>
ul {
	font: 14px Verdana, Geneva, sans-serif;
	text-align: justify;

	/* Обнуляем для родителя*/
	line-height: 0;
	font-size: 1px; /* 1px для Opera */

	/* Лекарство для IE6-7*/
	text-justify: newspaper;
	zoom:1;
}
	ul:after {
			width: 100%;
			height: 0;
			visibility: hidden;
			overflow: hidden;
			content: '';
			display: inline-block;

	}
		ul li  {
			background: #E76D13;
			width: 98px;
			height: 98px;
			display: inline-block;
			text-align: left;
			border: 1px solid #000;

			/* Востанавливаем у потомков, кроме последнего*/
			line-height: normal;
			font-size: 14px;

			/* Без него в Opera будет отступ под элементами */
			vertical-align: top;

			/* эмуляция inline-block для IE6-7*/
			//display : inline;
			//zoom : 1;
		}

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

Здорово! Трюк с псевдоэлементом сработал. Теперь наша разметка чистая, аккуратная и без лишнего "мусора". Нам удалось избавиться от дополнительного элемента, полностью заменив его сгенерированным.
Но, как обычно, не обошлось без IE6-7 приключений. К сожалению, в браузерах IE6-7 нет поддержки :after и :before. А если нет поддержки, следовательно и нет никаких вспомогательных блоков, а значит и растягивать попросту нечего. Поэтому картина в этих браузерах такова.

Как видно из скриншота, IE6-7 полностью игнорирует пробелы между блоков, прижимая их вплотную друг к другу. Даже не смотря на то, что мы уже задействовали тяжёлую артиллерию в виде text-justify: newspaper. Почему же это происходит? Давайте выясним.
Оказывается всё дело в том, что text-justify: newspaper лишь даёт возможность растягивать наши буквы (inline-block), но не команду. Проще говоря, он рассказывает строке, как бы он хотел, чтобы она была растянута, а text-align: justify является растягивающей силой. Т.е. text-align: justify отвечает за растяжение строки, а text-justify: newspaper лишь уточняет, как именно это растяжение будет происходить.
Да, но тогда возникает вопрос: "Если есть и возможность и сила, которая может её исполнить, то почему же тогда ничего не происходит?". Ответ кроется в самом свойстве text-align: justify. Если вы помните, в обсуждении его алгоритма я упомянул о том, что он не распространяется на последнюю строку в тексте. А так как мы убрали вспомогательный элемент в конце списка, то соответственно наша строка (пускай даже она одна) с блоками — стала уже последней, и следовательно алгоритм отказался с ней работать.

Как же быть? Есть ли выход?
К счастью мир не без добрых людей, и один такой добрый человек направил меня на верный путь. Оказывается решение было у меня под носом. text-align-last — свойство, которое включает алгоритм text-align: justify в самой последней строке текста, если к ней применён этот самый text-align: justify. Проще говоря, когда мы применяем к тексту обычный text-align: justify, то, видя это, text-align-last указывает первому на то, что он поступает плохо и что ему придётся теперь работать и в последней строчке тоже.
Самое интересное, что это свойство считается ориентированным именно на Internet Explorer, в котором он нам как раз и нужен). В общем пора переходить к делу.

ul {
	font: 14px Verdana, Geneva, sans-serif;
	text-align: justify;

	/* Обнуляем для родителя*/
	line-height: 0;
	font-size: 1px; /* 1px для Opera */

	/* Лекарство для IE6-7*/
	text-justify: newspaper;
	zoom:1;

	/* Включаем в работу последнюю строку*/
	text-align-last: justify;
}
	ul:after {
			width: 100%;
			height: 0px;
			visibility: hidden;
			overflow: hidden;
			content: '';
			display: inline-block;

	}
		ul li  {
			background: #E76D13;
			width: 98px;
			height: 98px;
			display: inline-block;
			text-align: left;
			border: 1px solid #000;

			/* Востанавливаем у потомков, кроме последнего*/
			line-height: normal;
			font-size: 14px;

			/* Без него в Opera будет отступ под элементами */
			vertical-align: top;

			/* эмуляция inline-block для IE6-7*/
			//display : inline;
			//zoom : 1;
		}

Да! У нас получилось. text-align-last: justify сделал то, что от него требовалось, и сделал это на 5 баллов. Алгоритм включился в работу, в нашей последней и единственной строке. Так что празднуем победу и подводим итоги этого способа.

Ну что ж, я доволен. Доволен тем, что нам удалось найти действительно достойное решение. Причём не просто найти, а разобраться во всём и довести его до абсолютной кроссбраузерности, затратив минимум кода и не засоряя разметки. На лицо одни плюсы, а что же с минусами? Как по мне, так их попросту — нет. По сравнению с предыдущим вариантом, в этом мы только улучшили результаты. Разве что…

Единственное, что теперь стоит помнить, это то, что в связи с обнулением размера шрифта мы не сможем задать нашим пунктам шрифт в таких единицах длины, как em, а так же % для <body> не принесут желаемого результата.

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

Резюме

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

Все варианты воедино

1. Вариант с разносторонним выравниванием (К сожалению неработающее решение. Надеюсь, что в комментариях кто нибудь натолкнёт на верный путь)
2. Вариант с двумя колонками (Работающее решение, но только с фиксированными по ширине блоками)
3. Вариант дополнительным элементом (Работающее решение)
4. Вариант с псевдо-элементом (Работающее решение)

Обновление от 08.10.2014

Вариант 5 — используем новые технологии

5. Вариант с использованием флексбоксов и постепенным улучшением

См. отдельную статью

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

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

  1. Прекрасно. На хтмлбуке видел это решение, но без последних важных и полезных замечаний под ие6/7( text-align-last: justify;).
    Спасибо, что в своем блоге не публикуешь общеизвестные и ширпотребные решения различных проблем, а рожаешь вот такие вот замечательные и подробные с объяснением логики работы статьи.
    Кстати, другая твоя статья про inline-block с объяснением всех возможных косяков и проблем, заставила переосмыслить использование float в принципе в пользу строчно-блочного метода.

    1. Спасибо. Но, благодарить можно не только меня, а ещё и моего дружищу Илюху (aka SelenIT). Так что я с ним поделюсь)))

      Рад, что статья понравилась. Но, на счёт вот этого:

      Кстати, другая твоя статья про inline-block с объяснением всех возможных косяков и проблем, заставила переосмыслить использование float в принципе в пользу строчно-блочного метод

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

      1. Нужно всегда смотреть по ситуации и выбирать методы исходя из задачи.

        О, я это прекрасно понимаю, просто раньше я редко использовал inline-block, где он был нужен, из-за непонятных отступов в разных браузерах, часть из которых сам победил, а некоторые так и остались загадкой до недавнего времени. В частности, спасибо за ul{ display:table } для сафари:)

      1. Вот пример, который вы предлагаете например

        1

        Вот тут использование блочного элемента Div в Li, да и вообще в Li нельзя вставлять блоки Div, не семантично это ^^, о семантичности очень хорошо рассказывает Pepelsbey — http://pepelsbey.net/2008/04/semantic-coding-1/

        1. Да нуу, глупости какие-та. БЭМ — это ни разу не симантика, а поверьте, он похуже чем просто «Div в Li». Главное, чтобы задача решалась, а не валидность и семантика.

          1. Это не критично, ну я все же стараюсь избегать подобного, и использовать только тогда, когда уже нету выбора, а за статью еще раз Спасибо!, задумался о display-inline в серьез, так как обычно списки все делают обтеканием с помощью float и камней там не намного меньше. Хотел еще спросить Вас, про display: table; насколько я понял, ширину он делает auto, а если нам надо будет к примеру сделать по центру? Пробовал задавать ширину в px не работает, срабатывает только если 100 % ставим, что посоветуете?

        2. Див — семантически стерильный элемент, у него нет никакой своей семантики (да и у списков ее немного, по большому счету). Его можно вставлять куда угодно, где допустимы блоки текста и где (пока) вынужденно необходима обертка для оформления. HTML5-доктора (http://html5doctor.com/you-can-still-use-div/) разрешают!

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

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

                Я все же постараюсь найти ту статью, если не найду, признаю что был не прав, со всяким бывает ^^

            1. Вообще рациональное зерно есть, только, по-моему, необходимость вставить див в список скорее повод задуматься, а нужен ли в этой задаче _список_ :). Но это всё не догма. Во всех ситуациях нужно руководствоваться здравым смыслом и целесообразностью, а не фанатизмом и категоричными лозунгами. Собственно, само понятие «семантика» от слова «смысл» и происходит :)

              1. Cогласен с Вами, ну раньше в 2006-2008 годах, много чего не поддерживалось, и большинство статей думаю составлялось на основе того, что поддерживалось, и возможно, то что сейчас поддерживается именно тогда и не входило в этот список.

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

  2. Отличная статья, Макс. Правда отличная. Спасибо, что сделал чуточку профессиональнее каждого, кто это прочитал.

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

  4. Хорошее решение, спасибо.

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

    Есть ли идеи на счет решения без сбрасывания размера шрифта?

    P.S. Я бы еще упомянул что при вынимании текстовых нод между блоками, блоки эти приятно слипаются вместе

    1. Пожалуйста. Рад, что смогли помочь.

      Есть ли идеи на счет решения без сбрасывания размера шрифта?

      Да, возможно с помощью этого способа можно решить проблему:
      http://css-live.ru/articles-css/zagadochnye-otstupy-mezhdu-inlajn-blokami/comment-page-1/#comment-106

      Кстати, нет желания помочь с переводами статей на блоге?

  5. к сожалению вариант 4 у меня не работает в linux opera 11.61(сборка 1250). нельзя ли что-нибудь придумать или хотя бы определить причину?

    на странице
    http://h31590.srv5.test-hf.ru/raja/
    я применил «вариант 4» в блоке .icons

      1. Спасибо за статью :)
        Этот вариант разъезжается в опере не только под маком и линухом, но так же и под виндой.
        Как бы простое удаление строки: font-size: 1px; помогает и в вебките всё хорошо.

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

    Как-то интересовалась этим вопросом здесь: http://www.xiper.net/collect/html-and-css-tricks/navigation/rubber-menu.html и здесь: http://www.xiper.net/collect/html-and-css-tricks/navigation/rubber-blocks-menu.html, но оно оказалось не универсальным :(

    1. Выпадающие меню можно делать через JS, пришвартовывать их в нужное место, к пунктам.

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

      В последнем варианте не пашет, потому что сделано через «таблицы»

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

        1. С абсолютным позиционированием, по идее, ничего смещаться не должно. Пример проблемного кода сохранился?

          1. Вот ссылка на шаблон: pattern

            Проблему я решила по-своему (без скрипта), но мне мое решение не нравится. Хотелось бы узнать ваше мнение.

              1. позиционтируем абсолютно не задавая left тогда левый край будет там где он был бы еслиб вообще небыл позиционирован.

            1. Ах, вот у вас какой шаблон хитрый — раскрываться должны подпункты всех пунктов сразу… Интересная задачка. На первый взгляд без однозначного решения, т.к. если не сдвигать — избыток ширины будет торчать с другой стороны, налезая на соседний пункт, какие-то издержки неизбежны.

              Изучить код, к сожалению, прямо сейчас не имею времени, но как только оно появится — с удовольствием поразбираюсь!

  7. Я, похоже, понял, в чем дело. Ты, наверное, когда готовил статью к публикации, перепутал строки в CSS, и повесил vertical-align на li вместо псевдоэлемента.

    В общем, если vertical-align: top перенести на ul:after, то никаких отступов не будет, и font-size сбрасывать тоже не придется.

  8. «В данной ситуации мы воспользовались псевдоэлементом :after, который сгенерировали в конце нашего списка.»

    Я что то не могу понять, ведь псевдоэлемент должен был добавится после списка, разве нет?

  9. Ребята есть кроссбраузерное решение для конкретной ширины блоков на все случаи жизни. Зачем все усложнять.
    Итак приступим:

    имеем блок(или экран) в 360пк
    имеем 6 блоков шириной по 100пк
    нужен отступ между блоками 20пк

    1
    основной блок двигаем влево (или вправо — по желанию) на 20пк
    position:relative; margin-left:20px; width: 360px;

    2
    даем отступы нашим блокам по 20 пк слева
    float:left; margin-left: 20px; width:100px;

    и наслаждаемся

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

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

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

  10. Хороший работоспособный способо, спасибо.

    Для пользователей Smarty момент.
    НЕ использовать тег {strip} на данном участке HTML. Этот тег удаляет пробелы, и создаёт из «DOM-лестницы» одну строку. В этом случае не работает Justify.

    1. Мужик, СПАСИБО огромное! Коммент не только для Smarty. Замучался пытаться понять, почему не работает. Все потому, что между элементами не было переноса строки! Полтора часа жизни под хвост коту.

  11. Спасибо огромное! Я почти сделал менюшку с использованием text-align:justify, но не разобрался как заставить это работать в ие7.

    1. Читайте внимательнее статью, именно ту часть, которая описывает, как заставить работать это решение в IE6-7.

  12. Обратите внимание на дну особенность в IE6-IE7. Данным способом можно выровнять даже инлан-блоки, у которых не задана ширина width. Но при этом обязательно нужно для них указывать свойство text-align, значение которого отлично от justify. Если этого не сделать, то из-за того. что свойства text-align и text-align-last наследуемые, то в IE6-IE7 инлайн-блочные элементы растянутся по ширине.

  13. neurontin generic cost
    [url=http://ejkted.webs.com/ ][b]Buy Generic Neurontin (Gabapentin) No Prescription Mastercard Visa [/b][/url]
    [i]Cheapest Generic Neurontin (Gabapentin) In Australia Discount [/i]
    [b]neurontin 800 pill [/b] — http://78ole6js.webs.com/
    [i]neurontin neuropathic pain dosage
    neurontin edema
    neurontin in children
    [/i][url=http://kamagrasoftonlinepharmacy.webs.com/ ][b]Cheap Generic Neurontin (Gabapentin) Overnight [/b][/url]
    [b]neurontin joint swelling [/b]
    29610 >>> http://wellbutringeneric.webs.com/
    [i]Purchase Generic Neurontin (Gabapentin) In Usa No Prescription [/i]
    [u][i]neurontin withdrawal burning [/i][/u]
    [url=http://uiugill.webs.com/ ][b]Buy Cheap Generic Neurontin (Gabapentin) 300/400mg [/b][/url]
    [i]Ozma’s private suite of apartments — hung the famous raise it. When all was in readiness, the King pulled the cord a «I believe it was Jack Pumpkinhead, who is also a farmer,» was the [/i]

  14. Отличный способ растягивания по всей ширине.. но вот есть такое желание … дикое… допустим 3-4 строки этих li.. в последней строке 2 элемента li, когда в строку помещается 5… можно ли последний li как нибудь прилепить к левому краю, да так, чтобы не нарушалась сетка? 

      1. Этот способ знаем, красивый, всегда пользуемся. Но вот хотелось бы чтобы блоки растягивались между собой на всю ширину блока в котором находяться. а в последней строке, если элементов меньше, чем помещается в строки выше они не растягивались на всю ширину , а также были в "сетке". 
        Попробую показать в картинках
        Сейчас по данной статье работает так http://clip2net.com/s/27plv
        А хочется вот так http://clip2net.com/s/27plN

              1. Я нашла свои ошибки. Все получилось! Вы меня направили в нужное русло! Спасибо вам большое.
                Вторая статья действительно помогла добиться нужно результата с двумя и более строками.  ^____^
                 

  15. По поводу кроссбраузерности:
    Opera Mac/Ubuntu
    @media all and (-webkit-min-device-pixel-ratio:10000),

    not all and (-webkit-min-device-pixel-ratio:0) {
    li{ font-size:24px;}
    }
    IE 7/8
    при использовании в ul text-justify-last у li остаётся это значение при text-align:center/text-align:justify, обнулйсяйце хаком  text-justify-last:none
     
     

  16. А как сделать, чтобы для IE картинки вставленные в прямоугольники (блоки) при таком методе центровались внутри и по горизонтали и вертикали????????????????????

  17. А решение в этой статье мне сильно помогло! Спасибо АВТОРУ!!! Только вот с центровкой картинок в блоках li пока не решил для IE!!!!

  18. Это можно делать только с элементами списка li, почему не получается сделать с divами c display:inline-block?

  19. Прочитал статью, предлагаю свое фроссбраузерное решение на JQuery
     
    добавить функцию (и использовать) после $(document).ready(function(){
     
    function justify(el){
       var W = $(el).width(); 
       var w = 0;
       var i = 0;
       var x = 0;
       $(el).children().each(function(){
          w += $(this).width();
          i++;
       });
       x = (W-w)/(i-1);
       $(el).children().css("marginRight", x+"px");
       $(el).children().last().css("marginRight", "0px");
       return;
    }
     
    использование:
    чтобы выровнить элементы внутри блока с id="main_menu" делаем так:
     
    justify("#main_menu");
     
     

  20. единственный нюанс для блока main_menu нужно указать в css свойство font-size:0; так как большенство браузеров генерятпробелы между inline елементами

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

  21. А у меня сработал такой вариант (мне нужно было равномерно распределить пункты меню по всей ширине блока, и я воспользовалась правилом, что строки кода, написанные ниже, переопределяют стиль строк кода, написанных выше):
     
    li  {
         float:left;
         display:inline-block;
         margin-right: 3%;   /*размер отступа можно установить в пикселях, если ширина блока и пунктов меню фиксирована*/
    }
     
    li:last-child  {
         float:left;
         display:inline-block;
         margin-right: 0;
    }
     
    или так (что по сути одно и тоже):
     

    li  {
         float:left;
         display:inline-block;
         margin-left: 3%;  
    }
     
    li:first-child  {
         float:left;
         display:inline-block;
         margin-left: 0;
    }

     

  22. В Opera 12.16 под макось пример не работает, пункты расфигачивает далеко за пределы родителя. 

    Пробороть проблему не получилось :/ 

     

     

  23. Спасибо за отличный урок и четкое объяснение. Но, у меня возникла проблема в том, что первый элемент строки (первая li) не прилегает к левому краю. Возможно, кто-то сможет подсказать в чем проблема. Заранее спасибо.

  24. В opera 12, в линуксе строка разтягивается, и inline-block вылазят из родительского элемента. 

    Частично помогло добавление не пустого псевдо элемента, а с символом (content = " "),но тогда выравнивание пропадает, но строка хотя бы не растягивается. при этом в остальных браузерах все хорошо. была бы благодарна, если бы вы помогли разобраться с причиной данного косяка

  25. Вопрос такой. В angularjs есть директива ng-repeat. После отрисовки блоков данной директивой почемуто стили для выравнивания по центру не применяются. Есть ли способ равномерно выравнить блоки отрисованные директивой ng-repeat ?

  26. Для правильного заполнения по ширине необходимо в исходном HTML между элементами списка вставлять какой-либо символ (пробел, разделитель строк).
    При использовании стилей из варианта с псевдо-элементом, в приведенном ниже примере, элементы 1-2, и 3-4 будут восприниматься как один, несмотря на то, что для каждого задано display: inline-block.

    12
    34
    5

    Причем, при просмотре кода элемента в Google Chrome элементы отображаются отдельно.

    1. Пример не получился ((
      Смысл такой: между закрывающим тегом и открывающим нет пробела, угловые скобочки стоят рядом, образуя букву Х:
      УЛ
      ЛИ 1 /ЛИ ЛИ 2 /ЛИ
      ЛИ 3 /ЛИ ЛИ 4 /ЛИ
      ЛИ 5 /ЛИ
      УЛ

      P.S. Если замечание стоящее, может, автор поправит пример. Лично мне это вынесло мозг. Joomla в шаблоне Atomic генерит главное меню именно таким образом.

      1. Замечание дельное, вариант на инлайн-блоках работает только при наличии пробелов между элементами. Без пробелов инлайн-блоки воспринимаются как «буквы одного слова», а растягивать буквы в слове умеет, как ни странно, только IE (благодаря упомянутому в статье text-justify: newspaper).

        Есть решение, основанное на Flexbox для новых браузеров и text-align-last для старых IE (см. обновление статьи и новую ссылку). Если добавить в него text-justify: newspaper для контейнера, должно кроссбраузерно работать без пробелов.

  27. Шикарно! Долго не мог выровнять элементы по ширине, последний способ супергуд! Ни один браузер не отказался!!! Спосибо!

  28. Зачем изобретать велосипед? И вдобавок кривой. Есть flexbox им надо и пользоваться. А вставлять div в список… глупо.

    1. Есть flexbox им надо и пользоваться

      Посмотрите на 1) дату публикации статьи, 2) вариант №5 в апдейте ;)

       вставлять div в список… глупо

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

    2. flexbox почему-то не везде корректно работает, кстати, в сафари не получилось его победить, а этот "велосипед" отлично справился.

  29. Большое спасибо за эту чудесную статью — читается залпом, легко! И какой Вы творческий человек, столько всего попробовали, в такие глубины залезли! Я Вами и Вашим другом восхищаюсь!

    Я благодарю вас! :)

  30. А почему бы не сделать такую логику.
    Чем шире элемент тем больший отступ он рождает. И распределяется это из общей величины отступов, пропорционально ширине конкретного элемента (тут зависимости может быть как линейной так и любой другой какой мы пожелаем).
    Отступы же между двумя элементами складываются из суммы отступов каждой из них.
    Мне кажется подобная реализация, в некотором роде, более логична.

    1. Тогда может получиться, что два соседних очень узких блока почти слипнутся, а между ними и очень широким соседом получится огромная «дырка». В каких ситуациях нужна именно такая логика?

      А так, в принципе, похожим образом ведут себя таблицы при table-layout:auto (правда, отступы получаются не между ячейками, а внутри них, но логика распределения свободного места очень похожа на описанную).

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

  31. Добрый день.
    Спасибо за статью, очень помогла.

    Можно усложню задачку?
    Как можно сверстать, чтобы не 1 ряд был таких блоков, растянутых по ширине, а, скажем, 3?
    Т.е. в 1 строке должно быть, например, 4 блока и таких строк несколько?
    И чтобы блоки были в 1 потоке, самое главное…..

  32. Спасибо Максим! Использую регулярно. В последнее время text-align-last начал чудить в браузерах на webkit (на FF не увидел), приходится добавлять для li text-align-last: left; чтобы весь текст не распределился равномерно по ширине как в случаи с justify. Раньше такого не наблюдалось.

  33. Здравствуйте.

    С удовольствием прочёл Вашу статью, открыл для себя text-align-last.
    Не знаю, насколько корректно обращаться в комментариях к Вам, но тут такое дело. Есть у меня задачка, но нет её решения. Суть задачи сводится к симуляции работы свойства Флексбокса flex (например, flex: 2 2 0%;) без применения Флексбокса. Блоки с этим свойством занимают 2 части из имеющейся ширины, например 20%. НО! Если задать блоку свойство min-width, то можно сделать великолепную адаптивную вёрстку, поскольку при достижении заданного min-width произойдёт перенос на новую строку. А УЖЕ на новой строке блок займёт 100% ширины несмотря ни на какие свойства flex, если он один. Если не один — соответственную часть.

    Как Вам задачка придумать кроссбраузерное решение без применения Флексбоксов? Например, с инлайн-блоками? И разместить решение на сайте?

    Сразу скажу, почему с Флексбоксами плохо. Кроссбраузерности нету даже в современных браузерах, особенно в мобильных браузерах под Андроид. Даже в новой Опере мини под Андроид Флексбокс совсем не работает. Причём именно совсем.((( Проверял лично. Я уже не говорю про Ослика ниже 10.

    1. В теории, полагаю, задача решаема комбинацией этого подхода с медиавыражениями (для каждого возможного кол-ва колонок — своя арифметика nth-*). Писать такое руками, конечно, страшновато, хотя это интересная задача для автоматизации (Sass-миксин/PostCSS-плагин?). Но не проще ли разобраться с причинами глюков флексбоксов? По caniuse Опера-Мини должна их поддерживать, может, там с meta viewport какая накладка случилась?

  34. Последний вариант мне подошел. Вот только отступ был с лева. Добавил ul { padding: 0; …. }. Теперь все ок.

  35. Как начинающий скажу — толковая статья.
    Уважение автору добавляет отличный русский язык.

    Только вот я не понял:
    «За решением данной проблемы мы отправляемся на самую высокую гору… на MSDN».
    Это что, для IE для проблем нужно искать отдельное решение?

    «Добавил ul { padding: 0; …. }. »
    Мне это не помогло — отступ слева остаётся.

  36. Нигде не мог найти более понятного и подробного описания о равномерном распределении блоков. Автору РЕСПЕКТ и огромное спасибо!

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

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

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