Главная страница «Первого сентября»Главная страница журнала «Информатика»Содержание №12/2009


Спецвыпуски

CSS - верстка для продолжающих

Тема 2. Позиционирование

Окончание. Начало в № 11/2009

2.4.4. Раскладка блоков

Определимся с поведением запланированных блоков. Начнем с блока BODY (он ведь всегда с нами, как контейнер для всего остального на странице):

BODY

{

position:relative; /* Для позиционирования блока sidebar */

min-width: 600px; /* Ограничение минимальной ширины */

max-width: 600px; /* Ограничение максимальной ширины */

margin:0 auto; /* Для центрирования BODY по горизонтали */

padding:20px; /* Внутренний отступ */

font: small Georgia, serif; /* Шрифт и его размер */

background: url(pic/bgbody.png) #F3F8F6; /* Фоновая заливка */

}

Будем откладывать смещения для абсолютно позиционированного sidebar от BODY , поэтому для BODY задано позиционирование ( position:relative).

С помощью свойств min-width и max-width решим две проблемы. Первая связана с разрушением разметки в узких окнах, вторая — с непомерно длинными строчками в широких.

За исключением блока sidebar, все на странице находится в потоке, то есть обладает “резиновым” поведением: при уменьшении ширины окна браузер будет перестраивать поток так, чтобы уложить блоки в новые пределы. Рано или поздно возникнут трудности, и разметка порушится. Свойство min-width не позволит браузеру сужать BODY менее заданных 600 пикселей — дальше включается прокрутка.

Вторым ограничением max-width обеспечивается разумный максимум для длины строки (в блоке content).

Именно в тот момент, когда BODY превысит заданные 900 пикселей, оно начнет центрироваться в области просмотра благодаря указанию margin:0 auto (см. 1.3.3).

Браузеры IE5 не центрируют блоки при помощи auto , браузеры IE вплоть до 6 версий включительно не понимают свойств min-width и max-width. Все это можно обойти, но не будем отвлекаться от основной задачи.

При помощи padding:20px задается внутренний отступ со всех четырех сторон.

Указание font: small Georgia, serif задает гарнитуру и размер шрифта.

Наконец, правило background: url(pic/bgbody.png) #F3F8F6; обеспечивает фоновую заливку для BODY (заливка будет работать только для BODY, блоки внутри будут окрашиваться отдельно).

С BODY разобрались, займемся блоками sidebar и content.

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

Нам не хочется, чтобы текст вываливался за пределы sidebar, когда пользователь увеличивает размер шрифта. Значит, ширину этого блока будем указывать не в пикселях, а в em. Значение 1em примерно равно размеру текущего шрифта. При переводе в пиксели размеры, заданные через em, будут меняться пропорционально размеру текущего шрифта, и наша “кольчужка” (sidebar) для текста будет всегда для него впору.

Зададим для sidebar ширину 13em.

Высота блока header зависит от содержимого и может меняться при изменении шрифта.

Интересный вопрос: какие смещения нужно указать для sidebar? С left все понятно: 20 пикселей (внутренний отступ BODY).

А какое значение должно быть у top? Это значение равно 20 пикселям (внутренний отступ BODY) плюс высота блока header. Но высоту header мы решили не фиксировать. Мы ее не знаем. Она зависит от содержимого и может меняться при изменении шрифта пользователем.

Ужасно. Как же быть? Очень просто! Мы не будем указывать top для sidebar, и тогда это смещение станет равным тому значению, которое блок имел бы в потоке (см. 2.1.1). В потоке sidebar идет сразу за header — получится то, что надо. Ура!

Для content задан левый внешний отступ в 15em. В освободившееся пространство помещен абсолютно позиционированный блок sidebar.

Теперь можно спокойно записать определения для sidebar и content:

/* Общие свойства боковой панели и содержания */

#sidebar, #content

{

border: 1px solid #638F7B;

background: white;

color: #2A3D34;

padding:0.5em;

}

/* Боковая панель */

#sidebar

{

position: absolute;

left:20px;

width: 13em; /* Для хор. старых */

\width: 14em; /* Обман для IE5 */

w\idth: 13em; /* Для хор. новых */

}

/* Содержание */

#content

{

margin-left: 15em;

}

Посмотрим, как это выглядит на экране.

Посмотреть (позиционирование/сайт “Роботландия”/шаг 3).

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

Посмотреть (позиционирование/сайт “Роботландия”/шаг 4).

Зададим и для блока footer левый внешний отступ в 15em, чтобы он не путался под ногами у sidebar:

/* Подвал */

#footer

{

margin-left: 15em;

padding: 0.5em;

}

Ну вот: основные дела сделаны! Теперь можно попить чаю с сухариками, а затем продолжить начатую работу.

2.5. Продолжение 1

Продолжаем построение сайта на базе типового двухколонного макета (начало построения — в разделе 2.4).

Посмотреть (позиционирование/сайт “Роботландия”/шаг 4).

Заданы стилевые правила, определяющие положение блоков в окне браузера.

Все блоки, кроме боковой панели, оставлены в потоке. Для блоков content и footer задан внешний отступ слева, и в освободившееся место помещен абсолютно позиционированный блок sidebar.

2.5.1. Заголовочная часть

Добавим в HTML-код заголовочной части элемент IMG для отображения логотипа:

<DIV id="header">

<H1>

<IMG src="pic/logo.png" width=72 height=69 alt="Логотип" title="Логотип">

Роботландия

</H1>

</DIV> <!-- Конец header -->

Запишем стилевые определения для блока header, для H1 внутри header и для IMG внутри H1 (который сам внутри header):

/* Заголовочная часть*/

#header

{

background: url(pic/bgheader.png)

#264756 right top repeat-y;

}

#header H1

{

margin: 0;

padding: 10px;

font-size: 250%;

white-space:nowrap;

text-transform: uppercase;

letter-spacing: 2px;

color: #ffdf5e;

}

#header H1 IMG

{

vertical-align:middle;

}

Посмотреть (позиционирование/сайт “Роботландия”/шаг 5).

Для блока header задан фоновый цвет #264756 и фоновая полоска — картинка bgheader.png, которая замащивает правую часть блока header по вертикали (right top repeat-y):

На фоновой картинке задан горизонтальный градиент по цвету от #264756 до #12201a.

2.5.2. Заголовки внутри sidebar и content

Запишем стилевые определения для особых заголовков H2. Заодно зададим отступы для заголовков меньшего уровня и для абзацев:

H2

{

margin:-0.425em;

margin-bottom:0.5em;

padding: 0.1em 0.5em;

font-family: Tahoma, sans-serif;

font-size: 130%;

text-transform: uppercase;

letter-spacing: 1px;

white-space:nowrap;

color: #ffdf5e;

background: url(pic/bgh2.png)

#264756 left bottom repeat-x;

}

H3,H4,H5,H6 { margin: 1em 0 0.5em 0 }

P { margin: 0.5em 0 1em 0 }

Отступы (для Hn и P) выбраны таким образом, чтобы заголовок был ближе к своему абзацу (следующему за ним) и дальше от чужого (который ему предшествует).

Отметим две особенности. Первая связана с отрицательным отступом для H2, вторая — с работой Сами­знаете какого браузера.

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

Конечно, вам покажется странным значение -0.425em, ведь “логично” было бы задать -0.5em (величина внутреннего отступа в блоках sidebar и content). Мне и самому это значение, подобранное экспериментально, показалось странным! Мне казалось, что оно должно быть равно -0.35em (ведь шрифт заголовка на 30% крупнее, значит, нужно на столько же процентов уменьшить em в заголовке, чтобы сравнять его с em в родительском блоке).

Но потом я заметил, что 0.425 = (0.5+0.35)/2, и решил, что именно по такому алгоритму браузеры вычисляют em на стыке двух значений.

Во всех браузерах страница смотрится хорошо, кроме браузера IE. Здесь подложка под H2 обзаводится клоном, который ляпается на текст в блоке content.

Вылечивается заданием относительного позиционирования для блока content:

#content

{

position:relative; /* Для IE */

margin-left: 15em;

}

Посмотреть (позиционирование/сайт “Роботландия”/шаг 6).

Для блоков H2 задан фоновый цвет #264756 и фоновая полоска — картинка bgh2.png, которая замащивает нижнюю часть блока H2 по горизонтали (left bottom repeat-x), создавая иллюзию объемности:

На фоновой картинке задан вертикальный градиент по цвету от #264756 до #000000.

Так будет выглядеть заголовок H2, если для него вообще не задавать внешних отступов.

Так выглядит страница в IE-6. Вылечивается заданием относительного позиционирования для блока content. Относительное позиционирование (без задания смещений) никак не отразится на поведении блока (он по-прежнему в потоке).

Странность поведения IE/Win часто возникает из особенности, связанной с концепцией “определения позиции”, которая лежит в основе драйвера вывода этого браузера.

Суть концепции в следующем. Разработчики Microsoft ради ускорения работы браузера решили, что не каждый элемент должен обладать способностью к “определению позиции”, а только тот, у которого специальное свойство hasLayout установлено в true.

Если свойство hasLayout элемента установлено в false, его размерами и позициями управляет ближайший элемент, у которого это свойство установлено в true (и это часто вызывает странности в поведении браузера).

Одни элементы имеют “определение позиции” по умолчанию (например, HTML, BODY, TABLE, HR), другие получают hasLayout лишь при задании некоторых свойств CSS (явного свойства hasLayout в CSS нет).

Практический совет следующий. Если IE ведет себя странно, попробуйте установить hasLayout при помощи задания следующих свойств (список не полный):

· position (кроме static);

· float (left или right);

· width (кроме auto);

· height (кроме auto);

· zoom (кроме normal, обычно 1).

Вот типичные “заклинания”, которые стоит попробовать (что-то одно) в тяжелых случаях:

* HTML .block { height:1%; } /* Holly Hack */

.block { width: 100%; }

.block { position: relative; }

.block { zoom: 1; }

Иногда установка hasLayout, вылечивая одну проблему, приводит к появлению других: несхлопывании вертикальных внешних отступов, появлении лишних отступов, пропадании частей элементов или “прыгании” элементов при изменении размеров окна или наведении курсора на ссылку.

В таких случаях нужно либо отказаться от hasLayout , либо “обернуть” проблемный элемент в другой, установить у оберточного элемента hasLayout, а у исходного — сбросить.

2.5.3. Меню и ссылки

Снабдим элемент UL в блоке sidebar идентификатором:

<!-- Боковая панель -->

<DIV id="sidebar">

<H2>Содержание</H2>

<UL id="menu">

<LI>Начало

<LI><A href="01.htm">Продукты</A>

<LI><A href="02.htm">Университет</A>

<LI><A href="03.htm">Ссылки</A>

<LI><A href="04.htm">Автор</A>

</UL>

Запишем стилевые определения для меню и ссылок (внутри меню и любых других):

#menu

{

list-style: none;

margin:0.5em 0 1em 0; /* Как у P */

padding-left: 0;

}

A {text-decoration:none}

A:link {color:blue}

A:visited {color:purple}

A:hover {color:red}

A:active {color:red}

Посмотреть (позиционирование/сайт “Роботландия”/шаг 7).

Здесь все просто: убрали списочные маркеры (list-style: none), лишние отступы, назначили стили для ссылок.

