Xiper

Резиновое меню

Автор: Евгений Рыжков Дата публикации:

Задача

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

пример резинового меню

Требования

  • все пункты должны быть в одну строку;
  • универсальность: число пунктов и длина текста в каждом за ранее не известна, количество слов в пункте может быть разным;
  • семантический код: меню должно быть обычным списком;
  • без использования javascript.

Проблема

Задача казалось бы несложная, но для ее решения обычно применяется таблица или javascript. Проэмулировать для этой задачи таблицу с помощью display: table/table-cell не выходит, по крайней мере кроссбраузерно. Вот и имеем 21 век на дворе, HTML5, CSS3, а такое меню реализовываем «костылями». Но решение все-таки нашлось.

Решение

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

<ul>
<li><a href="#">Главная</a></li>
<li><a href="#">Собираем</a></li>
<li><a href="#">Следим</a></li>
<li><a href="#">Без цензуры</a></li>
<li><a href="#">Учимся</a></li>
<li><a href="#">Справочники</a></li>
</ul>
ul {
	text-align: justify;
	overflow: hidden; /* нужно чтобы обрезать разные побочные эффекты приема */
	height: 20px; /* нужна чтобы устранить один побочный эффект, но иногда можно обойтись без нее */
	cursor: default; /* растянутый текст justify приведет к тому что почти вся плашка меню будет иметь cursor: text */
	margin: 50px 100px 0 100px;
	background: #CCCCCC;
	padding: 5px;
}
li {
	display: inline; /* чтобы пункты меню выступали в роли текста */
}
li a {
	display: inline-block; /* чтобы не разрывались слова в пунктах меню */
	color: #0000CC;
}
a:hover {
	color: #ff0000;
}
ul:after { /* эмуляция дополнительной строки, чтобы сработал justify */
	content: "1";
	margin-left: 100%;
	height: 1px;
	overflow: hidden;
	display: inline-block;
}

Для IE6 и 7 потребуется дополнительный код:

ul {
	z-index: expression(runtimeStyle.zIndex = 1, insertAdjacentHTML('beforeEnd', '<li class="last"></li>'));
}
ul .last {
	margin-left: 100%;
}
* html ul { /* need ie6 only */
	height: 30px;
}

Демо пример. Проверено в:

  • IE 6-8
  • Firefox 3.6
  • Opera 10.5
  • Safari 5
  • Chrome 6

Недостатки

  • «раздутый» CSS-код;
  • не получится сделать по обычному активный пункт меню (<li class="active">текущий пункт</li>) из-за того, что теперь слова в пункте будут разрываться (если, конечно, слов в пункте больше, чем одно). Решить можно добавлением тега, обрамляющего текст, с правилами аналогичными ссылке. Например так: <li class="active"><span>текущий пункт</span></li>;
  • из-за overflow могут быть проблемы, если понадобится выпадающие подменю — придется код под такую задачу подгонять.

Материалы

  • Резиновое меню