Pull to refresh

Tableless justification или inline-blocks revisited

Reading time 6 min
Views 6.7K
Все давно знают про кроссбраузерную реализацию инлайн-блоков, но не все знают, что данная реализация не такая уж и кроссбраузерная и полная, как кажется. Что, как и почему рассмотрим на простом примере: сделаем меню, пункты которого равномерно распределены по всей ширине экрана.

Обычно в разметке меню выглядит так:
<ul class="menu">
    <li class="menu-item">
        <a href="/news">Новости</a>
    </li>
    <li class="menu-item">
        <a href="/swen">Старости</a>
    </li>
    <li class="menu-item">
        <a href="/profit">Всякая информация</a>
    </li>
</ul>

А css для этого примерно таков:
.menu-item {
    display: inline-block;
}

* html .menu-item {
    display: inline;
    zoom: 1;
}

*+html .menu-item {
    display: inline;
    zoom: 1;
}

В итоге получается, что каждый пункт меню — это слово в строке (независимо от того, сколько слов внутри пункта, т.к. инлайн-блок — неделимая единица со своим контекстом форматирования) и чтобы слова растягивались на всю ширину строки нужно сделать следующее:
.menu {
    text-align: justify;
}

.menu-item {
    display: inline-block;
    text-align: left;
}

* html .menu-item {
    display: inline;
    zoom: 1;
}

*+html .menu-item {
    display: inline;
    zoom: 1;
}

Но желаемого результата это не даст, т.к. в css нет значения full-justify для свойства text-align, а обычный justify растягивает расстояния между слов в строках, кроме последней, так что нам нужно вписать какое-нибудь достаточно длинной слово, которое бы переносилось на новую строку (в css3 есть text-align-last для манипуляции выравниванием последней строки, но это прокатит лишь в светлом будущем). Писать слово, конечно, не очень хороший вариант, достаточно просто сделать ещё один пункт меню и растянуть его на 100% ширины родителя (можно и меньше 100, тогда можно добиться довольно интересного эффекта):

<ul class="menu">
    <li class="menu-item">
        <a href="/news">Новости</a>
    </li>
    <li class="menu-item">
        <a href="/swen">Старости</a>
    </li>
    <li class="menu-item">
        <a href="/profit">Всякая информация</a>
    </li>
    <li class="menu-item menu-item_sizer">
        &nbsp;
    </li>
</ul>

.menu {
    text-align: justify;
}

.menu-item {
    display: inline-block;
    text-align: left;
}

* html .menu-item {
    display: inline;
    zoom: 1;
}

*+html .menu-item {
    display: inline;
    zoom: 1;
}

.menu-item_sizer {
    width: 100%;
}

Данный код будет одинаково хорошо работать во всех браузерах… кроме Internet Explorer 6 и 7 (кто бы сомневался). А проблема кроется в том, что такие «инлайн-блоки» в данных версиях эксплорера «слишком блочные» — они не учитывают пробельные символы вокруг себя. Наглядный пример:
<span class="inlineBlock">Test</span> word

В нормальных браузерах это будет выглядеть так:
Test word

В старых версиях эксплорера так:
Testword

К счастью есть решение этой проблемы и лежит оно на поверхности — надо сделать этот «инлайн-блок» более инлайновым, для это его нужно обернуть в инлайн элемент. Что получается:
<ul class="menu">
    <li class="menu-item_wrap">
        <div class="menu-item">
            <a href="/news">Новости</a>
        </div>
    </li>
    <li class="menu-item_wrap">
        <div class="menu-item">
            <a href="/swen">Старости</a>
        </div>
    </li>
    <li class="menu-item_wrap">
        <div class="menu-item">
            <a href="/profit">Всякая информация</a>
        </div>
    </li>
    <li class="menu-item_wrap menu-item_sizer">
        &nbsp;
    </li>
</ul>

.menu {
    text-align: justify;
}

.menu-item_wrap {
    display: inline;
}

.menu-item {
    display: inline-block;
    text-align: left;
}

* html .menu-item {
    display: inline;
    zoom: 1;
}

*+html .menu-item {
    display: inline;
    zoom: 1;
}

.menu-item_sizer {
    width: 100%;
}

