Уявний монументалізм абсолютного позиціонування
Очевидним недоліком HTML є відсутність можливості однозначно і просто позиціонувати блок там, де хочеться. Наприклад, я хочу, щоб блок був зміщений на 250 пікселів від верхнього краю, і на 45 від лівого. Звичайно, в HTML можна реалізувати таке положення блоку за допомогою таблиць і розпірок, але хочеться простоти. У CSS така можливість забезпечується властивістю position, найпопулярніше значення у якої absolute. Для завдання зміщення існують властивості top і left. Моє бажання за допомогою каскадних таблиць стилів реалізується ось так:
#block {position: absolute; top: 250px; left: 45px}
А в коді документа дуже простий блок:
<div id="block"> зміст блоку </div>
Якщо повернутися до таблиць і спробувати позиціонувати такий же блок з їх допомогою, то код трохи ускладниться:
<table cellpadding="0" cellspacing="0"> <tr> <td width="45"></td> <td> <img src="/imgs/web/dubakov0.gif" width="1" height="250"><br> <div> зміст блоку </div> </td> </tr> </table>
Перша комірка таблиці використовується для відступу ліворуч, а відступ зверху формується розпіркою. Пристойна різниця в коді чи не так? Звичайно, в реальному житті такої переваги і каскадних таблиць стилів зазвичай не буває, але все ж економія може бути суттєвою.
Отже, у нас є три чарівних властивості, які дозволяють точно позиціонувати блоки. Виникає питання, відносно чого задається зсув? Варіанта два: або відносно вікна браузера, або відносно іншого позиціонованого блоку. Якщо один блок вкладений в інший, і обидва з них мають оголошення position: absolute, то зсув другого блоку буде здійснюватися відносного першого. Давайте краще на прикладі, а то занадто складно звучить. Припустимо, є два блоки:
<div id="left-col"> <div id="news"> </div> </div>
Стилі на блоки наступні:
#left-col {position: absolute; top: 20px; left: 10px}
#news {position: absolute; top: 20px; left: 10px}
Давайте подивимося, якими будуть абсолютні значення відступів відносно вікна браузера. Перший блок буде на 20 пікселів відбитий від верхнього краю і на 10 від лівого. Другий блок буде відбитий на 40 пікселів від верхнього краю і на 20 від лівого. Тобто він зміщується на зазначені значення не від самого вікна браузера, а відносно першого блоку. А якщо б блоки не були вкладеними:
<div id="left-col"> </div> <div id="news"> </div>
То вони накладалися б один на одного. Сподіваюся, зараз все стало зрозуміло.
У нас є інструмент, який, за ідеєю, може відмінно замінювати табличну верстку. Правда, чому б і ні? Адже він дозволяє з точністю до пікселя позиціонувати блоки, не використовуючи розпірки. Давайте перевіримо можливості абсолютного позиціонування на нашому прикладі. Зараз ми спробуємо зверстати сторінку на основі CSS-блоків і CSS-позиціювання.
Поверніться до початку глави і подивіться на мал.9.1, якщо ще не запам’ятали всіх деталей. Як не крути, виходить три окремі головні блоки: шапка, основний з трьома колонками і копірайт. Ми вже верстали кожен з цих блоків за допомогою таблиць, а зараз спробуємо CSS. Зауважу, що відразу я буду створювати блоки згідно специфікації, тобто для браузерів Mozilla і Opera. Адаптацією коду під Internet Explorer займемося трохи пізніше.
Як і при табличній верстці, верхній великий блок краще розбити на два маленьких. Перший блок назвемо top-left, а другий – top-right. Перший блок містить логотип, а другий помаранчеву плашку і слово «новини». Почнемо з верхнього лівого блоку:
<div id="top-left"> <img src="/imgs/web/dubakovlogo.gif" border="0" alt=""> </div>
Зверніть увагу, що для картинки я вже не вказую атрибути width і height, тому що немає таблиць. Вже немає необхідності вказувати розміри картинок для того, щоб таблиці швидше оброблялися браузером.
Залишилося написати стилі для блоку. Ми знаємо, що ширина блоку 517 пікселів, а висота дорівнює висоті логотипу, тобто 74 пікселя. Пишемо:
#top-left {width: 517px; height: 74px}
Цей перший блок навіть не треба позиціонувати. Дійсно, все рівно зміщення зліва і зверху дорівнюють нулю. Потім треба зробити чорну лінію, яка перетинає весь наш блок. Коли ми верстали таблицями, то робили лінію за допомогою фонового малюнка, заввишки 50 пікселів. Він дублювався у комірці таблиці й таким чином створювалася лінія. У CSS є можливість розмножувати фоновий малюнок вздовж необхідної осі. Більше того, є можливість точно позиціонувати фон, тобто зміщувати малюнок на необхідну кількість пікселів від верхнього краю блоку. Все це дозволяє нам в якості фонового малюнка взяти всього одну чорну крапку розміром 11 пікселів! Ми зміщуємо фон на 49 пікселів щодо верхнього краю блоку:
#top-left {
background: url(i/bd.gif) repeat-x 0px 49px;
width: 517px; height: 74px}
З фоном все гаразд, але логотип треба відбити на 30 пікселів. Раніше ми робили це за допомогою атрибуту hspace тега <img>, але тепер для блоку top-left можна просто поставити лівий відступ необхідної ширини:
#top-left {
background: url(i/bd.gif) repeat-x 0px 49px;
width: 517px; height: 74px;
padding-left: 30px}
Але я забув, що відступи впливають на сумарну ширину блоку. Тобто мені зараз треба відняти від значення властивості width рівно 30 пікселів:
#top-left {
background: url(i/bd.gif) repeat-x 0px 49px;
width: 487px; height: 74px;
padding-left: 30px}
У результаті таких стилів перший блок буде виглядати точно так само, як і при верстці таблицями.
Створюємо правий блок:
<div id="top-right"> </div>
Ми знаємо, що його ширина 233 пікселя, а висота 32 пікселя. Ось зараз нам знадобиться і позиціонування. Раз ширина першого блоку 517 пікселів, то другий блок треба зрушити вліво рівно настільки ж.
#top-right {
width: 233px; height: 32px;
position: absolute; left: 517px}
Поки я точно не знаю, яке потрібне вертикальне зміщення. Тому я поки відкладаю сам блок і переходжу до його вмісту. Плашку я зроблю на чистому CSS без всяких картинок. Ну, а «новини» доведеться вставити картинкою. HTML-код буде наступний:
<div id="top-right"> <div class="rectangle"></div> <img src="/imgs/web/dubakovnews.gif" border="0" alt=""> </div>
Тепер треба написати стилі на клас rectangle (так я назвав плашку). Ширина плашки рівно 233 пікселя, а загальна висота 19. Але я знаю, що блок повинен мати рамку товщиною 1 піксель, значить значення властивостей height і width треба вказувати на 2 пікселі менше (щоб компенсувати ширину рамки). Виходить ось так:
.rectangle {
width: 231px; height: 17px;
border: 1px solid #000}
Після цього залишається тільки поставити помаранчевий фон:
.rectangle {
background: #F90;
width: 231px; height: 17px;
border: 1px solid #000}
Поточний результат показаний на рис. 9.4. Для наочності блоки виділені пунктирною лінією:
Мал. 9.4 Нестиковка верхніх блоків
Як бачите, за замовчуванням другий блок позиціонується точно під першим. Ми явно для блоку top-right не задали зміщення відносно верхнього краю, а крім чисельних значень властивості top і left можуть приймати значення auto. Щоб краще розібратися, як воно діє на блок, треба трохи заглибитися в теорію.
Ліричний відступ про нормальний потік
Почнемо з того, що існує поняття нормального потоку. Нормальний потік – це спосіб форматування блоків браузером за замовчуванням, тобто коли немає ніяких інших схем позиціонування. Таблична верстка відноситься до нормального потоку, тоді як CSS-позиціювання – ні.
При форматуванні блоків у нормальному потоці браузер керується лише двома правилами: кожен блоковий елемент починається з нового рядка, кожен рядковий елемент переноситься на новий рядок в міру необхідності. До блокових елементів відносяться <p>, <table>, <h1> і т.д. До рядкових елементів – <b>, <i>, <sup> і т.д. Спеціально для каскадних таблиць стилів створили два елементи:
- <div> – Загальний блоковий елемент
- <span> – Загальний рядковий елемент
За допомогою даних елементів і стилів можна створювати будь-які блоки, які не передбачені мовою HTML. Наприклад, блок новин можна створити ось так:
<div id="news"></div>
А блок для виділення подати ось так:
<span class="date"></span>
Нормальний потік дуже простий для розуміння. Якщо ви в коді напишете послідовно два блокових елемента, то на сторінці вони розташуються рівно один під одним. Якщо ви напишете два строкових елемента, то вони розташуються в один рядок.
У нашому випадку ми маємо два блоки, які в коді слідують один за одним. Якби все відбувалося в рамках нормального потоку (тобто не було вказано position: absolute), то картина була б такою, як показано на мал. 9.5:
Мал.9.5 Блоки в нормальному потоці
Більше говорити про нормальний потік немає сенсу, так що повернемося до абсолютного позиціонування.
При абсолютному позиціонуванні елемент виривається з нормального потоку і розміщується у відповідності з вказаними значеннями зсувів. Наприклад, якщо в коді є два блоки, і другий блок позиціонується таким чином:
#block2 {position: absolute; left: 0px; top: 0px}
то в результаті другий блок буде накладено на перший. Ми впритул підібралися до відповіді на питання, чому в нашому прикладі другий блок хоч і зміщений вліво як треба, але по вертикалі все одно перебуває під першим блоком. Справа в тому, що значення auto позиціонує блок так, як ніби він знаходиться в нормальному потоці. Тобто виходить, що ми задали зсув ліворуч 517 пікселів, і блок справді змістився, але зсув зверху ми не задали взагалі, а за замовчуванням використовується top: auto. Якщо ми хочемо, щоб другий блок мав точний відступ зверху, то треба його поставити. Методом підбору можна визначити, що для правильного стикування блоків верхнє зміщення повинно бути 49 пікселів:
#top-right {
width: 233px; height: 32px;
position: absolute; left: 517px; top: 49px}
У результаті блоки будуть стикуватися абсолютно правильно, що й видно на мал. 9.6:
Мал. 9.6 Правильна стиковка двох блоків шапки
Підведемо проміжний підсумок. Якщо при верстці таблицями ми використовували 5 картинок, то зараз всього три. Крім того, одна картинка ну зовсім легка, адже це однопіксельний gif. Сам HTML-код шапки став більш простим:
<div id="top-left"> <img src="/imgs/web/dubakovlogo.gif" width="272" height="74" border="0" alt=""> </div> <div id="top-right"> <div class="rectangle"></div> <img src="/imgs/web/dubakovnews.gif" width="59" height="9" border="0" alt=""> </div>
Якщо виражати в цифрах, то було 392 байти, стало 206 байт. Але зате з’явився додатковий CSS-код обсягом 265 байт. Так що якщо брати вагу чистого коду документа, то він збільшився. Але вага картинок була 2,88 Кб, а стала 2,77 Кб. Якщо все підсумувати, то було 3341 байт, стало – 3307 байт. Звичайно, економія просто сміховинна, але поки на цьому не будемо загострювати уваги. З позитивних моментів можна відзначити зменшення кількості використовуваних картинок, що завжди добре, і збільшення логічності коду.
Давайте перейдемо до основного блоку. Раз вже ми можемо точно задавати місце розташування блоків, то має сенс зробити три блоки, які йдуть в документі один за одним:
<div id="main-left"> </div> <div id="main"> </div> <div id="main-right"> </div>
З назви блоків зрозуміло, який за що відповідає. Код всередині блоків практично не відрізнятиметься від того, що ми писали при верстці таблицями, за винятком шрифту. Давайте займемося стилями. Ширина лівого блоку 125 пікселів, висота 80 пікселів. Зсув ліворуч 3 пікселя, а зверху – 96 пікселів. Виходять такі стилі:
#main-left {
width: 125x; height: 80px;
position: absolute; left: 3px; top: 96px}
З центральним блоком дещо цікавіше. Його ширина 364 пікселі, а висота в принципі може змінюватися, якщо буде більше тексту. Тому висоту поки не будемо задавати і подивимося, що з цього вийде. Зсув ліворуч дорівнює ширині лівого блоку і ще 15 пікселів, тобто виходить 140 пікселів. А зсув зверху такий самий:
#main {
width: 364px;
position: absolute; left: 140px; top: 96px}
З правим блоком все практично так само. Ми знаємо розміри і обчислюємо зсуви. Єдина різниця в шрифті. Текст новин виводиться дрібним шрифтом, так що треба його задати:
#main-right {
font: 10px Verdana;
width: 233px;
position: absolute; left: 517px; top: 96px}
Як бачите, немає абсолютно нічого складного в абсолютному позиціонуванні блоків. Знай собі вираховуй зміщення і пиши зовсім однотипні стилі. Проміжний результат нашої титанічної праці показаний на мал.9.7:
Мал. 9.7 Проміжний вигляд блоків
Залишилося реалізувати тільки блоки з копірайтом. Ось тут у всій красі і проявить себе проблема з висотою CSS-блоків. Ми заздалегідь не знаємо висоту центрального або лівого блоку. Звичайно, ми її знаємо для нашого макета, але на сайті може бути скільки завгодно новин. Що якщо адміністратор сайту вирішить вивести не дві, а чотири новини? Або в першій новині помістить 146 слів замість 10? Виходить, що висота блоків абсолютно невизначена, так що її не можна задавати жорстко. Але це ще півбіди. Головне питання, наскільки нам необхідно змістити блок копірайту, щоб на сторінці все було нормально?
Тут вимальовується концептуальна перевага нормального потоку. У ньому елементи йдуть строго один за одним і зовсім не треба піклуватися про всякі зсуви. Коли ми верстали за допомогою таблиць, то просто помістили таблицю з копірайтом після всіх інших блоків. Браузер завжди буде її виводити останньою, збільшуючи висоту центральної таблиці наскільки необхідно.
Зараз ми не може використовувати переваги нормального потоку, тому що при абсолютному позиціонуванні блоки повністю з нього вириваються. Якщо ми вирішимо залишити останній блок у нормальному потоці, то він буде відображатися в самому верху, ніби більше на сторінці немає ніяких блоків. З іншого боку, ми не можемо поставити йому будь-який точний зсув, бо не знаємо висоти центральних блоків. Як вирішити проблему?
Єдиний коректний варіант рішення: динамічно змінювати зміщення в залежності від максимальної висоти центральних блоків. Для цього необхідно знати JavaScript і DOM. Чесно кажучи, я не люблю такі обхідні рішення, але в даному випадку більше ніякого реального варіанту немає. Давайте це підтвердимо, спробувавши деякі теоретично можливі способи.
Можна укласти центральні блоки і копірайт в один загальний блок. Здається, що в такому випадку вийде розташувати копірайт безпосередньо під центральним блоком. Впевненість пов’язана з властивістю bottom. Дана властивість задає зсув відносно нижнього краю контейнера, тобто в нашому випадку якщо задати усунення 0 щодо нижнього краю спільного блоку, все повинно вийти. Структура коду буде така:
<div id="general"> <!-- Три центральних блоки --> <div id="main-left"></div> <div id="main"></div> <div id="main-right"></div> <!-- Блок з копірайтом --> <div id="bottom-left"></div> </div>
Стилі для двох нових блоків будуть ось такими:
#general {
width: 760px;
top: 96px; position: absolute}
#bottom-left {
width: 233px; height: 40px;
position: absolute; left: 0px; bottom: 0px}
Здається, що висота спільного блоку повинна автоматично підлаштовуватися під висоту найвищого блоку з трьох вкладених. Але цього не відбувається з тієї причини, що блоки знаходиться поза нормального потоку. Виходить, що висоту спільного блоку треба задавати жорстко, так що ми повернулися до загальної проблеми. Якщо висоту задати жорстко, то можливі випадки, коли вміст блоків перевищує задану висоту і буде накладатися на блок bottom-left. На мал. 9.8 якраз показаний випадок, коли вміст перевищує задану висоту блоку.
Мал. 9.8 Накладення блоку main на блок bottom-right
Для наочності блок general виділено сірим фоном, і всі блоки взяті в пунктирні рамки.
Якщо спробувати винести блок з копірайтом з блоку general і в стилях теж вказати bottom: 0px (зміщення щодо нижнього краю вікна браузера буде нульовим), то все одно проблема з висотою не вирішиться, тому що вміст блоку може перевищувати висоту всього вікна браузера, і це не рідкість.
На основі проведеного досвіду можна зробити висновок, що за допомогою абсолютного позиціонування не можна зверстати такий макет. В окремому випадку, якщо змусити адміністратора сайту вставляти в блоки суворо обмежену кількість тексту або новин, можна задати зсув для блоку з копірайтом і все вийде. Але ніякий нормальний адміністратор на це не піде і буде правий. Окремі випадки гнучкістю не відрізняються, а для гарного сайту гнучкість обов’язкова.
Якщо все узагальнити, то виходить, що на основі абсолютного позиціонування можна легко верстати сайти, у яких немає такого нижнього блоку. Відразу скажу, що таке буває нечасто, але все ж буває.
Фактично, проблема полягає в прив’язці одного блоку до іншого. Тобто якщо б ми могли при абсолютному позиціонуванні сказати блоку bottom-right, що він завжди повинен розташовуватися під блоком general, проблему вдалося б зняти. Я не знаю, що завадило розробникам стандарту CSS реалізувати яку-небудь спеціальну властивість, яка дозволяла б це робити. Наприклад, можна було б зробити для властивості position функцію after, в яку передавати id блоку. Тоді проблема з позиціюванням блоку bottom-left вирішувалася елементарно:
#bottom-left {
width: 233px; height: 40px;
position: after(#general); left: 0px}
Ось вам ще одна серйозна недоробка групи створення стандарту CSS, яка практично виключає можливість використання абсолютного позиціонування для верстки серйозних сайтів.
Можливо, в каскадних таблицях стилів є інший вид позиціонування, який дозволяє вирішити проблему з нижнім блоком? Справді, вид позиціонування є, але наскільки він вирішує проблему, треба ще подивитися. У наступній частині буде розглянута плаваюча модель.
***
Це переклад статті.
Джерело: webmascon