Меню на странице только одно — в начале, значит, позаботимся о ссылке вверх на концах блоков sidebar и content. Будем записывать ссылку, используя событие onclick (щелчок) и метод scroll(0,0) — чтобы не мусорить историю переходов внутристраничными ссылками:

<A class="up" href="javascript:void(0)" onclick="scroll(0,0)">вверх>/A>

Оформим ссылку с использованием фоновой картинки:

/* Оформление ссылки вверх */

A.up

{

background: url(pic/up.png)

no-repeat center right;

padding-right: 0.8em;

}

Посмотреть (позиционирование/сайт “Роботландия”/шаг 8).

Освободим 0.8em справа (padding-right: 0.8em) и в появившееся пространство выведем фоновую картинку up.png.

2.5.4. Картинки

Картинку будем окружать рамкой с внутренним отступом в 10 пикселей. Нижняя и правая части рамки сделаны более темными для придания объема.

Для картинок, которые будет обтекать текст, введен уточняющий стиль left.

Запишем стилевые определения для картинок, которые будут размещаться в блоках sidebar и content:

#sidebar IMG, #content IMG

{

border: 1px solid gray;

border-bottom-color: black;

border-right-color: black;

padding: 10px;

}

.left

{

float:left;

margin: 0 10px 0 0;

}

Добавляем в блок content картинку и запускаем страницу в браузере.

Opera — нормально, Firefox — нормально, IE… Что такое? Место под картинку освободилось, а где же сама картинка? Ох уж этот IE!..

Спокойно. Может быть, она каким-то образом подлезла под content? Добавляем в стиль z-index:1000. Не помогло. Попробуем zoom:1. Не помогло. Последнее понятно: эта уловка — для установки hasLayout, но у картинки есть размеры, значит, она уже имеет hasLayout.

Посмотреть (позиционирование/сайт “Роботландия”/шаг 9).

Еще несколько попыток вернуть картинку, все бесполезно. Набираем в Яндексе запрос “float ie пропала картинка” и попадаем в форум Ивана Сагалаева (ссылка [10]). Ему пишут:

Затык какой-то. Вот следующий код в FF отображется нормально, а в IE пропадает картинка. Ума не приложу, в чем может быть проблема?

Ага! То, что надо, тем более что в приведенном коде как раз присутствует float для картинки, как и у нас. Но ответ Ивана рецепта не содержит:

Поведение IE не нужно объяснять J. Это просто баги, их очень много, и они проявляются самыми затейливыми способами. Логики в этом нет.

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

2.6. Продолжение 2

Продолжаем построение сайта на базе типового двухколонного макета (начало построения — в разделе 2.4).

Посмотреть (позиционирование/сайт “Роботландия”/шаг 9).

В предыдущем разделе (2.5) работа закончилась огорчением: картинка со свойством float не захотела показываться в IE, в то время как в других браузерах она отображалась нормально.

Но вот кофе выпит. Голова немного прояснилась, и руки уже тянутся к клавиатуре! Сначала надо выяснить причину, которая свела с ума IE, затем придумать лечение.

2.6.1. Танцы с бубнами

Исследование показало, что причина кроется в свойстве position: relative, которое было задано для блока content:

#content

{

position:relative; /* Для IE */

margin-left: 15em;

}

Если убрать эту строку, картинка на странице появляется.

Убрать, конечно, не жалко, ведь позиционирование для content и не нужно. Но…

Мы уже видели, что если позиционирования нет, появится дублирующая фоновая полоска от H2.

Так будет выглядеть заголовок H2 при нулевом внешнем отступе для этого блока.

H2 не примыкает вплотную к границам content (как хочется) — мешает внутренний отступ блока content.

Чтобы придвинуть H2 к границам content, было использовано отрицательное значение внешнего отступа для H2.

Отрицательные значения для margin вполне законны, они задокументированы в описаниях стандарта CSS2.1 (ссылка [1]).

Проблема с IE в этом месте возникла из-за отрицательных значений для внешнего отступа, которыми мы смещали заголовок H2 к границам content сквозь внутренний отступ этого блока.

H2

{

margin: -0.425em;

margin-bottom: 0.5em;

padding: 0.1em 0.5em;

font-family: Tahoma, sans-serif;

font-size: 130%;

text-transform: uppercase;

letter-spacing: 1px;

white-space:nowrap;

color: #ffdf5e;

background: url(pic/bgh2.png) #264756 left bottom repeat-x;

}

Хотя отрицательные значения для margin законны, они оказались не по зубам для IE.

Как же быть? А давайте откажемся от внутренних отступов для блоков sidebar и content.

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

Правда, к границам прижмутся и все другие элементы в этих блоках!

Сделаем вот что: все элементы, кроме H2, “обернем” в дополнительные блоки wrapper, для которых зададим внешние отступы.

.wrapper

{

margin-left: 10px;

margin-right: 10px;

}

Соответственно, HTML-код изменится следующим образом:

<BODY>

...

<!-- Боковая панель -->

<DIV id="sidebar">

<H2>Содержание</H2>

<DIV class="wrapper">

...

</DIV> <!-- Конец wrapper -->

<H2>Новости</H2>

<DIV class="wrapper">

...

</DIV> <!-- Конец wrapper -->

</DIV> <!-- Конец sidebar -->

<!-- Содержание -->

<DIV id="content">

<H2>О Роботландии</H2>

<DIV class="wrapper">

...

</DIV> <!-- Конец wrapper -->

</DIV> <!-- Конец content -->

...

</BODY>

Опишем правки, выполненные в стилевых определениях в связи с удалением внутренних отступов в sidebar и content и введением оберточного блока wrapper. Правок немного.

Было:

Стало:

#sidebar, #content

{

border: 1px solid #638F7B;

background: white;

color: #2A3D34;

margin-top: 0.5em;

padding:0.5em;

}

#sidebar, #content

{

border: 1px solid #638F7B;

background: white;

color: #2A3D34;

margin-top: 0.5em;

}

Убрали внутренние отступы в блоках sidebar и content.

 

Было:

Стало:

#sidebar

{

position: absolute;

left:20px;

width: 13em;

\width: 14em;

w\idth: 13em;

}

#sidebar

{

position: absolute;

left:20px;

width:14em;

font-family: "Trebuchet MS", Tahoma, sans-serif;

}

Указали ширину блока sidebar равной 14em. Так как отступов больше нет, отказались от трюка с width
для IE. Расхождение стандартной блочной модели с моделью IE теперь можно проигнорировать (2 пикселя рамки в сумме с обеих сторон). Заодно указали шрифт с засечками для контраста с основным содержанием.

 

Было:

Стало:

#content

{

position:relative; /* Для IE */

margin-left: 15em;

}

#content

{

margin-left: 15em;

}

.wrapper

{

margin-left: 10px; margin-right: 10px;

}

Убрали позиционирование и добавили оберточный блок wrapper.

 

Было:

Стало:

H2

{

margin:-0.425em;

margin-bottom:0.5em;

padding: 0.1em 0.5em;

font-family: Tahoma, sans-serif;

font-size: 130%;

text-transform: uppercase;

letter-spacing: 1px;

white-space:nowrap;

color: #ffdf5e;

background: url(pic/bgh2.png) #264756 left bottom repeat-x;

}

H2

{

margin: 0;

padding: 0.1em 10px;

zoom:1;

font-family: Tahoma, sans-serif;

font-size: 130%;

text-transform: uppercase;

letter-spacing: 1px;

white-space:nowrap;

color: #ffdf5e;

background: url(pic/bgh2.png) #264756 left bottom repeat-x;

}

Отрицательный внешний отступ заменили нулем. От em во внутреннем горизонтальном отступе перешли к px — чтобы выровнять левый край H2 по левому краю текста внутри блока. Пришлось, правда, добавить zoom:1 для установки hasLayout — без него IE снова становится плохо.

 

Было:

Стало:

#menu

{

list-style: none;

margin:0.5em 0 1em 0;

padding-left: 0;

}

#menu

{

list-style: none;

margin:0.5em 0;

padding-left: 0;

}

Изменили нижний внешний отступ с 1em на 0.5em (по дизайнерским соображениям).

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

Перемены пошли на пользу. Во-первых, избавились от трюка с блочной моделью для блока sidebar. Во-вторых, перешли к пикселям в горизонтальных отступах, что позволило выровнять контент слева по одной четкой вертикали (em-отступы зависят от размера шрифта, и на заголовках они крупнее).

Тем не менее ширина блока sidebar оставлена в em, и это правильно — с увеличением шрифта блок будет расширяться пропорционально, и текст (в том числе и в меню) никогда не выйдет за его пределы.

Приятно посмотреть (позиционирование/сайт “Роботландия”/шаг 10).

Все нормально работает во всех браузерах. И даже в IE!

2.6.2. Внешние ссылки

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

Роботландия (www.botik.ru/~robot)

Самое лучшее — оформлять внешние ссылки явным образом. Сразу видно, что ссылка внешняя, кроме того, при необходимости ее легко скопировать через буфер обмена.

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

Можно снабжать внешнюю ссылку небольшим графическим маркером — так поступают многие сайты, и мы пойдем этим путем.

Посмотреть (позиционирование/сайт “Роботландия”/шаг 11).

Внешняя ссылка присутствует в новостях на боковой панели.

Конечно, мы не станем рядом с каждой внешней ссылкой записывать тег <IMG> — будем выводить картинку (off.gif) как фоновый рисунок:

/* Оформление внешней ссылки */

A.off

{

background: url(pic/off.gif)

no-repeat center right;

padding-right: 11px;

margin-right: 2px;

zoom:1;

}

Почему введено правило zoom:1, объяснять, думаю, уже не стоит!

 

Каждый элемент A, соответствующий внешней ссылке, снабжается классом off. Можно было бы вообще обойтись без задания класса, используя определение:

/* Оформление внешней ссылки */

A[href^="http://"]

{

background: url(pic/off.gif) no-repeat center right;

padding-right: 11px;

margin-right: 2px;

zoom:1;

}

Селектор A[href^="http://"] указывает на все элементы A, у которых значение атрибута href начинается с http://. К сожалению, IE6 этой премудрости (в отличие от других браузеров) не понимает.

2.6.3. Инварианты страницы

Используя CSS, мы отделили визуальный образ сайта от его содержания и поместили все стилевые определения в отдельный файл main.css. Этот файл будет подключаться к страницам при помощи указания, расположенного в головной части каждого HTML-файла:

<STYLE type="text/css">@import url(main.css);</STYLE>

Теперь, если мы захотим что-то изменить в визуальном образе сайта, нам не придется вносить правки в многочисленные файлы HTML, мы будем работать с единственным файлом main.css, и эти правки автоматически скажутся на всех страницах сайта.

Очень хорошо, но… Что делать, если мы решим добавить в меню еще один пункт? Освежить новости, сменить логотип, исправить контактную информацию? Увы, придется выполнять одни и те же правки во всех HTML-файлах.

Эта печальная перспектива наводит на счастливую мысль выделить все инварианты сайта в один JavaScript-файл и все правки выполнять только в нем, не прикасаясь к файлам HTML.

Мы так уже делали при построении меню на вкладках в разделе “1.6.5. Подключаем JavaScript”. Проделаем и здесь аналогичную работу.

2.6.3.1. Подвал

Начнем с самого простого инварианта — подвала.

