Распределяйте элементы равномерно, используя CSS - PullRequest
25 голосов
/ 23 марта 2009

Метод равномерного распределения элементов в контейнере с использованием CSS появился сегодня в журнале Smashing Magazine .

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

Есть этот вопрос , где gargantaun говорит:

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

Но я не могу сказать дизайнеру изменить его дизайн, и я не согласен с тем, что недостатки CSS должны ограничивать дизайнеров.

В любом случае, вот что у меня есть в HTML (переведено и упрощено):

<div id="menu">
    <ul>
        <li><a href="/">Home</a></li>
        <li><a href="/news">News</a></li>
        <li><a href="/theme">Theme</a></li>
        <li><a href="/activities">Activities</a></li>
        <li><a href="/contact">Contact</a></li>
    </ul>
</div>

в CSS (несущественные свойства удалены и упрощены):

#menu li { float: left; margin-right: 20px; }
#menu a  { display: block; padding: 0 1em; }

и в Javascript:

function justifyMenu() {
    var menuItems  = $$("#menu li");
    var menuWidth  = $("menu").getWidth();
    var totalWidth = 0;

    menuItems.each(function(e) {
        totalWidth += e.getWidth();
    });

    var margin = (menuWidth - 4 - totalWidth) / (menuItems.length - 1);
    margin = parseInt(margin);

    menuItems.each(function(e) {
        e.setStyle({ marginRight: margin + 'px' });
    });

    menuItems[menuItems.length - 1].setStyle({ marginRight: '0' });
}

А вот уменьшенный снимок экрана (посмотрите, как меню выравнивается по изображению заголовка):

screenshot

Есть идеи, как мне этого добиться без Javascript?

Ответы [ 13 ]

36 голосов
/ 03 августа 2010

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

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

Да, я знаю, что это не тот ответ, который вы искали, но, черт возьми, это так очевидно .

34 голосов
/ 12 марта 2012

Были случайные утверждения, что таблицы являются очевидным решением, однако, не было никакого реального обсуждения того, как его реализовать. Я покажу вам, что отображение div в виде таблицы - это правильный способ сделать это, но это не так просто, как центрирование всех ячеек и установка автоматической ширины. Проблема в том, что у вас нет контроля над внешними полями самого левого и правого содержимого ячейки. Они оба вставлены из его коробки с произвольным количеством, которое вы не можете контролировать. Вот работа вокруг:

Во-первых, небольшое изменение HTML-кода Гудера:

<div id="menu">
    <ul>
        <li class="left"><a href="/">Home</a></li>
        <li><a href="/news">News</a></li>
        <li><a href="/theme">Theme</a></li>
        <li><a href="/activities">Activities</a></li>
        <li><a href="/contact">Contact</a></li>
    </ul>
</div>

Теперь CSS:

#menu {display:table; width:// some width in px //}
#menu ul {display:table-row; width: 100%}
#menu li.left {display: table-cell; text-align: left; width: 20px; white-space:nowrap;}
#menu li {display: table-cell; text-align: right; width: auto;}

Теперь у нас есть полный контроль над самыми внешними сторонами меню, которые совпадают с крайними левыми и крайними правыми сторонами вмещающего блока, и расстояние между каждым элементом является постоянным. Вы заметите, что я использовал трюк, чтобы получить самую дальнюю левую ячейку для точной ширины ее содержимого. Я установил свойство width в маленький размер, явно ниже того, что обычно было бы его содержимым. Затем я установил пустое пространство без обтекания, которое растягивает ячейку наименьшее количество, чтобы поместиться в текст ячейки. Вы можете увидеть здесь изображение, которое показывает эффект этого (с использованием различных элементов HTML):

riffing on cats

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

8 голосов
/ 23 марта 2009

Это то, что нужно для отображения: таблица-ячейка должна быть достигнута - однако, IE просто не делают этого, и я считаю, что FF <3 тоже имеет проблемы с этим. </p>

Эти стили работают в FF3, Chrome, Safari и (я думаю) Opera 9:

#menu ul {display:table;padding:0;}
#menu li {display:table-cell;text-align:center;}

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

