Xiper

Верстка содержания

Автор: Александр Головко Дата публикации:

Задача

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

блок с содержанием

Основной момент — сделать максимально правильно с точки зрения семантики. Понятно, что точки — это оформление и в HTML им не место.

Таким образом, точки делаем фоном, само содержание — списком.

Немного погуглив нахожу первый вариант решения.

Решение 1. Флоатами

Soh Tanaka предлагает следующий вариант (код я немного модифицировал, так как в оригинале вместо нейтральных span использовались strong и em):

<ul>
	<li><span>Глава 1</span><span class="page">2</span></li>
	<li><span>Глава 2</span><span class="page">32</span></li>
	<li><span>Глава 3</span><span class="page">122</span></li>
</ul>
ul {
	width: 500px;
	list-style: none;
}
li {
	padding: 10px 0;
	text-align: right;
	background: url(images/dotted.gif) 0 22px repeat-x;
}
li span {
	float: left;
	background: #fff;
	text-align: left;
}
.page {
	float: none;
	text-align: right;
}

dotted.gif здесь и далее вот такой рисунок (4x4 пикселя на прозрачном фоне):

dotted.gif

Демо пример.

Проверено в:

  • IE 6-8
  • Firefox 3.5
  • Opera 9.5-10.6
  • Safari 4
  • Chrome 7