Но и это не даст нужного результата, хотя всё вроде бы как надо. Проблема заключается в том, что эксплорер не считает каждый пункт единым целым и поэтому всё распадается — дело в пробельных символах закрывающей последовательности тегов. Фиксится просто:
<ul class="menu">
    <li class="menu-item_wrap">
        <div class="menu-item">
            <a href="/news">Новости</a></div></li>
    <li class="menu-item_wrap">
        <div class="menu-item">
            <a href="/swen">Старости</a></div></li>
    <li class="menu-item_wrap">
        <div class="menu-item">
            <a href="/profit">Всякая информация</a></div></li>
    <li class="menu-item_wrap menu-item_sizer">&nbsp;</li>
</ul>

Если вы пользуетесь шаблонизатором, то пусть эту работу сделает он (например тег spaceless в django templates или jinja).
Теперь всё выглядит хорошо и так как нужно во всех браузерах… пока мы не хотим сделать что-нибудь интересное внутри каждого пункта, пусть это будет иконка прижатая к правому краю каждого пункта:
<ul class="menu">
    <li class="menu-item_wrap">
        <div class="menu-item">
            <span class="menu-item-icon"> </span>
            <a href="/news">Новости</a></div></li>
    <li class="menu-item_wrap">
        <div class="menu-item">
            <span class="menu-item-icon"> </span>
            <a href="/swen">Старости</a></div></li>
    <li class="menu-item_wrap">
        <div class="menu-item">
            <span class="menu-item-icon"> </span>
            <a href="/profit">Всякая информация</a></div></li>
    <li class="menu-item_wrap menu-item_sizer">&nbsp;</li>
</ul>

.menu {
    text-align: justify;
}

.menu-item_wrap {
    display: inline;
}

.menu-item {
    background-color: Green;
    display: inline-block;
    padding: 5px 15px;
    position: relative;
    text-align: left;
}

* html .menu-item {
    display: inline;
    zoom: 1;
}

*+html .menu-item {
    display: inline;
    zoom: 1;
}

.menu-item_sizer {
    width: 100%;
}

.menu-item-icon {
    background-color: Red;
    height: 5px;
    position: absolute;
    right: 0;
}

Во всех браузерах включая старые эксплореры всё ок… кроме всех версий Оперы (кто бы сомневался). В Опере можно увидеть, что иконка не прижата к правому краю пункта, а находится левее — это банальный баг, который будет мешать жить разработчикам ещё лет 10, не меньше. Заключается он в том, что если в inline-block элементе находится не блочный элемент или не текст, происходит нарушение границ позиционирования. Фиксится это добавлением ещё одной, внутренней, блочной обёртки.
<ul class="menu">
    <li class="menu-item_outerWrap">
        <div class="menu-item">
            <div class="menu-item_innerWrap">
                <span class="menu-item-icon">&nbsp;</span>
                <a href="/news">Новости</a></div></div></li>
    <li class="menu-item_outerWrap">
        <div class="menu-item">
            <div class="menu-item_innerWrap">
                <span class="menu-item-icon">&nbsp;</span>
                <a href="/swen">Старости</a></div></div></li>
    <li class="menu-item_outerWrap">
        <div class="menu-item">
            <div class="menu-item_innerWrap">
                <span class="menu-item-icon">&nbsp;</span>
                <a href="/profit">Всякая информация</a></div></div></li>
    <li class="menu-item_outerWrap menu-item_sizer">&nbsp;</li>
</ul>

.menu {
    text-align: justify;
}

.menu-item_outerWrap {
    display: inline;
}

.menu-item {
    background-color: Green;
    display: inline-block;
    padding: 5px 15px;
    position: relative;
    text-align: left;
}

* html .menu-item {
    display: inline;
    zoom: 1;
}

*+html .menu-item {
    display: inline;
    zoom: 1;
}

.menu-item_innerWrap {
    display: block;
}

.menu-item_sizer {
    width: 100%;
}

.menu-item-icon {
    background-color: Red;
    height: 5px;
    position: absolute;
    right: 0;
}

Теперь главное не включить какому-нибудь блочному элементу внутри .menu-item hasLayout в IE6, т.к. это спровоцирует «распирание» нашего инлайн-блока на всю ширину. Решить это можно задав фиксированную ширину блочному элементу (если нужна ширина по содержимому нужно задать нулевую ширину, но только для IE6).

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

Ну и для закрепляющего эффекта, «формула» настоящего инлайн-блока в zen-css-подобном псевдокоде:
inline>inline-block>block


Может кто-то всё-таки донесёт до администрации, что нам тут нужна подсветка CSS? А то моя просьба как-то была проигнорирована.
Tags:
Hubs:
+49
Comments 66
Comments Comments 66

Articles