Сейчас этот фрагмент кода выглядит так (заодно отнесли к нему и горизонтальную прямую, которая показывается без CSS):

<HR class="none">

<!-- Подвал -->

<DIV id="footer">

&copy;&nbsp;2009 А.&nbsp;А.&nbsp;Дуванов <A href="mailto:kurs@robotland.pereslavl.ru"> kurs@robotland.pereslavl.ru</A>

</DIV> <!-- Конец footer -->

Создадим функцию PutFooter, которая будет формировать этот код и выводить его на экран:

// Подвал

function PutFooter()

{

var str = '<HR class="none"><DIV id="footer">';

str += '&copy;&nbsp;2009 А.&nbsp;А.&nbsp;Дуванов <A ' +

'href="mailto:kurs@robotland.pereslavl.ru"> kurs@robotland.pereslavl.ru </A>';

str += '</DIV>';

document.write(str);

}

Эту функцию мы поместим в файл main.js, а в HTML внесем следующие правки:

Было:

<HR class="none">

<!-- Подвал -->

<DIV id="footer">

&copy;&nbsp;2009 А.&nbsp;А.&nbsp;Дуванов <A href="mailto:kurs@robotland.pereslavl.ru"> kurs@robotland.pereslavl.ru </A>

</DIV> <!-- Конец footer -->

Стало:

<!-- Подвал -->

<SCRIPT language="JavaScript" type="text/javascript">

<!--

PutFooter();

//-->

</SCRIPT>

Не забудем подключить main.js к HTML-файлу:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"

"http://www.w3.org/TR/html4/loose.dtd">

<HTML lang="ru">

<HEAD>

<META http-equiv="Content-Type" content="text/html; charset=windows-1251">

<META http-equiv="imagetoolbar" content="no">

<META http-equiv="author" content="А.Дуванов">

<META name="keywords" content="Роботландия, Роботландский университет">

<META name="description" content="ПМК для школ, сетевое обучение.">

<META http-equiv="Content-Style-Type" content="text/css">

<STYLE type="text/css">@import url(main.css);</STYLE>

<SCRIPT language="JavaScript" type="text/javascript" src="main.js"></SCRIPT>

<TITLE>Роботландия</TITLE>

</HEAD>

2.6.3.2. Заголовочная часть

Функция PutHeader будет формировать код заголовочной части и выводить его на экран. У функции будет параметр page — номер текущей страницы. Для всех страниц, кроме главной (для которой page равен нулю), логотип должен быть ссылкой на главную страницу:

// Заголовочная часть

// page=0 для главной страницы

function PutHeader(page)

{

var str = '<DIV id="header"><H1>';

if (page) str += '<A href="index.htm">';

str += '<IMG src="pic/logo.png" border=0 width=72 height=69 alt="Логотип" title=';

str += page ? '"На главную">' : '"Логотип Роботландии">';

if (page) str += '</A>';

str += ' Роботландия'

str += '</H1></DIV><HR class="none">';

document.write(str);

}

Было:

<!-- Заголовочная часть -->

<DIV id="header">

<H1>

<IMG src="pic/logo.png" width=72 height=69

alt="Логотип Роботландии"

title="Логотип Роботландии">

Роботландия

</H1>

</DIV> <!-- Конец header -->

<HR class="none">

Стало:

<!-- Заголовочная часть -->

<SCRIPT language="JavaScript" type="text/javascript">

<!--

PutHeader(0);

//-->

</SCRIPT>

2.6.3.3. Боковая панель

Функция PutSidebar(page) формирует код всей боковой панели sidebar. Параметр page определяет номер текущей страницы — эта страница в меню не должна быть ссылкой:

// Боковая панель

// page -- номер текущей страницы (нумерация с нуля)

function PutSidebar(page)

{

var str = '<DIV id="sidebar">';

str += '<H2>Содержание</H2>';

str += PutМеню(page); // Меню

str += '<H2>Новости</H2>';

str += PutNews(); // Новости

str += '</DIV><HR class="none">'

document.write(str);

}

Было:

<!-- Боковая панель -->

<DIV id="sidebar">

<H2>Содержание</H2>

<DIV class="wrapper">

<UL id="menu">

<LI>Начало

<LI><A href="sale.htm">Продукты</A>

<LI><A href="ru.htm">Университет</A>

<LI><A href="links.htm">Ссылки</A>

<LI><A href="author.htm">Автор</A>

</UL>

</DIV> <!-- Конец wrapper -->

<A class="none" href="#content">Пропустить новости</A>

<HR class="none">

<H2>Новости</H2>

<DIV class="wrapper">

<H3>Зима, 2009</H3>

<P>

Написаны заметки под названием &laquo;CSS-верстка&raquo;.

<H3>31.01.09</H3>

<P>

Роботландии исполнилось 18 лет!

<P>

День рождения предприятия &laquo;Роботландия&raquo;&nbsp;&#8212; 31&nbsp;января 1991 года. Однако работы по созданию курса информатики для младших школьников группа сотрудников будущей Роботландии начала 3&nbsp;сентября 1986 года в недрах лаборатории школьной информатики Института программных систем Академии наук СССР

(<A class=off href="http://www.botik.ru/PSI">ссылка</A>).

<H3>Лето, 2008</H3>

<P>

Создан электронный учебник &laquo;Изучаю компьютер&raquo;&nbsp;&#8212; гипертекстовый интерактивный курс для младших школьников и одаренных дошкольников, работающих под руководством наставника.

</P>

<A class="up" href="javascript:void(0)" onclick="scroll(0,0)">вверх</A>

</DIV> <!-- Конец wrapper -->

</DIV> <!-- Конец sidebar -->

<HR class="none">

Стало:

<!-- Боковая панель -->

<SCRIPT language="JavaScript" type="text/javascript">

<!--

PutSidebar(0);

//-->

</SCRIPT>

Функция PutSidebar(page) обращается к двум вспомогательным функциям:

· PutМеню(page) — построение меню;

· PutNews() — построение блока новостей.

Ниже приводятся их описания.

2.6.3.3.1. PutМеню

// Меню

// Пункты меню

// -----------

var ItemsMenu = new Array (

new Array ("Начало", "index.htm"),

new Array ("Продукты", "sale.htm"),

new Array ("Университет", "ru.htm"),

new Array ("Ссылки", "links.htm"),

new Array ("Автор", "author.htm")

);

// page -- номер текущей страницы (нумерация с нуля)

function PutМеню(page)

{

var str = '<DIV class="wrapper"><UL id="menu">';

for (var i = 0; i < ItemsMenu.length; i++)

{

str += '<LI>';

if (i != page) str += '<A href="' + ItemsMenu[i][1] + '">';

str += ItemsMenu[i][0];

if (i != page) str += '</A>';

str += '</LI>';

}

str += '</UL></DIV><A class="none" href="#content">Пропустить новости </A><HR class="none">';

return str;

}

2.6.3.3.2. PutNews

// Новости

function PutNews()

{

var str = '<DIV class="wrapper">';

str += ' <H3>Зима, 2009</H3>' +

' <P>'+

'Написаны заметки под названием &laquo;CSS-верстка&raquo;.'+

''+

' <H3>31.01.09</H3>'+

' <P>'+

'Роботландии исполнилось 18 лет!'+

' <P>'+

'День рождения предприятия &laquo;Роботландия&raquo;&nbsp;&#8212; '+

'31&nbsp;января 1991 года. Однако работы по созданию курса информатики для '+

'младших школьников группа сотрудников будущей Роботландии начала '+

'3&nbsp;сентября 1986 года в недрах лаборатории школьной информатики '+

'Института программных систем Академии наук СССР '+

'(<A class=off href="http://www.botik.ru/PSI">ссылка</A>).'+

''+

' <H3>Лето, 2008</H3>'+

' <P>'+

'Создан электронный учебник &laquo;Изучаю компьютер&raquo;&nbsp;&#8212; '+

'гипертекстовый интерактивный курс для младших школьников и одаренных '+

'дошкольников, работающих под руководством наставника.'+

' </P>';

str += '<A class="up" href="javascript:void(0)" ' +

'onclick="scroll(0,0)">вверх</A></DIV>';

return str;

}

2.6.4. Готовый шаблон

Наконец для главной страницы построен шаблон, по которому можно строить остальные страницы сайта. Шаблон состоит из трех файлов: HTML, CSS и JavaScript.

Посмотреть (позиционирование/сайт “Роботландия”/шаг 12).

2.6.4.1. Плюсы и минусы JavaScript-инвариантов

Плюсы выделения инвариантов в отдельный JavaScript-файл очевидны: такой подход упрощает HTML, убыстряет загрузку и позволяет выполнять правки в одном-единственном файле.

Минусы связаны с поисковыми системами, благодаря которым сайт становится доступным широким слоям публики.

Мы прячем от сетевых пауков важную информацию: заголовок сайта, меню, новости и контактные данные.

Особенно обидно прятать заголовок сайта — самую значимую для поисковиков информацию.

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

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

В любом случае не следует пренебрегать мета-информацией в заголовочной части — ее поисковики читают с большим удовольствием.

Другой вариант — строить сайт при помощи серверных PHP-скриптов. Здесь видимость для поисковиков полная, в какие бы функции мы не “заворачивали” код, — в итоге клиент получает с сервера страницу на “чистом” HTML, независимо от того, сколько функций было “потрачено” на построение кода.

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

2.6.5. Готовый сайт

Примерный вид страницы “Университет”.

Посмотреть сайт (позиционирование/сайт “Роботландия”/шаг 13).

Шаблон (файлы index.htm, main.css и main.js) построен!

Теперь можно копировать содержимое index.htm в файлы, обозначенные в меню как sale.htm, ru.htm, links.htm, author.htm, и наполнять их содержимым.

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

Кажется, мы потрудились неплохо, и результат не стыдно показать публике!

 

ТЕМА 3. ПЛАВАЮЩИЕ БЛОКИ

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

Содержание темы

· 3.1. Свойство float

· 3.2. Свойство float на практике

· 3.3. Пример (разработка сайта “Фотография”)

3.1. Свойство float

CSS-cвойство float обобщает действие атрибутов align=left и align=right , которые работают только для картинок (и таблиц в некоторых браузерах), на все элементы HTML.

Как вы помните, картинка с align=left прижимается к левому краю, а другие элементы обтекают ее справа. Картинка с align=right , наоборот, прижимается справа, а обтекание происходит слева.

Рассмотрим в деталях поведение элемента с заданным для него свойством float , но прежде введем два определения.

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

В качестве примера рассмотрим код:

<BODY>

<DIV>

<P>

В этом абзаце внутри <b>сильного выдeления находится обычное <EM>выделение</EM></b>.

</P>

</DIV>

</BODY>

Контейнером элемента EM является блок P.

Строчный элемент b является родителем элемента EM, но не является его контейнером. Первый блок-предок — P (дедушка), именно он и является контейнером для EM .

Будем называть элемент с заданным свойством float:left или float:right плавающим элементом (в ходу еще один термин: перемещаемый элемент).

3.1.1. Правила перемещения

3.1.1.1. Плавающий элемент становится блоком

Плавающий элемент автоматически становится блоком, даже если он был строчным элементом.

HTML-код:

<P>

 

.

</P>

CSS-код:

EM

