Равномерно распределенная высота дочерних элементов с помощью CSS

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

Вот как это должно выглядеть с четырьмя цветами, высотой списка 90 пикселей и толстой рамкой вокруг:

Firefox

Изображение выше создано в Firefox 3.6.13 из следующего источника:

<ul style="height: 90px; border: 5px solid black; padding: 0;">
    <li style="height: 25%; background: red;">
    <li style="height: 25%; background: blue;">
    <li style="height: 25%; background: yellow;">
    <li style="height: 25%; background: green;">
</ul>

Все в порядке. Список действительно имеет высоту 90 пикселей — в границах — и каждый цвет получает (на первый взгляд) равную долю этого пространства. Теперь давайте отобразим тот же HTML/CSS в Safari или Chrome:

Chrome

Обратите внимание на узкий белый ряд между зеленым рядом и границей. Существует довольно простое объяснение тому, что мы здесь видим: 0.25 × 90 = 22.5

WebKit в Safari и Chrome не очень любит нецелочисленные высоты пикселей и отбрасывает десятичные дроби. С четырьмя строками высотой 22 мы получаем 2 пустых пикселя внизу списка: 90 - 4 × 22 = 2

В контексте статического HTML-файла мы можем легко установить высоту элементов на 23, 22, 23, 23 пикселя соответственно, и список будет нормально отображаться в любом браузере. Если, с другой стороны, цвета загружаются из базы данных, а их количество меняется при каждом запросе, требуется более гибкое решение.

Я знаю, как решить эту проблему, вычисляя и устанавливая высоту целочисленного значения в каждой строке onload с помощью Javascript, и я опубликую это решение, если ничего не появится. Однако я бы предпочел чисто CSS-решение проблемы. Можете ли вы придумать один?


person Jørn Schou-Rode    schedule 25.02.2011    source источник
comment
Вы можете схитрить и установить цвет фона ul на цвет последнего li.   -  person thirtydot    schedule 25.02.2011
comment
Джон Резиг о проблеме.   -  person thirtydot    schedule 25.02.2011
comment
@thirty Ну, для нескольких строк это будет выглядеть нормально. При большем количестве строк накопленный зазор может увеличиться до высоты, превышающей одну строку. В таком случае расширение последней строки выглядело бы странно.   -  person Jørn Schou-Rode    schedule 25.02.2011
comment
@thirty Хорошая ссылка! Однако, судя по беглому просмотру, в статье не обсуждаются решения. Может по комментариям - посмотрю внимательнее.   -  person Jørn Schou-Rode    schedule 25.02.2011
comment
Еще одна статья по этой проблеме - но решения нет :(   -  person whostolemyhat    schedule 25.02.2011
comment
Я прошерстил Google, и похоже, что ни у кого нет чистого CSS-решения для этого. Еще одно полезное чтение: elasticss.com/   -  person thirtydot    schedule 25.02.2011


Ответы (2)


Как и было обещано, вот решение проблемы на Javascript. Меня все еще очень интересует простое решение на основе CSS, но пока этот ответ может помочь другим выполнить свою работу.

Сценарий ожидает, что в точке входа будут объявлены две переменные: list должна указывать на DOM-элемент контейнера (например, <ul>), а items — на набор элементов (например, <li>) в этом списке.

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

var spaceRemaining = list.clientHeight;
var itemsRemaining = items.length;

while (itemsRemaining > 0) {
    var itemHeight = Math.round(spaceRemaining / itemsRemaining);
    items[itemsRemaining - 1].style.height = itemHeight;
    spaceRemaining -= itemHeight;
    itemsRemaining -= 1;
}

Для тех, кто предпочитает краткость читабельности, вот более короткая версия того же скрипта:

for (var space = list.clientHeight, i = items.length; i; i--) {
    space -= items[i-1].style.height = Math.round(space / i);
}
person Jørn Schou-Rode    schedule 25.02.2011
comment
+1, выглядит хорошо для меня. Может быть, сделать демонстрацию jsFiddle? - person thirtydot; 25.02.2011

Попробуйте удалить все концы между тегами <li>. Например

<ul><li></li><li></li></ul>

Иногда это было проблемой, но в вашей ситуации я не уверен, что это поможет ;) Просто попробуйте ;)

person Adam Lukaszczyk    schedule 25.02.2011
comment
Извините, но в данном случае это не проблема. Вы думаете о display: inline-block. (не мой минус) - person thirtydot; 25.02.2011