5 голосов
/ 27 сентября 2012

Несмотря на то, что ответ Колина Брогана обеспечивает прочную основу для «почти готового» решения проблемы, он все равно зависит от длины текста. Если текст слишком длинный, ячейка будет шире и, следовательно, будет больше места слева. Я попытался решить проблему, основываясь на коде, представленном в его ответе, но я пришел к выводу, что проблема не имеет реального возможного решения с помощью таблиц или поддельных таблиц (display: table-cell).

Так что нам придется подождать, пока гибкая блочная модель CSS3 не станет более широко поддерживаемой (вы можете проверить обновленную поддержку здесь ). Тем временем вы можете использовать полифилл Flexie для исправления браузеров, которые его не поддерживают. Если вы хотите проверить, как это будет выглядеть в WebKit сейчас (без необходимости заполнения), попробуйте следующий CSS:

#menu ul {
  display: -webkit-box;
  -webkit-box-orient: horizontal;
  -webkit-box-pack: justify;
  width: 940px;
  list-style: none;
  padding: 0;
  border: 1px solid gray;
}
#menu li {
  border: 1px solid silver;
}

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

Этот подход принимает неизвестное количество элементов и ширины текста без знания их фактической ширины и распределяет их равномерно по всему контейнеру (в данном случае #menu ul).

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

3 голосов
/ 23 марта 2009

Да, вы можете сделать это, если ширина элементов, подлежащих распределению, известна заранее. Но это немного грязно.

Хитрость заключается в том, что вам нужен интервал между каждым элементом '(Wp-sum (Wc)) / (Nc-1)', то есть ширина родительского элемента минус общая ширина всех дочерних элементов, разделенная на поровну между количеством промежутков между элементами.

Поскольку у CSS нет возможности делать выражения, мы должны немного его взломать. Сначала мы добавляем поле к родительскому элементу размером «сумма (Wc)», общая ширина всех дочерних элементов. Так что теперь родительский объект имеет ширину ‘(Wp-sum (Wc))’, и мы можем использовать значение заполнения в% относительно этой ширины.

Так, например, для четырех изображений размером 10px, 20px, 40px и 80px соответственно, наша сумма (Wc) составляет 150px. Установите это как родительское поле, тогда у дочерних элементов может быть треть этой ширины как отступ между ними.

<style type="text/css">
    #nava { width: 10px; height: 20px;}
    #navb { width: 20px; height: 20px;}
    #navc { width: 40px; height: 20px;}
    #navd { width: 80px; height: 20px;}

    #nav { margin-right: 150px; white-space: nowrap; }
    #nava, #navb, #navc { padding-right: 33.3%; }
</style>

<div id="nav"
    ><img id="nava" src="nava.png" alt="a"
    ><img id="navb" src="navb.png" alt="b"
    ><img id="navc" src="navc.png" alt="c"
    ><img id="navd" src="navd.png" alt="d"
></div>

Забавный отступ от тега - избежать пробелов между изображениями. «Nowrap» необходим, потому что если ширина родительского элемента меньше ширины страницы, иначе невозможно было бы разместить все элементы в строке. Наконец, в IE вам может понадобиться добавить div обёртки вокруг лота с шириной:: 100%; Переполнение: скрыто, чтобы предотвратить нежелательные полосы прокрутки, если вы занимаетесь всей страницей. И, конечно же, вам захочется быть в режиме стандартов.

Это также может работать с текстовыми элементами, если вы сделаете их встроенными блоками, чтобы вы могли добавить отступы, и вы явно указали их размер в ems. Это не будет работать, если размеры дочерних элементов заранее не известны (например, они содержат динамическое содержимое), так как вы не будете знать значение «sum (Wc)» для использования.

Честно говоря, я бы, наверное, просто использовал стол. Алгоритм компоновки таблицы очень легко справляется с вычислением, как распределить запасную ширину таблицы. (Используйте «table-layout: fixed» для получения наилучших результатов с ячейками известной ширины или «auto» для ответа на динамическое содержимое.) Таким образом, вам также не нужно беспокоиться об ошибках округления пикселей.

2 голосов
/ 23 марта 2009

Если бы вы использовали текстовые размеры (em, ex), это было бы намного проще. Вы можете использовать буквы, а не пиксели.

Пример: все это 30 заглавных букв шириной Ms. Затем вы можете использовать ширину каждого навигационного элемента (в зависимости от его текстового содержимого) и выполнять статическую математику оттуда.

1 голос
/ 16 ноября 2012

Лучший ответ не сработал для меня, и ответ GarciaWebDev пока не сработает, потому что мне нужно поддерживать несколько других браузеров, включая IE8.

Этот метод работал для меня. Идея состоит в том, чтобы сделать содержащий элемент text-align: justify и заставить элементы распространять display: inline-block.

HTML:

<div id="menu">
    <ul>
        <li><a href="/">Home</a></li>
        <li><a href="/news">News</a></li>
        <li><a href="/theme">Theme</a></li>
        <li><a href="/activities">Activities</a></li>
        <li><a href="/contact">Contact</a></li>
        <li class="filler"></li>
    </ul>
</div>

CSS:

#menu {
    text-align: justify;
}