{

float:left;

background:#fff2a6;

padding:10px;

margin:10px;

border: 1px solid black;

}

Элемент EM превращается в блок и сдвигается к левой границе своего контейнера P. Остальная часть абзаца обтекает плавающий элемент справа.

Как вы помните (см. 1.2.4), внешние отступы строчных элементов работают только по горизонтали. Здесь свойство margin:10px действует со всех сторон, что типично для блока.

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

Практический вывод: если для строчных элементов (таких, как A, IMG , SPAN ) задается свойство float:left или float:right , то для них автоматически устанавливается свойство display:block , и нет необходимости в явной установке этого свойства.

Более того, если мы даже укажем для плавающего элемента свойство display:inline , оно будет проигнорировано.

3.1.1.2. Позиция по горизонтали

Плавающий элемент со значением left прижимается к левому краю, а плавающий элемент со значением right — к правому краю своего контейнера.

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

Действует правило:

Плавающий элемент примыкает к внутреннему краю своего контейнера своим внешним краем.

То есть внешний край внешнего отступа плавающего элемента соприкасается с внутренним краем внутреннего отступа контейнера.

Такое поведение означает, что плавающий элемент не может выйти за пределы своего контейнера.

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

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

3.1.1.3. Позиция по вертикали

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

3.1.1.3.1. Элемент был блочным

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

HTML-код:

<BODY>

<P>Первый абзац. Он расположен в коде до плавающего элемента.

<P class=float>Второй абзац. Это плавающий элемент.

<P>Третий абзац. Он расположен в коде после плавающего элемента.

<P>Четвертый абзац. Он расположен в коде последним.

</BODY>

CSS-код:

BODY { padding:0; margin:0;}

P /* Правила для всех абзацев */

{

background:cyan;

padding:10px;

margin:10px;

border: 1px solid black;

}

P.float /* Дополнения для абзаца float */

{

float:left;

width:160px;

background:#fff2a6;

}

Верх плавающего элемента занимает такое место, которое оно имело бы в потоке, за исключением одного момента: внешние отступы плавающего элемента не схлопываются (см. 1.2.2).

Видим на иллюстрации, что расстояние от границы плавающего блока до границы первого абзаца равно не 10 пикселей, а 20. Внешний отступ первого абзаца (10 пикселей) сложился с внешним отступом плавающего блока (10 пикселей).

Заметим, что в горизонтальном направлении плавающий блок внешней частью своего внешнего отступа точно примыкает к границе своего контейнера BOD

В этом месте придется остановиться, чтобы сделать замечание для IE.

Так отображается приведенный выше код в самом популярном на сегодня браузере Интернета.

Видим, что внешний отступ слева удваивается (у плавающих элементов с float:right будет удваиваться внешний отступ справа).

К счастью, этот баг излечивается очень просто — если для плавающего элемента записать display:inline , IE “исправится”.

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

3.1.1.3.2. Элемент был строчным

Рассмотрим поведение плавающего элемента, который был строчным.

Стандарт формулирует здесь два правила.

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

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

Таким образом, если элемент находится в первой строке, то верх образованного из него плавающего элемента должен совпадать с верхом первой строки. Так и происходит на практике.

Рассмотрим теперь поведение элемента, расположенного не в первой строке.

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

Если строчный элемент стал плавающим, его верх должен совпадать с верхом строки, в которой он ранее находился.

На практике браузеры либо всегда выводят плавающий элемент на строку ниже (IE, FireFox), либо и так и этак, в зависимости от того, есть ли в текущей строке место (Opera).

Пусть страница задана следующим кодом.

HTML-код:

<BODY>

<P>

В этом предложении внутри <b>сильного выделения находится обычное <EM>выделение
</EM></b>. Это второе предложение, оно завершает абзац.

</P>

</BODY>

CSS-код:

BODY { margin:0; padding:0; }

P { margin:0; padding:0; background:cyan; }

EM

{

float:left;

margin:0; padding:10px;

width:100px;

background:#fff2a6;

border: 1px solid black;

}

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

Opera может вывести элемент и во вторую строку.

Все вышесказанное относится к элементам с нулевыми и положительными внешними отступами. Отрицательные значения margin-top заставляют элемент подниматься выше обычного положения и даже перекрывать расположенные там элементы.

3.1.1.4. Поведение плавающего элемента

Изымается ли плавающий элемент из потока? Да, конечно.

Если элемент был блочным, он перестает занимать всю ширину родителя (что свойственно блокам в потоке (см. 1.5.2)), для него перестает действовать правило схлопывания (см. 1.2.2) внешних отступов и правило проваливания (см. 1.2.3) за границу родителя.

Если элемент был строчным, он превращается в блок и покидает свое строчное место (удаляется из строки).

Плавающий элемент поднимается над потоком. Он смещается по горизонтали к внутреннему краю своего контейнера. Блоки, следующие за ним, подтягиваются вверх и занимают освободившееся место в потоке.

Однако плавающий элемент не перекрывает содержимое других блоков. Содержимое начинает обтекать плавающий блок со свободной стороны.

Плавающий элемент располагается над потоком, но не перекрывает содержимое (хотя перекрывает фон блоков в потоке).

3.1.1.5. Прекращение обтекания

Свойство clear позволяет прекратить обтекание:

· clear:left — прекращает обтекание элементов float:left ;

· clear:right — прекращает обтекание элементов float:right ;

· clear:both — прекращает обтекание элементов float:left и float:right .

Свойство clear применимо только к блочным элементам (например, нельзя использовать <BR style="clear:left"> , так как элемент BR — строчный).

Блок, в котором задано свойство clear , опускается, пока не окажется ниже плавающего элемента.

3.1.1.6. Когда плавающих элементов много

Плавающие блоки не перекрывают друг друга и не перекрывают содержимое потока (если только у них не заданы отрицательные внешние отступы).

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

Если плавающие блоки с однотипным указанием (float:left или float:right ) идут в коде непосредственно друг за другом, то каждый следующий блок пытается пристроиться со свободной стороны предыдущего блока на одной с ним горизонтали, если есть место.

Если места нет, плавающий блок, идущий в коде дальше, смещается ниже.

Следующие в коде друг за другом плавающие блоки разного типа (float:left и float:right ) ведут себя аналогично: пытаются уместиться на одной горизонтали.

Если места нет, блок, который в коде идет дальше, смещается ниже.

3.1.1.7. Плавающий внутри плавающего

Пусть в потоке один блок вложен в другой:

<DIV id="block1">

<DIV id="block2">

...

</DIV>

</DIV>

Сделаем вложенный блок плавающим:

#block2 { float:left}

Видим “странную” картину: блок-родитель “схлопнулся” по вертикали до размеров своих рамок и отступов.

Никакой странности нет. Плавающий блок покинул поток, и его родитель оказался пустым.

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

#block1 { float:left}

#block2 { float:left}

Плавающий элемент не выходит за пределы своего плавающего родителя (если только не заданы отрицательные внешние отступы).

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

3.2. Свойство float на практике

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

Заметим, что свойство float не предназначено для верстки колонок, хотя используется для этой цели очень часто. Но что делать, если настоящие средства многоколонной верстки присутствуют только в стандарте CSS3 (серия свойств с префиксом column ), который браузеры пока не поддерживают. Вот и приходится разработчикам придумывать техники, основываясь на подручном материале.

Примеры и практические рекомендации приводятся в разделах:

· 3.2.1. Плавающая врезка

· 3.2.2. Горизонтальное меню

· 3.2.3. Плавающие колонки фиксированной ширины

· 3.2.4. Плавающие пропорциональные колонки

3.2.1. Плавающая врезка

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

Код HTML (с сокращениями):

<BODY>

<DIV id="header">...</DIV>

<DIV id="incut">

<H3>Роботландский конь</H3>

...

</DIV>

<P>

Курс с таким названием вошел...

<P>

...

<DIV id="footer">...</DIV>

</BODY>

Код CSS (с сокращениями):

#incut

{

float:right;

width:15em;

margin: 0 0 10px 10px;

}

Посмотреть (плавающие блоки/плавающая врезка/шаг 1).

Врезка размещается в блоке incut , который объявлен плавающим. Ширина врезки указана в em , но можно было задать значение в процентах (например, width:30% ).

Фокус с display:inline (см. 3.1.1.3.1) для IE не потребовался, потому что внешний отступ справа у блока incut задан нулевым, для выравнивания его справа по одной вертикали с остальным содержимым BODY .

3.2.2. Горизонтальное меню

Используя свойство float, построим горизонтальное меню сайта.

Меню — это список. Поэтому логично записать его в виде UL :

<UL class="menu">

<LI><SPAN>Начало</SPAN>

<LI><A href="01.htm">Продукты</A>

<LI><A href="02.htm">Университет</A>

<LI><A href="03.htm">Ссылки</A>

<LI><A href="04.htm">Автор</A>

</UL>

Пункт, который не является ссылкой, обернут элементом SPAN , чтобы можно было формулировать для него стилевые правила (как для A в других пунктах).

Вот так это выглядит в окне браузера.

На горизонтальное меню совсем не похоже, но стоит взять в руки CSS…

Сформулируем правила CSS, которые превращают вертикальный список в горизонтальный:

.menu

{

width:100%;

margin:0;

padding: 6px;

list-style: none; /* Минус маркеры */

background: #ffcb2d;

}

.menu LI

{

float:left;

display:inline; /* Уловка для IE */

margin-left:1em;

font-family: Arial, sans-serif;

font-size:70%;

}

Так это выглядит в FireFox и Opera

Так — в IE.

Посмотреть (плавающие блоки/горизонтальное меню/шаг 1).

Благодаря float:left элементы LI стали плавающими и примкнули друг к другу по горизонтали.

Однако налицо две проблемы: появилась горизонтальная прокрутка, и фон, заданный для UL , не охватывает элементы LI в браузерах FireFox и Opera.

С прокруткой все понятно. Мы указали 100% для ширины блока UL . Но это ширина без отступов и границ. Значит, общая ширина UL получается равной 100% + 12 пикселей (6 пикселей, заданные для padding с обеих сторон). То есть ширина UL больше ширины BODY на 12 пикселей — вот и включается прокрутка.

Вывод: если для блока определена ширина в 100%, нельзя для него задавать горизонтальные отступы и границу.

В нашем случае надо положить padding:0 (в дополнение к margin:0 ).

Но почему фон, заданный для UL , не охватывает элементы LI в браузерах FireFox и Opera?

На самом деле эти браузеры ведут себя согласно стандартам, а ошибается как раз IE!

Блок UL находится в потоке, а его предки LI удалены из потока свойством float:left. Блок UL оказался пустым. Мы бы вообще его не увидели, если бы не padding: 6px . Вот эти 12 пикселей (6 плюс 6, сверху и снизу), окрашенные в цвет фона, мы и видим по вертикали (см. 3.1.1.7).

Добавим в правила для блока menu указание float:left и уберем внутренний горизонтальный отступ:

.menu

{

float:left;

width:100%;

margin:0;

padding: 6px 0;

list-style: none; /* Минус маркеры */

background: #ffcb2d;

}

Благодаря float:left блок menu (это наш UL ) стал охватывать своих плавающих потомков LI .

Отказ от горизонтальных отступов (padding: 6px 0 ) убрал линейку прокрутки.

Посмотреть (плавающие блоки/горизонтальное меню/шаг 3).