Недостатки:

  • если название главы растянется на несколько строк, то верстка ломается;
  • не совсем очевидное позиционирование фона (background-position у li задан, как "0 22px". Вот эти самые 22px зависят как от строки "padding: 10px 0;", так и от размера шрифта, т.е. подбирать нужно индивидуально для каждого случая;
  • использование обычного списка вместо списка определений (dl), который тут больше подходит по смыслу.

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

Решение 2. С абсолютным позиционированием

Основная трудность — в случае, когда название занимает несколько строк разместить номер страницы внизу:

номер страницы внизу

Самый простой вариант — позиционируем номер страницы абсолютом:

<ul>
	<li><span>Глава 1</span><span class="page">2</span></li>
	<li><span>Глава 2. Название длинное не влезло в одну строку и получился перенос на две</span><span class="page">32</span></li>
	<li><span>Глава 3</span><span class="page">122</span></li>
</ul>
li {
	width: 476px;
	background: url(images/dotted.gif) 0 bottom repeat-x;
	position: relative;
	padding-right: 24px; /* отступ равен ширине поля для номера */
	margin-bottom: 10px;
}
* html li {
	width: 500px; 
}
li span{
	background: #fff; /* перекрываем фон из точек под надписями */
}
.page {
	position: absolute;
	right: 0;
	bottom: 0;
}

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

Демо пример.

Проверено в:

  • IE 6-8
  • Firefox 3.5
  • Opera 9.5-10.6
  • Safari 4
  • Chrome 7

Достоинства по сравнению с предыдущим методом:

  • нормально работает при любой длине названия главы;
  • позиция фона не зависит от высоты строки — проще настраивать.

Недостатки:

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

Давай попробуем сделать все по-красоте и реализовать содержание, используя список определений.

Решение 3. Используем dl. Урезанный вариант.

Основаня трудность в том, что у списка определений нет общего контейнера для каждой пары dt-dd. Поэтому абсолютное позиционирование уже не подходит. Нужно искать другой вариант, как выровнять номер страницы по нижнему краю. И тут в очередной раз на помощь приходит строчный блок (display:inline-block)!

<dl>
	<dt><span>Глава 1</span></dt>
	<dd>2</dd>
	<dt><span>Глава 2. Название длинное не влезло в одну строку и получился перенос на две</span></dt>
	<dd>32</dd>
	<dt><span>Глава 3</span></dt>
	<dd>122</dd>
</dl>
dl {
	width: 500px;
}
dt {
	display: inline-block;
	//display: inline;
	zoom: 1;
	width: 470px;
	background: url(images/dotted.gif) 0 bottom repeat-x;
	margin-bottom: 10px;
}
dt span {
	background: #fff;
}
dd {
	display: inline-block;
	//display: inline;
	zoom: 1;
	width: 24px;
	margin-bottom: 10px;
}

Демо пример.

Проверено в:

  • IE 6-8
  • Firefox 3.5
  • Opera 9.5-10.6
  • Safari 4
  • Chrome 7

Достоинства по сравнению с предыдущим методом:

  • семантичность — используем список определений;
  • HTML почти предельно прост. Фактически у нас только один лишний (с точки зрения HTML) элемент — это span вокруг названия главы. Но если название главы — это, например, ссылка, которая ведет на саму главу, то вместо span ставим a, и лишних элементов нет вообще!

Недостатки:

  • используем хак для IE6-7 и невалидное свойство zoom, так как эти браузеры не понимают display:inline-block. Боремся с этим недостатком, вынося специфические для IE правила в отдельный CSS, подключенный условными комментариями.
  • такой вариант решения не позволяет полностью управлять отображением.

Немного подробнее по второму недостатку: за простоту HTML приходиться расплачиваться гибкостью. При таком решении невозможно сделать, чтобы точки подходили вплотную к номеру страницы (из-за бага с идущими подряд строчными блоками). Ширина поля с точками не варьируется в зависимости от количества цифр номера.

В общем, если дизайн не требует таких нюансов — данное решение можно считать идеальным. А если требует?

Решение 4. Используем dl. Окончательный вариант.

Применяем прием из статьи Долой отступы между строчными элементами (и блоками) + добавляем span для номера страниц для того, чтобы точки подстраивались под количество цифр номера:

<dl>
	<dt><span>Глава 1</span></dt>
	<dd><span>2</span></dd>
	<dt><span>Глава 2. Название длинное не влезло в одну строку и получился перенос на две</span></dt>
	<dd><span>32</span></dd>
	<dt><span>Глава 3</span></dt>
	<dd><span>122</span></dd>
</dl>
dl {
	width: 500px;
	font-size: 0;
	line-height: 0;
	letter-spacing: -1px;
}
dt {
	display: inline-block;
	//display: inline;
	zoom: 1;
	width: 476px;
	background: url(images/dotted.gif) 0 bottom repeat-x;
	margin-bottom: 10px;
	font-size: 14px;			/* восстанавливаем значения по умолчанию */
	line-height: normal;
	letter-spacing: normal;
}
dt span,
dd span {
	background: #fff;
}
dd{
	display: inline-block;
	//display: inline;
	zoom: 1;
	text-align: right;
	width: 24px;
	background: url(images/dotted.gif) 0 bottom repeat-x;
	margin-bottom: 10px;
	font-size: 14px;			/* восстанавливаем значения по умолчанию */
	line-height: normal;
	letter-spacing: normal;
}

Демо пример.

Проверено в:

  • IE 6-8
  • Firefox 3.5
  • Opera 9.5-10.6
  • Safari 4
  • Chrome 7

Это решение достаточно «тяжеловесное» в плане CSS, но оно позволяет наиболее гибко настраивать внешний вид содержания и использует семантичный HTML.

Убираем лишнюю картинку

Если дизайн позволяет, то можно отказаться от картинки в фоне. Вместо нее просто используем border-bottom, с соответствующими значениями (скорее всего dotted).

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

Модифицированный CSS для второго решения (с ul и абсолютным позиционированием):

li {
	width: 476px;
	border-bottom: 1px dotted #539127;
	position: relative;
	padding-right: 24px;
	margin-bottom: 10px;
}
* html li {
	width: 500px;
}
li span{
	background: #fff;
	position :relative;
	bottom: -1px;
}
.page{
	position: absolute;
	right: 0;
	bottom: -1px;
}

Для остальных вариантов изменить CSS по аналогии не составляет труда.

Выводы

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