ul {
    margin: 0;
    padding: 0;
}

#menu li {
    display: inline-block;
}

.filler {
    width: 100%;
    height: 0;
}
1 голос
/ 25 ноября 2011

Вот код в формате jQuery для тех, кто считает его полезным

function justifyClients() {
                        var menuItems  = $("#clients-wrapper ul li").get();                         
                        var menuWidth  = $("#clients-wrapper ul").width();                          
                        var totalWidth = 0;

                        $("#clients-wrapper ul li").each(function(i,e)
                                        {
                                            totalWidth += $(e).width();
                                        });

                        var margin = (menuWidth - 4 - totalWidth) / ($("#clients-wrapper ul li").length - 1);
                        margin = parseInt(margin);

                        $("#clients-wrapper ul li").each(function(i,e) {

                            if(i < $("#clients-wrapper ul li").length - 1)
                            {
                                alert(i + " " + $("#clients-wrapper ul li").length);
                                $(e).css('margin-right',  margin);
                            }
                        });
                    }

                    $(document).ready(function() {
                      justifyClients();
                    });
0 голосов
/ 15 августа 2015

Благодаря модулю CSS3 Flexbox * это возможно с двумя строками CSS.

Проверьте таблицу совместимости браузера для Flexbox

HTML

<div id="menu">
  <ul>
    <li><a href="/">Home</a>
    </li>
    <li><a href="/news">News</a>
    </li>
    <li><a href="/theme">Theme</a>
    </li>
    <li><a href="/activities">Activities</a>
    </li>
    <li><a href="/contact">Contact</a>
    </li>
  </ul>
</div>

CSS

ul {
  display: flex;
}
li {
  flex: 1; /* Short hand for flex-grow: 1 and flex-shrink: 1 */
}

Выход:

ul, li {
  margin: 0;
  padding: 0;
}
ul {
  display: flex;
  list-style: none;
}
li {
  flex: 1;
  text-align: center;
}
<div id="menu">
  <ul>
    <li><a href="/">Home</a>
    </li>
    <li><a href="/news">News</a>
    </li>
    <li><a href="/theme">Theme</a>
    </li>
    <li><a href="/activities">Activities</a>
    </li>
    <li><a href="/contact">Contact</a>
    </li>
  </ul>
</div>
0 голосов
/ 08 августа 2014

Улучшенный вариант Тревора Диксона (без лишних <li>)

HTML

<ul>
    <li><a href="/">Home</a></li>
    <li><a href="/news">News</a></li>
    <li><a href="/theme">Theme</a></li>
    <li><a href="/activities">Activities</a></li>
    <li><a href="/contact">Contact</a></li>
</ul>

CSS

ul {
    margin: 0;
    padding: 0;
}
ul li {
    display: inline-block;
    text-align: justify;
}
ul:after{
    display: inline-block;
    content: '';
    width: 100%;
    height: 0;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...