Для элементов A добавлен внутренний отступ шириной в два пикселя со всех сторон. Это сделано, чтобы прямоугольник под курсором не прилипал к буквам. Аналогичный отступ объявлен и для SPAN , чтобы унифицировать геометрию всех пунктов.

Несколько возможных дополнительных штрихов, и меню готово:

.menu LI A, .menu LI SPAN

{padding: 2px}

.menu A {text-decoration: none;}

.menu A:link {color: #0040ff}

.menu A:visited {color: purple}

.menu A:hover

{

color:white;

background: #ff7f00;

}

.menu A:active {color: red}

3.2.3. Плавающие колонки фиксированной ширины

Рассмотрим уже знакомый макет страницы (см. 2.4.1), представленный на рисунке.

HTML-код:

<BODY>

<DIV id="header"></DIV>

<DIV id="sidebar"></DIV>

<DIV id="content"></DIV>

<DIV id="footer"></DIV>

</BODY>

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

Блоки header и footer оставим в потоке, а колонки (блоки sidebar и content ) сделаем плавающими. Не забудем при этом для блока footer указать свойство clear:both , чтобы прекратить обтекание и обеспечить расположение подвала ниже обеих колонок.

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

Определимся с общей шириной BODY : внешний отступ (10) + граница (1) + внутренний отступ (10) + ширина содержимого (740). Получается 762 пикселя без учета внешних отступов и 782 с внешними отступами. Как правило, этого достаточно, чтобы не вызвать прокрутку в окне шириной 800 (остаток идет на границы самого окна и линейку вертикальной прокрутки).

Проверка: 740 (ширина содержимого BODY ) = 240 (общая ширина sidebar ) + 500 (общая ширина content ).

Для sidebar и content не задаются внешние отступы и границы.

Общая ширина sidebar: внутренний отступ (10) + ширина содержимого (220). Всего: 240.

Общая ширина content : внутренний отступ (10) + ширина содержимого (480). Всего: 500.

Теперь нетрудно написать стилевые определения:

HTML

{

text-align: center; /* Центрирование BODY для IE5 */

background: #F6F2E4;

}

BODY

{

/* Раскладка */

margin: 10px auto; /* BODY по центру HTML */

text-align: left; /* Восстановим выравнивание текста слева */

padding:10px;

border: 1px solid #638F7B;

width: 740px; /* Для старых хороших браузеров */

\width: 762px; /* Обман для IE5 */

w\idth: 740px; /* Для новых хороших браузеров */

/* Шрифты */

font: small Georgia, serif;

/* Цвета */

color:black;

background: white;

}

/* Заголовочная часть*/

#header

{

}

/* Общие свойства боковой панели и содержания */

#sidebar, #content

{

color: #2A3D34;

padding: 10px;

}

/* Боковая панель*/

#sidebar

{

float:left;

background:#fff2a6;

width: 220px; /* Для старых хороших браузеров */

\width: 240px; /* Обман для IE5 */

w\idth: 220px; /* Для новых хороших браузеров */

}

/* Содержание */

#content

{

float:left;

background:#d4dff3;

width: 480px; /* Для старых хороших браузеров */

\width: 500px; /* Обман для IE5 */

w\idth: 480px; /* Для новых хороших браузеров */

}

/* Подвал */

#footer

{

clear:both;

padding: 0 10px 10px 10px;

}

Для блока content задано float:left , но можно было бы задать и float:right — в данном случае нет никакой разницы.

Получилось неплохо, кроме одной неприятности: на колонки это совсем не похоже.

Колонки по определению должны иметь одинаковую высоту!

Проблема имеет решение под названием “ложные колонки”. Оно было описано Дэном Седерхольмом (Dan Cederholm) в статье “Faux Columns” (ссылка [13]).

Посмотреть (плавающие блоки/плавающие колонки фиксированной ширины/шаг 1).

Суть метода в следующем: для контейнера колонок задается фоновая полоска, которая замащивает блок по вертикали, “продлевая” тем самым высоту колонок до конца контейнера. Высоты колонок, конечно, остаются прежними, но визуально они уравниваются.

В нашем случае контейнером колонок является блок BODY , что неудовлетворительно. Ведь если замостить BODY ложными колонками, в них попадут блоки header и footer , что совершенно не нужно.

Придется колонки обернуть во вспомогательный блок wrapper (он станет контейнером колонок) и именно для него указать нужную фоновую полоску.

Изменения в HTML:

<BODY>

<DIV id="header"></DIV>

<DIV id="wrapper">

<DIV id="sidebar"></DIV>

<DIV id="content"></DIV>

</DIV>

<DIV id="footer"></DIV>

</BODY>

Изменения в CSS:

/* Обертка блоков sidebar и content */

#wrapper

{

float:left;

background: url(pic/wrapbg.png)

repeat-y;

}

Посмотреть (плавающие блоки/плавающие колонки фиксированной ширины/шаг 2).

Вероятно, вы догадались, зачем блок wrapper сделан плавающим!

Если оставить блок в потоке, фоновой полоски мы не увидим, ведь блок окажется пустым (его плавающие потомки не в счет), значит, высота его равна нулю (см. 3.1.1.7).

Понятно, что показанная методика работает при любом числе колонок. Для всех плавающих блоков задаем float:left, и они будут прилипать друг к другу слева на одной горизонтали. Самое главное — аккуратно задать размеры всем блокам так, чтобы сумма равнялась значению width для BODY .

3.2.4. Плавающие пропорциональные колонки

Давайте сделаем обе колонки “резиновыми”. Пусть они заполняют ширину текущего окна в отношении 30:70%:

HTML-код:

<BODY>

<DIV id="header"></DIV>

<DIV id="wrapper">

<DIV id="sidebar">

<DIV class="wrap"></DIV>

</DIV>

<DIV id="content">

<DIV class="wrap"></DIV>

</DIV>

</DIV>

<DIV id="footer"></DIV>

</BODY>

Блоки sidebar и content , как и ранее, обернуты контейнером wrapper с той же целью: создать ложные колонки в виде фона блока wrapper .

Кроме того, содержимое блоков sidebar и content погружено в блоки <DIV class="wrap"></DIV> . Зачем?

Так сделано потому, что мы не можем в этом макете задавать отступы (и границу) для блоков sidebar и content , как это делали в макете с фиксированной шириной колонок (см. 3.2.3).

В самом деле, мы должны указать:

#sidebar

{

float: left;

width: 30%;

}

#content

{

float: left;

width: 70%;

}

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

Поэтому блокам sidebar и content отступы не приписываются, а будут заданы для специально введенных блоков DIV , которыми обернуто содержимое блоков sidebar и content .

Пусть для sidebar и content задан padding:10px . Тогда общая ширина этих блоков станет 100%+40px . Места для плавающего блока content справа от sidebar нет, и он свалится вниз:

Если бы IE понимал селектор “> ”, можно было бы обойтись безымянными блоками, записав правило:

#sidebar > DIV, #content > DIV

{

padding:10px;

}

Но IE дочернего селектора не понимает, приходится для этих вспомогательных блоков явно задавать класс (wrap).

Заметим, что записать это правило при помощи контекстного селектора нельзя, ведь указания #sidebar DIV и #content DIV будут относиться не только к прямым потомкам блоков sidebar и content , но и ко всем другим, на любом уровне вложенности, что нам совсем не нужно.

С учетом сказанного записываем стилевые определения для нашего “резинового” макета:

BODY

{

/* Раскладка */

margin: 10px;

padding: 0;

/* Шрифты */

font: small Georgia, serif;

/* Цвета */

color: black;

background: white;

}

/* Заголовочная часть*/

#header

{

}

/* Обертка блоков sidebar и content */

#wrapper

{

float: left;

width: 100%;

}

/* Общие свойства боковой панели и содержания */

#sidebar, #content

{

color: #2A3D34;

}

/* Отступы для содержимого блоков sidebar и content */

DIV.wrap

{

padding:10px;

}

/* Боковая панель*/

#sidebar

{

float: left;

width: 30%;

background:#fff2a6;

}

/* Содержание */

#content

{

float: left;

width: 70%;

background: #d4dff3;

}

/* Подвал */

#footer

{

clear: both;

padding: 0 10px 10px 10px;

}

Отметим еще две неприятности, связанные с браузером IE.

Первая связана с тем, что IE не умеет считать.

Предположим, ширина блока wrapper равна 1000 пикселям. В этом случае IE не подкачает, он определит 300 пикселей (30%) для блока sidebar и 700 пикселей (70%) для блока content . В сумме получится как раз 1000 пикселей, никаких проблем нет.

Посмотреть (плавающие блоки/плавающие пропорциональные колонки/шаг 1).

Пусть теперь ширина блока wrapper равна 999 пикселям. Получаем: 30% от 999 равно 299.7 и, соответственно, 70% от 999 равно 699.3. Нормальные браузеры получают 300 и 699 пикселей, IE всегда округляет в большую сторону. У него получается 300 и 700. Для блока content места справа от sidebar не хватает, и он сваливается вниз.

Неприятности можно избежать, если для второго блока задать ширину, равную не 70%, а только 69.9%.

Существуют и другие решения, но это самое простое (расплата — лишний небольшой отступ справа).

Вторая неприятность связана с тем, что в узких окнах разметка рушится: блоки начинают наползать друг на друга, а в IE блок content опять скачет вниз.

Для нормальных браузеров решение очень простое — нужно задать для BODY свойство min-width . Для IE приходится идти на хитрости при помощи свойства expression (IE7 уже научили понимать свойство min-width ).

Таким образом, получаем следующие правки в стилевых определениях:

BODY

{

/* Раскладка */

margin: 10px;

padding: 0;

min-width: 640px;

width: expression(documentElement.clientWidth < 700 ? '700px' : 'auto');

/* Шрифты */

font: small Georgia, serif;

/* Цвета */

color: black;

background: white;

}

/* Боковая панель*/

#sidebar

{

float: left;

width: 30%;

background:#fff2a6;

}

/* Содержание */

#content

{

float: left;

width: 69.9%;

background: #d4dff3;

}

Значения 700px для IE и 640px для других браузеров подобраны экспериментальным путем.

В IE был установлен самый крупный шрифт (в меню Вид/Размер шрифта), затем уменьшалась ширина окна, пока блок content не стал сваливаться вниз.

Займемся теперь фоновой полоской, создающей ложные колонки. В резиновом макете ширина блока wrapper переменная, поэтому подход, описанный в разделе “Плавающие колонки фиксированной ширины” (см. 3.2.3), не подходит.

Дуглас Бауман и Эрик Мейер доработали идею Дэна Седерхольма для пропорциональных колонок и предложили (в 2004 году) способ, описанный ниже (ссылка [14]).

Общая длина фоновой полоски делается настолько большой, например 3000 пикселей, чтобы она смогла покрыть ширину окна на огромном мониторе.

Полоска делится разными цветами в таком же процентном отношении, что и наши колонки (30:70% ). То есть для первого цвета в нашем случае отводится 900 пикселей, а для второго — 2100.

Для вывода полоски используется стилевое правило:

/* Обертка блоков sidebar и content */

#wrapper

{

background: url(pic/wrapbg.png)

30% top repeat-y;

}

Две вертикали, одна — на элементе, другая — на картинке, проведенные в 30% от левого края (соответственно, элемента и картинки), будут совпадать при любой ширине элемента wrapper .

Это как раз то поведение, которое нам нужно для создания ложных колонок.

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

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

Пусть, например, для элемента задано:

background: url(pic.png) 50% 50%

Стартовое положение определится таким образом, чтобы центр элемента совместился с центром картинки:

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

Загрузите макет и проверьте его работу в окнах разной ширины.

Посмотреть (плавающие блоки/плавающие пропорциональные колонки/шаг 2).

Описанный метод легко обобщить на любое число колонок.

Например, для трех колонок задаем два оберточных блока и две фоновые картинки: первую — для блока wrapper1 , содержащего первую колонку и блок wrapper2 , вторую — для блока wrapper2 , содержащего вторую и третью колонки.

3.3. Пример (разработка сайта “Фотография”)

Построим сайт, посвященный делу, к которому Роботландия давно стремится, но за чередой других забот никак не находит времени им заняться, — фотографии!

Этапы разработки:

· 3.3.1. Набросок результата и блочный макет

· 3.3.2. Без CSS

· 3.3.3. Выбираем модель и задаем геометрию

· 3.3.4. Заголовок

· 3.3.5. Меню

· 3.3.6. Боковая панель

· 3.3.7. Наполняем содержимым главную страницу

· 3.3.8. Инварианты

· 3.3.9. Галерея работ

· 3.3.10. Результат

· 3.3.11. Неидеальные итоги

Если вам не терпится посмотреть, что получится, пожалуйста: посмотреть (плавающие блоки/сайт “Фотография”/шаг 8).

Работают все ссылки в главном (горизонтальном) меню и ссылка “Портрет” на боковой панели. Результат немного отличается от первоначальной задумки, но это и неудивительно! Ведь в процессе работы над проектом понемногу меняются и представления о нем.

3.3.1. Набросок результата и блочный макет

Прикинем страницу сайта в графическом редакторе, изобразим упрощенный блочный макет и запишем для него скелетный HTML:

<BODY>

<DIV id="header">Заголовок</DIV>

<DIV class="menu">Меню</DIV>

<DIV id="wrapper">

<DIV id="content">

<DIV сlass="wrap">Содержание</DIV>

</DIV>

<DIV id="sidebar">

<DIV class="wrap">Панель</DIV>

</DIV>

</DIV>

<DIV class="menu">Меню</DIV>

<DIV id="footer">Подвал</DIV>

</BODY>

Обертка wrapper позволит создать ложные колонки (см. 3.2.3), а блоки <DIV class="wrap"> (дополнительно оборачивающие содержимое блоков content и sidebar ) сделают ненужным трюк для IE с блочной моделью (см. 3.2.3).

Заметим, что блоки меню помечены классом class="menu" , а не идентификатором, так как идентификатор может быть на странице только один (см. 2.1.1).

Оборачивая с одержимое блоков content и sidebar в дополнительные DIV'ы, мы нарушаем правило “в HTML не место визуальным конструкциям”. Но трюки с блочной моделью — тоже плохо. Увы, мы живем не в идеальном мире, и рафинированный подход не всегда является правильным (и даже возможным). Если мы позволили себе использовать обертку wrapper ради создания ложных колонок, то нелогично отказываться от дополнительных DIV 'ов, упрощающих создание отступов, взывая к структурной чистоте HTML (см. 3.3.11).

3.3.2. Без CSS

Вот так это выглядит в потоке.

Горизонтальные линии <HR class="none"> введены для улучшения восприятия страницы в браузерах без CSS, мы так уже делали (см. 2.4.3) при построении сайта Роботландии. Для класса none будет определено свойство display:none , которое удалит линии с экрана в браузерах с поддержкой стилевых таблиц.

Посмотреть (плавающие блоки/сайт “Фотография”/шаг 1).

3.3.3. Выбираем модель и задаем геометрию

Зададим для BODY фиксированную ширину в 760 пикселей и будем выравнивать этот блок по центру HTML в горизонтальном направлении.

Блоки header, menu, footer оставим в потоке, а колонки — блоки content (ширина 500 пикселей) и sidebar (ширина 260 пикселей) — сделаем плавающими.

Плавающей придется сделать и обертку wrapper , чтобы она не схлопывалась в потоке, а охватывала своих плавающих потомков (см. 3.1.1.7). Вспомним, что блок wrapper нужен для задания фона для ложных колонок (см. 3.2.3).

Запишем CSS:

HTML

{

text-align: center; /* Центрирование BODY для IE5 */

background: #F6F2E4;

}

BODY

{

/* Раскладка */

margin: 10px auto; /* BODY по центру HTML */

text-align: left; /* Восстановим выравнивание текста слева */

padding:0;

border: 1px solid #638F7B;

width: 760px;

/* Шрифты */

font: small Georgia, serif;

/* Цвета */

color:black;

background: white;

}

/* Обертка блоков sidebar и content */

#wrapper

{

float:left;

background: url(pic/wrapbg.png) repeat-y; /* Ложные колонки */

}

/* Общие свойства боковой панели и содержания */

#sidebar, #content

{

color: #2A3D34;

}

/* Отступы для содержимого блоков sidebar и content */

DIV.wrap

{

padding:10px;

}

/* Содержание */

#content

{

float:left;

width: 500px;

}

/* Боковая панель*/

#sidebar

{

float:left;

width: 260px;

}

/* Меню */

.menu

{

clear:both;

background: #fff2a6;

padding: 2px 10px;

}

/* Подвал */

#footer

{

padding: 2px 10px;

color:white;

background:#828377;

}

#footer A

{

color:yellow;

}

/* Для устранения элементов при включенном CSS */

.none { display:none }

Вот так это выглядит в окне нормального браузера.

Тень справа от колонки content создана на фоновой полоске wrapbg.png , которой мы замащиваем по вертикали оберточный блок wrapper , создавая ложные колонки.

Посмотреть (плавающие блоки/сайт “Фотография”/шаг 2)

А вот так это выглядит в IE.

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

Этот баг, известный под названием “ошибка дублирования символов браузерами IE6”, проявляется, когда между первым и последним плавающими элементами в HTML есть несколько комментариев. В нашем случае код выглядит так:

<!-- Обертка блоков content и sidebar -->

<DIV id="wrapper">

<!-- Панель основного содержания -->

<DIV id="content">

<DIV class="wrap">

Содержание

</DIV>

</DIV>

<HR class="none">

<!-- Боковая панель -->

<DIV id="sidebar">

<DIV class="wrap">

Боковая панель

</DIV>

</DIV>

</DIV>

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

Основные операции по разработке сайта проделаны: нарисован эскиз, выбрана модель (двухколонный вариант с фиксированной шириной блоков), заготовлены файлы HTML и CSS. Теперь можно постепенно продвигаться далее, занимаясь деталями проекта.

Небольшая переменка с кусочком сыра никому не повредит!

3.3.4. Заголовок

Вот такой заголовок мы собираемся сконструировать. Он состоит из одних картинок. Разберем его на составные части.

Фото можно сделать фоном для блока header и вывести этот фон без повторений, сместив стартовую точку в правый верхний угол (right top no-repeat).

Фоновой полоской замастим слева по вертикали блок H1 (left top repeat-y).

На этот фон выведем логотип (элемент IMG внутри H1). Записывать картинку в файл будем с правильного фона, чтобы создать верный антиалиасинг.

Заголовок сайта будет элементом IMG внутри H1.

Таким образом, HTML-код заголовочной части перепишем следующим образом:

<!-- Заголовочная часть -->

<DIV id="header">

<H1>

<IMG id="logo" src="pic/logo.png"

width=160 height=112

alt="Логотип" title="Логотип">

<IMG id="title" src="pic/title.png"

width=468 height=72

alt="Фотография" title="Фотография">

</H1>

</DIV>

Для блока header задали высоту, равную высоте фото, — 119 пикселей. Кроме того, задали position:relative, чтобы логотип и заголовок абсолютно позиционировать относительно этого блока (см. 2.1.1).

У блока H1 убрали внешние и внутренние отступы, установленные по умолчанию для этого элемента.

Логотип и заголовок абсолютно сместили в нужные позиции относительно блока header.

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

Разгадка проста: для картинок в блоке H1 задано абсолютное позиционирование, картинки вышли из потока, блок H1 оказался пустым и высота его схлопнулась в ноль.

Оформим запланированные стилевые определения:

#header

{

position:relative;

background: url(pic/image.jpg) right top no-repeat;

height:119px;

}

#header H1

{

margin:0; padding:0;

background: url(pic/h1bg.png) left top repeat-y;

}

#header #logo

{

position:absolute;

top:6px; left:10px;

}

#header #title

{

position:absolute;

top:22px; left:200px;

}

Чтобы предотвратить схлопывание блока H1 по вертикали, добавим в его стилевые правила указание высоты:

#header H1

{

height:119px;

margin:0; padding:0;

background: url(pic/h1bg.png) left top repeat-y;

}

Теперь заголовочная часть выводится правильно.

Но только не в IE! Что-то не сложилось со свойством определения позиции hasLayout (см. 2.5.2) у блока header. Высота для него уже задана, зададим еще ширину:

#header

{

position:relative;

background: url(pic/image.jpg)

right top no-repeat;

height:119px;

width:100%;

}


 

Ширина header и так 100%, явное указание width:100% — лишнее, но IE помогло. “Раз!
И боль пропала”:

Можно было бы всю заголовочную часть вывести одной картинкой. Это упростило бы верстку, но увеличило бы килобайтный объем графики. Сейчас все графические элементы в сумме дают 18 килобайт, единственная картинка весила бы 28. Ради экономии

Итак, заголовочная часть собрана из двух элементов IMG: логотипа и заголовка сайта. Эти картинки абсолютно позиционированы внутри блока header, для которого задано относительное позиционирование.

Фото справа выведено как фон блока header, а градиентная заливка под логотипом — как фон блока H1, вложенного в header. в 10 килобайт стоит немного потрудиться!

Посмотреть (плавающие блоки/сайт “Фотография”/шаг 3).

3.3.5. Меню

Построим меню на плавающих блоках. За основу возьмем разработку из раздела “Свойство float на практике” (см. 3.2.2).

HTML:

<UL class="menu">

<LI>Начало

<LI><A href="01.htm">Техника фотосъемки</A>

<LI><A href="02.htm">Основы мастерства</A>

<LI><A href="03.htm">Ссылки</A>

<LI><A href="04.htm">О сайте</A>

</UL>

CSS:

/* Меню */

.menu

{

float:left; /* Чтобы блок охватил своих потомков */

clear:both; /* Прерывание обтекания для вышерасположенных блоков */

width:100%; /* Растягиваем на всю ширину родителя */

margin:0; /* Убираем внешние отступы списка */

padding: 2px 0; /* Убираем лишние внутренние отступы списка */

background: #fff2a6; /* Фон меню */

border-top: 1px solid #638F7B; /* Рамка сверху */

border-bottom: 1px solid #638F7B; /* Рамка снизу */

list-style: none; /* Убираем маркеры */

}

.menu LI

{

float:left; /* Каждый элемент списка -- плавающий блок */

display:inline; /* Уловка для IE */

margin-left:10px; /* Расстояние между пунктами меню */

font-family: "Trebuchet MS", Arial, sans-serif; /* Шрифт */

font-size:90%; /* Размер шрифта */

}

/* Свойства ссылок */

A {text-decoration: none;} /* Отменяем подчеркивание всех ссылок */

.menu A:link {color:#0040ff} /* Цвет не посещенной ссылки */

.menu A:visited {color:purple} /* Цвет посещенной ссылки */

.menu A:hover /* Когда курсор над ссылкой */

{

color:white; /* Белый текст */

background: #ff7f00; /* На оранжевом фоне */

}

.menu A:active {color:red} /* Цвет активной ссылки */

Меню построено. Обратите внимание на невидимую вертикаль слева, отстоящую от рамки BODY на 10 пикселей. По ней выровнены все элементы: логотип, первый пункт меню, текст основной колонки и подпись в подвале.

Посмотреть (плавающие блоки/сайт “Фотография”/шаг 4).

3.3.6. Боковая панель

Информацию на боковой панели решено выводить в красивых блоках. Давайте посмотрим, как можно конструировать такие боксы при помощи CSS и фоновых картинок.

Сначала нарисуем бокс в графическом редакторе на том же фоне (это важно), который будет под ним на сайте.

Закругленные углы, градиент в верхней части, тени — красота!

Ширина бокса на сайте фиксированна, а высота должна подстраиваться под содержимое.

Бокс любой высоты можно собрать из трех частей: верхней, средней и нижней, если средней частью замащивать площадь бокса по вертикали.

В силу этих соображений отрежем от нарисованной картинки три нужные части и сохраним их в виде графических файлов.

Хорошим инструментом для нарезки частей служит приложение ImageReady.

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

Замастим box по вертикали фоновой полоской boxbg.gif.

Для его потомков (которые на экране располагаются “над” предком) зададим фоновые картинки:

topbg.gif для H3 и bottombg.gif для in .

Опишем структуру бокса таким кодом:

<DIV class="box">

<H3>Заголовок</H3>

<DIV class="in">

Содержимое бокса

</DIV>

</DIV>

Блоки находятся в потоке, поэтому они занимают по горизонтали всю ширину родителя, а их высоты определяются содержимым.

Получаются такие стилевые правила:

/* Бокс для боковой панели */

.box

{

width:233px;

background: url(pic/boxbg.gif) top repeat-y;

margin-bottom:5px;

}

.box H3

{

margin:0;

padding:6px 10px 4px 10px;

background: url(pic/topbg.gif) top no-repeat;

}

.box .in

{

padding:0px 20px 20px 10px;

background: url(pic/bottombg.gif) bottom no-repeat;

}

Вот так это выглядит на странице сайта.

Чтобы подровнять бокс по ширине боковой панели, изменим отступы для блока wrap (этот блок оборачивает содержимое боковой панели):

#sidebar .wrap

{

padding-left:20px;

padding-right:0px;

}

Теперь все замечательно.

В итоге для боковой панели разработан красивый бокс — с закругленными углами, градиентной заливкой и тенями.

Бокс построен из трех картинок, которые выводятся в виде фоновых заливок средствами CSS. Ширина бокса фиксированна (ибо фиксированна ширина его родительского элемента), высота определяется по содержимому.

Посмотреть (плавающие блоки/сайт “Фотография”/шаг 5).

3.3.7. Наполняем содержимым главную страницу

Проделана большая подготовительная работа. Самое время посмотреть, как все это будет смотреться в совокупности на странице сайта.

Запишем реальное содержание в основную колонку и расположим на боковой панели три бокса с дополнительными (по отношению к главному меню) ссылками.

Изменения в HTML очевидны. Что касается CSS, то добавлено правило, регулирующее поведение списка ссылок в боксе:

.box UL

{

margin:0;

padding:0 0 10px 0;

list-style: none; /* Убираем маркеры */

}

Посмотреть (плавающие блоки/сайт “Фотография”/шаг 6).

3.3.8. Инварианты

Так же, как это было сделано при построении сайта “Роботландия” (см. 2.6.3), выделим постоянные части страниц в отдельный файл JavaScript.

Для главной страницы получается такой HTML:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"

"http://www.w3.org/TR/html4/loose.dtd">

<HTML lang="ru">

<HEAD>

<META http-equiv="Content-Type" content="text/html; charset=windows-1251">

<META http-equiv="imagetoolbar" content="no">

<META name="keywords" content="Фотография, Роботландский университет">

<META name="description" content="Сайт РУ, посвященный фотографии.">

<META http-equiv="author" content="А.Дуванов">

<META http-equiv="Content-Style-Type" content="text/css">

<STYLE type="text/css">@import url(main.css);</STYLE>

<SCRIPT language="JavaScript" type="text/javascript" src="main.js"></SCRIPT>

<TITLE>Фотография</TITLE>

</HEAD>

<BODY>

<SCRIPT language="JavaScript" type="text/javascript">

PutHeader(0); // Заголовочная часть

PutМеню(0); // Меню

</SCRIPT>

<DIV id="wrapper">

<DIV id="content">

<DIV class="wrap">

<H2>Вступление</H2>

<P>

...

</DIV>

</DIV>

<SCRIPT language="JavaScript" type="text/javascript">

PutSizebar(-1); // Боковая панель

</SCRIPT>

</DIV>

<SCRIPT language="JavaScript" type="text/javascript">

PutМеню(0); // Меню

PutFooter(); // Подвал

</SCRIPT>

</BODY>

Поясним, что делает каждая функция, замещающая фрагмент HTML-кода.

3.3.8.1. Подвал

Функция PutFooter — самая простая. Она собирает в строку str HTML-код подвала и выводит его на экран при помощи метода document.write:

// Подвал

function PutFooter()

{

var str = '<HR class="none"><DIV id="footer">';

str += '&copy; 2009 Роботландский университет, А. А. Дуванов: <A ' +

'href="mailto:kurs@robotland.pereslavl.ru"
                >kurs@robotland.pereslavl.ru</A>';

str += '</DIV>';

document.write(str);

}

Отметим, что в код подвала добавлен элемент HR для браузеров, которые не поддерживают CSS. Браузеры с CSS этот элемент проигнорируют благодаря свойству display:none, указанному в определении класса none.

3.3.8.2. Заголовочная часть

Функция PutHeader обеспечивает вывод заголовочной части страницы. Любое значение параметра page, отличное от нуля, делает логотип ссылкой на главную страницу. Кроме того, на главной странице атрибут title будет иметь значение "Логотип сайта Фотография", на остальных — "На главную".

// Заголовочная часть

// page=0 для главной страницы

function PutHeader(page)

{

var str = '<DIV id="header"><H1>';

if (page) str += '<A href="index.htm">';

str += '<IMG id="logo" src="pic/logo.png" width=160 height=112 alt="Логотип" border=0 title=';

str += page ? '"На главную">' : '"Логотип сайта Фотография">';

if (page) str += '</A>';

str += '<IMG id="title" src="pic/title.png" width=468 height=72 alt="Фотография" title="Фотография">';

str += '</H1></DIV>';

document.write(str);

}

3.3.8.3. Главное меню

Функция PutМеню вызывается в HTML два раза: один раз в начале страницы, другой — в конце.

Если значение параметра page совпадает с индексом страницы по массиву ItemsMenu, соответствующая позиция в меню будет обычным текстом (не ссылкой).

Когда значение параметра page выпадает из диапазона индексов этого массива (например, page равно -1), все пункты меню будут кодироваться как ссылки. Такой вариант необходим для страниц, которые вызываются по ссылкам из боковой панели.

// Главное Меню

// Пункты меню

// -----------

var ItemsMenu = [

["Начало", "index.htm"],

["Техника фотосъемки", "01.htm"],

["Основы мастерства", "02.htm"],

["Ссылки", "03.htm"],

["О сайте", "04.htm"]

];

// Построение меню

// page -- номер текущей страницы (нумерация с нуля)

// Если page = -1, все позиции в меню ссылки

function PutМеню(page)

{

var str = '<HR class="none"><UL class="menu">';

for (var i = 0; i < ItemsMenu.length; i++)

{

str += '<LI>';

if (i != page) str += '<A href="' + ItemsMenu[i][1] + '">';

str += ItemsMenu[i][0];

if (i != page) str += '</A>';

str += '</LI> '; // Пробел устраняет дублирование символов в IE6

}

str += '</UL>';

document.write(str);

}

Пробел после закрывающего тега </LI> пришлось добавить, так как без него браузеру IE6 становится плохо, и он дублирует последнюю букву в отдельную строку на конце страницы, как это уже описывалось ранее (см. 3.3.3). Вы можете убрать этот пробел и посмотреть, что получится.

О причинах такого поведения IE спрашивать меня не надо! Рецепт лечения был найден методом ненаучного “тыка”.

3.3.8.4. Боковая панель

Здесь пришлось повозиться побольше.

Из всех ссылок на боковой панели был сформирован массив LinksSizebar:

// Ссылки на боковой панели

// ------------------------

var LinksSizebar = [

["Портрет", "work01.htm"],

["Пейзаж", "work02.htm"],

["Школа", "work03.htm"],

["Спорт", "work04.htm"],

["Животные", "work05.htm"],

["Разное", "work06.htm"],

["Фотокамера", "faq01.htm"],

["Съемка", "faq02.htm"],

["Обработка", "faq03.htm"],

["Печать", "faq04.htm"],

["&laquo;Конкурс&raquo;", "contest.htm"]

];

Затем для формирования списка ссылок была записана вспомогательная функция PutUlSizebar:

// Построение списка ссылок

// start -- индекс первой ссылки в массиве LinksSizebar

// end -- индекс последней ссылки в массиве LinksSizebar

// page -- номер ссылки, соответствующей текущей странице или -1

function PutUlSizebar(start, end, page)

{

var str ='<UL>';

for (var i = start; i <= end; i++)

{

str += '<LI>';

if (i != page) str += '<A href="' + LinksSizebar[i][1] + '">';

str += LinksSizebar[i][0];

if (i != page) str += '</A>';

str += '</LI> ';

}

str += '</UL>';

return str;

}

Вызов PutUlSizebar(0, 5, 2) вернет, например, код списка со ссылками, расположенный в первом боксе “Работы автора”. При этом позиция “Школа” будет выведена как обычный текст (page=2 соответствует третьему элементу массива LinksSizebar, так как нумерация начинается с нуля).

И, наконец, был написан бесхитростный код основной функции PutSizebar:

// Построение боковой панели

// page -- номер ссылки, соответствующей текущей странице или -1

function PutSizebar(page)

{

var str = '<HR class="none">';

str +=

' <DIV id="sidebar"> ' +

' <DIV class="wrap"> ' +

' ' +

' <DIV class="box"> ' +

' <H3>Работы автора</H3> ' +

' <DIV class="in"> ' +

PutUlSizebar(0, 5, page)+

' <IMG src="pic/box1.png" width=200 height=81 ' +

' alt="Работы автора" title="Работы автора"> ' +

' </DIV> ' +

' </DIV> ' +

' ' +

' <DIV class="box"> ' +

' <H3>Вопросы и ответы</H3> ' +

' <DIV class="in"> ' +

PutUlSizebar(6, 9, page)+

' <IMG src="pic/box2.png" width=200 height=81 ' +

' alt="Вопросы и ответы" title="Вопросы и ответы"> ' +

' </DIV> ' +

' </DIV> ' +

' ' +

' <DIV class="box"> ' +

' <H3>Фотоконкурс</H3> ' +

' <DIV class="in"> ' +

'На сайте работает постоянный конкурс для любителей. ' +

'Подробности на странице ';

if (10 != page) str += '<A href="' + LinksSizebar[10][1] + '">';

str += LinksSizebar[10][0];

if (10 != page) str += '</A>';

str += '.' +

' </DIV> ' +

' </DIV> ' +

' ' +

' </DIV> ' +

' </DIV> ';

document.write(str);

}

Что получено в итоге.

За визуальный образ страницы отвечает стилевой файл main.css. Это один файл на весь сайт. Очень удобно! Вместо того чтобы “лопатить” сотни HTML (например, для изменения цвета ссылок), мы наносим легкий “укол” единственному CSS.

С той же целью все инварианты собраны в единственный файл JavaScript. Если в меню теперь появится новый пункт, мы не будем часами “разгребать” HTML, а выполним правку в одном-единственном JavaScript.

В отдельный файл вынесены:

· заголовочная часть;

· меню;

· подвал;

· вся боковая панель.

Посмотреть (плавающие блоки/сайт “Фотография”/шаг 7).

На странице, к счастью, ничего не изменилось, мы видим ту же картинку на экране, но в ее формировании теперь участвуют скрипты.

3.3.9. Галерея работ

Страницы, которые вызываются по ссылкам в боксе “Работы автора”, представляют собой галерею фотографий.

Галерею проще всего организовать с помощью таблицы. Структура двух табличных строк показана на рисунке.

Эти две строки будут повторяться в таблице, пока не закончатся фотографии.

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

Кодируется таблица так:

<TABLE class="gallery" cellspacing=10>

<TR class="pics">

<TD>

<IMG src="pic/work0101.jpg"

width=170 height=255

alt="Портрет скульптора" title="Портрет скульптора">

</TD>

<TD>

<IMG src="pic/work0102.jpg"

width=170 height=255

alt="Портрет рыбака" title="Портрет рыбака">

</TD>

</TR>

<TR>

<TD>Портрет скульптора</TD>

<TD>Портрет рыбака</TD>

</TR>

</TABLE>

CSS:

/* Таблица занимает по ширине все

доступное пространство */

.gallery

{

width: 100%;

margin: 0; padding: 0;

border: none;

text-align: center;

}

/* Ячейка занимает половину ширины

таблицы и имитирует паспарту */

.gallery .pics TD

{

width: 50%;

margin: 0;

padding: 20px;

background:#e5e5e5;

border: 1px solid black;

}

/* Картинку окружает белая рамка */

.gallery .pics TD IMG

{

border: 2px solid white;

}

Немного CSS, и каждая ячейка нечетной строки таблицы превращается в паспарту (“картонную” рамку).

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

Максимальная ширина картинки, задаваемой элементом IMG, в данной разметке составляет 170 пикселей, высота не ограничена. Картинка будет автоматически центрироваться по центру паспарту.

Стилевые определения, относящиеся к галереям, поместим в отдельный стилевой файл work.css. Подсоединять этот файл будем только к страницам из бокса “Работы автора”. Тем самым, не будем загромождать основной стилевой файл main.css, что упростит работу с ним и немного ускорит загрузку сайта.

Страница work01.htm (соответствует пункту “Портрет”) будет иметь вид:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"

"http://www.w3.org/TR/html4/loose.dtd">

<HTML lang="ru">

<HEAD>

<META http-equiv="Content-Type" content="text/html; charset=windows-1251">

<META http-equiv="imagetoolbar" content="no">

<META name="keywords" content="Фотография, Роботландский университет">

<META name="description" content="Сайт РУ, посвященный фотографии.">

<META http-equiv="author" content="А.Дуванов">

<META http-equiv="Content-Style-Type" content="text/css">

<STYLE type="text/css">@import url(main.css);</STYLE>

<STYLE type="text/css">@import url(work.css);</STYLE>

<SCRIPT language="JavaScript" type="text/javascript" src="main.js"></SCRIPT>

<TITLE>Портрет</TITLE>

</HEAD>

<BODY>

<SCRIPT language="JavaScript" type="text/javascript">

PutHeader(-1); // Заголовочная часть

PutМеню(-1); // Меню

</SCRIPT>

<DIV id="wrapper">

<DIV id="content">

<DIV class="wrap">

<H2>Портрет</H2>

<TABLE class="gallery" cellspacing=10>

...

</TABLE>

</DIV>

</DIV>

<SCRIPT language="JavaScript" type="text/javascript">

PutSizebar(0); // Боковая панель

</SCRIPT>

</DIV>

<SCRIPT language="JavaScript" type="text/javascript">

PutМеню(-1); // Меню

PutFooter(); // Подвал

</SCRIPT>

</BODY>

</HTML>

Обратите внимание в этом коде на следующие моменты:

· в головной части кода подключаются два стилевых файла — основной main.css и вспомогательный work.css ;

· вызов функции PutМеню выполняется с параметром -1 , так как на странице “Портрет” все пункты главного меню должны быть ссылками;

· вызов функции PutSizebar выполняется с параметром 0 , так как на странице “Портрет” первая ссылка на боковой панели (нумерация с нуля) должна превратиться в обычный текст.

На иллюстрации показана страница “Портрет”.

Соответствующий пункт меню в боксе “Работы автора” не является ссылкой. Все остальные пункты во всех других меню ссылками являются.

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

Галерея построена на таблице (элемент TABLE). Ячейки TD, содержащие иллюстрации, оформлены в виде паспарту при помощи CSS.

“Вес” всех шести работ, представленных на странице, в сумме равен 36 килобайтам, что не критично. Но если фотографий много, их геометрический размер в галерее нужно уменьшать (например, помещать в строке не 2, а 4 работы).

Уменьшать картинки нужно, конечно, в графическом редакторе с последующей оптимизацией, а не атрибутами тега IMG.

Посмотреть (плавающие блоки/сайт “Фотография”/шаг 8).

3.3.10. Результат

Проделана большая работа: построен макет сайта, в котором за внешний вид отвечают два файла CSS: один общего назначения (main.css), другой привязан к страницам бокса “Работы автора” (work.css). В последнем файле формулируются несколько дополнительных правил, связанных с построением галереи.

Кроме того, в отдельный файл main.js вынесены инварианты страниц: заголовочная часть, меню, боковая панель и подвал.

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

Настоящее содержание страниц требует дополнительной и отдельной работы, поэтому воспользуемся генератором текста (ссылка [15]) для создания на страницах “рыбного филе”.

Работа несложная, но на последней странице “О сайте” возникла проблема, с которой мы уже неоднократно сталкивались.

Да, да! Это снова IE! Он опять стал дублировать последний символ из меню в новом блоке, который специально, паршивец, для этого создал!

Никакие “заклинания”, направленные на установку свойства hasLayout (см. 2.5.2), не помогли, пришлось действовать по-другому.

Видим, что дефект IE проявляется только на последней странице, когда меню заканчивается не ссылкой. Ну что ж, дополним меню еще одним пунктом-ссылкой. Чтобы этот лишний пункт не отображался на экране, снабдим его классом none, который был создан для сокрытия элементов HR в браузерах, поддерживающих CSS (см. 3.3.2).

Внесем соответствующее изменение в функцию PutМеню:

// Построение меню

// page -- номер текущей страницы (нумерация с нуля)

// Если page = -1, все позиции в меню ссылки

function PutМеню(page)

{

var str = '<HR class="none"><UL class="menu">';

for (var i = 0; i < ItemsMenu.length; i++)

{

str += '<LI>';

if (i != page) str += '<A href="' + ItemsMenu[i][1] + '">';

str += ItemsMenu[i][0];

if (i != page) str += '</A>';

str += '</LI> '; // Пробел устраняет дублирование символов в IE6

}

str += '<LI class="none"><A href="#"></A></LI> '; // Тоже для IE6

str += '</UL>';

document.write(str);

}

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

Построение учебного примера завершено.

Работают все ссылки в главном (горизонтальном) меню и ссылка “Портрет” на боковой панели.

Много времени потрачено на борьбу с ошибками браузера IE. Если бы все пользователи сети сами писали сайты, браузер IE мгновенно прекратил бы свое существование. Но обычные сетевые странники даже не подозревают, сколько усилий приложено, чтобы угодить их любимому браузеру.

Посмотреть (плавающие блоки/сайт “Фотография”/шаг 8).

3.3.11. Неидеальные итоги

Построен сайт, визуальный образ которого не содержится в HTML.

Хочется трижды крикнуть “ура” и выкинуть веселое коленце по этому поводу.

Но нет, увы! К сожалению, визуальные следы в нашем HTML присутствуют. Посмотрите на этот код:

<!-- Обертка блоков content и sidebar -->

<DIV id="wrapper">

<!-- Панель основного содержания -->

<DIV id="content">

<DIV class="wrap">

Содержание

</DIV>

</DIV>

<!-- Боковая панель -->

<DIV id="sidebar">

<DIV class="wrap">

Боковая панель

</DIV>

</DIV>

</DIV>

Блок wrapper оборачивает колонки. Мало того, содержимое каждой из них завернуто еще и в новый полушубок wrap. Эти оболочки имеют структурный характер? Нет, они отвечают за внешний вид. Мы их ввели, ибо браузеры еще не поддерживают нормальную верстку колонок, обозначенную в новых свойствах CSS3, а также потому, что блочная модель браузера IE отличается от стандартной.

С точки зрения строгой структурной разметки в HTML должен остаться только такой простой код:

<!-- Панель основного содержания -->

<DIV id="content">

Содержание

</DIV>

<!-- Боковая панель -->

<DIV id="sidebar">

Боковая панель

</DIV>

При проектировании галереи использован элемент TABLE:

<TABLE class="gallery" cellspacing=10>

<TR class="pics">

<TD>

<IMG src="pic/work0101.jpg"

width=170 height=255

alt="Портрет скульптора" title="Портрет скульптора">

</TD>

<TD>

<IMG src="pic/work0102.jpg"

width=170 height=255

alt="Портрет рыбака" title="Портрет рыбака">

</TD>

</TR>

<TR>

<TD>Портрет скульптора</TD>

<TD>Портрет рыбака</TD>

</TR>

</TABLE>

Информация, представленная в этой таблице, является табличной с большой натяжкой, то есть таблица служит здесь визуальным средством, а не структурным. Кроме того, в теге TABLE оставлен явно визуальный атрибут cellspacing, так как аналогичное CSS-свойство border-spacing не поддерживается браузером IE.

Наконец, в HTML отставлены явно визуальные атрибуты картинок: width и height, ибо размечать все картинки идентификаторами или классами, а потом задавать для них width и height в CSS было бы очень громоздко.

Что делать! Мы живем в неидеальном мире, и рафинированный подход не всегда осуществим и даже разумен. По этому поводу не стоит сильно горевать, а тем более ради “чистоты” структуры накручивать многоэтажные заплаты на слабом еще теле CSS.

Мы и так сделали очень много! И будем рассчитывать в будущем сделать еще больше. Ибо сам принцип: “развести структуру и представление по разным кроватям” — хорош и продуктивен!

А.. А.. Дуванов,
г. Переславль-Залесский

TopList