Как я * действительно * оправдываю горизонтальное меню в HTML + CSS?

Вы найдете множество руководств по меню в HTML, но для этого конкретного (хотя и общего характера IMHO) я не нашел подходящего решения:

#  THE MENU ITEMS    SHOULD BE    JUSTIFIED     JUST AS    PLAIN TEXT     WOULD BE  #
#  ^                                                                             ^  #
  • Здесь различное количество текстовых элементов меню и макет страницы являются текучими.
  • Первый элемент меню должен быть выровнен по левому краю, последний элемент меню должен быть выровнен по правому краю.
  • Остальные элементы должны быть оптимально распределены в строке меню.
  • Число меняется, поэтому нет возможности предварительно рассчитать оптимальные ширины.

Обратите внимание, что ТАБЛИЦА не будет работать здесь:

  • Если вы центрируете все TD, то первый и последний элементы arent выравниваются правильно.
  • Если вы выровняете выравнивание по левому краю и выравниваете по правому краю вправо или влево. последние элементы, интервал будет субоптимальным.

Не странно ли, что нет очевидного способа реализовать это чистым способом, используя HTML и CSS?

Ответы

Ответ 1

Современный подход - Flexboxes!

Теперь, когда CSS3 flexboxes имеют лучшую поддержку браузера, некоторые из нас могут наконец начать использовать их. Просто добавьте дополнительные префиксы для большего количества браузеров.

В этом случае вы просто установите родительский элемент display на flex, а затем измените свойство justify-content на space-between или space-around, чтобы добавить пробел между элементами flexbox.

Используя justify-content: space-between - ( пример здесь):

ul {
    list-style: none;
    padding: 0;
    margin: 0;
}
.menu {
    display: flex;
    justify-content: space-between;
}
<ul class="menu">
    <li>Item One</li>
    <li>Item Two</li>
    <li>Item Three Longer</li>
    <li>Item Four</li>
</ul>

Ответ 2

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

#menu {
  text-align: justify;
}

#menu * {
  display: inline;
}

#menu li {
  display: inline-block;
}

#menu span {
  display: inline-block;
  position: relative;
  width: 100%;
  height: 0;
}
<div id="menu">
  <ul>
    <li><a href="#">Menu item 1</a></li>
    <li><a href="#">Menu item 3</a></li>
    <li><a href="#">Menu item 2</a></li>
  </ul>
  <span></span>
</div>

Ответ 3

Хорошо, это решение не работает на IE6/7 из-за отсутствия поддержки :before/:after, но:

ul {
  text-align: justify;
  list-style: none;
  list-style-image: none;
  margin: 0;
  padding: 0;
}
ul:after {
  content: "";
  margin-left: 100%;
}
li {
  display: inline;
}
a {
  display: inline-block;
}
<div id="menu">
  <ul>
    <li><a href="#">Menu item 1</a></li>
    <li><a href="#">Menu item 2</a></li>
    <li><a href="#">Menu item 3</a></li>
    <li><a href="#">Menu item 4</a></li>
    <li><a href="#">Menu item 5</a></li>
  </ul>
</div>

Ответ 4

Получено решение. Работает в FF, IE6, IE7, Webkit и т.д.

Перед закрытием span.inner убедитесь, что вы не помещаете пробелы. IE6 сломается.

Вы можете указать .outer ширину

.outer {
  text-align: justify;
}
.outer span.finish {
  display: inline-block;
  width: 100%;
}
.outer span.inner {
  display: inline-block;
  white-space: nowrap;
}
<div class="outer">
  <span class="inner">THE MENU ITEMS</span>
  <span class="inner">SHOULD BE</span>
  <span class="inner">JUSTIFIED</span>
  <span class="inner">JUST AS</span>
  <span class="inner">PLAIN TEXT</span>
  <span class="inner">WOULD BE</span>
  <span class="finish"></span>
</div>

Ответ 5

Работает с Opera, Firefox, Chrome и IE

ul {
   display: table;
   margin: 1em auto 0;
   padding: 0;
   text-align: center;
   width: 90%;
}

li {
   display: table-cell;
   border: 1px solid black;
   padding: 0 5px;
}

Ответ 6

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

Работает в Chrome, Firefox, Safari. Не знаю об IE.

Тест: http://jsfiddle.net/c2crP/1

ul {
  margin: 0; 
  padding: 0; 
  list-style: none; 
  width: 200px; 
  text-align: justify; 
  list-style-type: none;
}
ul > li {
  display: inline; 
  text-align: justify; 
}

/* declaration below will add a whitespace after every li. This is for one line codes where no whitespace (of breaks) are present and the browser wouldn't know where to make a break. */
ul > li:after {
  content: ' '; 
  display: inline;
}

/* notice the 'inline-block'! Otherwise won't work for webkit which puts after pseudo el inside of it parent instead of after thus shifting also the parent on next line! */
ul > li:last-child:after {
  display: inline-block;
  margin-left: 100%; 
  content: ' ';
}
<ul>
  <li><a href="#">home</a></li>
  <li><a href="#">exposities</a></li>
  <li><a href="#">werk</a></li>
  <li><a href="#">statement</a></li>
  <li><a href="#">contact</a></li>
</ul>

Ответ 7

Сделайте <p> с text-align: justify?

Обновить: Nevermind. Это не работает вообще, как я думал.

Обновление 2. Не работает ни в каких браузерах, кроме IE, но CSS3 поддерживает это в виде text-align-last

Ответ 8

Для браузеров, основанных на Gecko, я придумал это решение. Это решение не работает с браузерами WebKit (например, Chromium, Midori, Epiphany), они по-прежнему показывают конечное пространство после последнего элемента.

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

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

Код:

<div style="width:500px; background:#eee;">
 <p style="text-align:justify">
  <a href="#">THE&nbsp;MENU&nbsp;ITEMS</a>
  <a href="#">SHOULD&nbsp;BE</a>
  <a href="#">JUSTIFIED</a>
  <a href="#">JUST&nbsp;AS</a>
  <a href="#">PLAIN&nbsp;TEXT</a>
  <a href="#">WOULD&nbsp;BE</a>
  <img src="/Content/Img/stackoverflow-logo-250.png" width="400" height="0"/>
 </p>
 <p>There an varying number of text-only menu items and the page layout is fluid.</p>
 <p>The first menu item should be left-aligned, the last menu item should be right-aligned. The remaining items should be spread optimal on the menu bar.</p>
 <p>The number is varying,so there no chance to pre-calculate the optimal widths.</p>
 <p>Note that a TABLE won't work here as well:</p>
 <ul>
  <li>If you center all TDs, the first and the last item aren't aligned correctly.</li>
  <li>If you left-align and right-align the first resp. the last items, the spacing will be sub-optimal.</li>
 </ul>
</div>

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

Ответ 9

Текст является оправданным, если предложение естественно вызывает разрыв строки. Итак, все, что вам нужно сделать, - это, естественно, заставить разрыв строки и скрыть что на второй строке:

CSS

ul {
  text-align: justify;
  width: 400px;
  margin: 0;
  padding: 0;
  height: 1.2em;
  /* forces the height of the ul to one line */
  overflow: hidden;
  /* enforces the single line height */
  list-style-type: none;
  background-color: yellow;
}

ul li {
  display: inline;
}

ul li.break {
  margin-left: 100%;
  /* use e.g. 1000px if your ul has no width */
}

HTML:

<ul>
  <li><a href="/">The</a></li>
  <li><a href="/">quick</a></li>
  <li><a href="/">brown</a></li>
  <li><a href="/">fox</a></li>
  <li class="break">&nbsp;</li>
</ul>

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

Протестировано в IE7, IE8, IE9, Chrome, Firefox 4.

Ответ 10

если пойти с javascript, который возможен (этот script является базой на mootools)

<script type="text/javascript">//<![CDATA[
    window.addEvent('load', function(){
        var mncontainer = $('main-menu');
        var mncw = mncontainer.getSize().size.x;
        var mnul = mncontainer.getFirst();//UL
        var mnuw = mnul.getSize().size.x;
        var wdif = mncw - mnuw;
        var list = mnul.getChildren(); //get all list items
        //get the remained width (which can be positive or negative)
        //and devided by number of list item and also take out the precision
        var liwd = Math.floor(wdif/list.length);
        var selw, mwd=mncw, tliw=0;
        list.each(function(el){
            var elw = el.getSize().size.x;
            if(elw < mwd){ mwd = elw; selw = el;}
            el.setStyle('width', elw+liwd);
            tliw += el.getSize().size.x;
        });
        var rwidth = mncw-tliw;//get the remain width and set it to item which has smallest width
        if(rwidth>0){
            elw = selw.getSize().size.x;
            selw.setStyle('width', elw+rwidth);
        }
    });
    //]]>
</script>

и css

<style type="text/css">
    #main-menu{
        padding-top:41px;
        width:100%;
        overflow:hidden;
        position:relative;
    }
    ul.menu_tab{
        padding-top:1px;
        height:38px;
        clear:left;
        float:left;
        list-style:none;
        margin:0;
        padding:0;
        position:relative;
        left:50%;
        text-align:center;
    }
    ul.menu_tab li{
        display:block;
        float:left;
        list-style:none;
        margin:0;
        padding:0;
        position:relative;
        right:50%;
    }
    ul.menu_tab li.item7{
        margin-right:0;
    }
    ul.menu_tab li a, ul.menu_tab li a:visited{
        display:block;
        color:#006A71;
        font-weight:700;
        text-decoration:none;
        padding:0 0 0 10px;
    }
    ul.menu_tab li a span{
        display:block;
        padding:12px 10px 8px 0;
    }
    ul.menu_tab li.active a, ul.menu_tab li a:hover{
        background:url("../images/bg-menutab.gif") repeat-x left top;
        color:#999999;
    }
    ul.menu_tab li.active a span,ul.menu_tab li.active a.visited span, ul.menu_tab li a:hover span{
        background:url("../images/bg-menutab.gif") repeat-x right top;
        color:#999999;
    }
</style>

и последний html

<div id="main-menu">
    <ul class="menu_tab">
        <li class="item1"><a href="#"><span>Home</span></a></li>
        <li class="item2"><a href="#"><span>The Project</span></a></li>
        <li class="item3"><a href="#"><span>About Grants</span></a></li>
        <li class="item4"><a href="#"><span>Partners</span></a></li>
        <li class="item5"><a href="#"><span>Resources</span></a></li>
        <li class="item6"><a href="#"><span>News</span></a></li>
        <li class="item7"><a href="#"><span>Contact</span></a></li>
    </ul>
</div>

Ответ 11

Упрощенная разметка, протестированная в Opera, FF, Chrome, IE7, IE8:

<div class="nav">
  <a href="#" class="nav_item">nav item1</a>
  <a href="#" class="nav_item">nav item2</a>
  <a href="#" class="nav_item">nav item3</a>
  <a href="#" class="nav_item">nav item4</a>
  <a href="#" class="nav_item">nav item5</a>
  <a href="#" class="nav_item">nav item6</a>
  <span class="empty"></span>
</div>

и css:

.nav {
  width: 500px;
  height: 1em;
  line-height: 1em;
  text-align: justify;
  overflow: hidden;
  border: 1px dotted gray;
}
.nav_item {
  display: inline-block;
}
.empty {
  display: inline-block;
  width: 100%;
  height: 0;
}

Живой пример.

Ответ 12

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

ul li {
margin-right:20px;
}
ul li:last-child {
margin-right:0;
}

Ответ 13

Я знаю исходный вопрос, заданный HTML + CSS, но он конкретно не сказал javascript;)

Попытка сохранить CSS-код и разметку настолько чистыми, насколько это возможно, и насколько это возможно семантически значимо (используя UL для меню), я придумал это предложение. Вероятно, это не идеальный вариант, но это может быть хорошей отправной точкой:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">

<html>

    <head>
        <title>Kind-of-justified horizontal menu</title>

        <style type="text/css">
        ul {
            list-style: none;
            margin: 0;
            padding: 0;
            width: 100%;
        }

        ul li {
            display: block;
            float: left;
            text-align: center;
        }
        </style>

        <script type="text/javascript">
            setMenu = function() {
                var items = document.getElementById("nav").getElementsByTagName("li");
                var newwidth = 100 / items.length;

                for(var i = 0; i < items.length; i++) {
                    items[i].style.width = newwidth + "%";
                }
            }
        </script>

    </head>

    <body>

        <ul id="nav">
            <li><a href="#">first item</a></li>
            <li><a href="#">item</a></li>
            <li><a href="#">item</a></li>
            <li><a href="#">item</a></li>
        <li><a href="#">item</a></li>
            <li><a href="#">last item</a></li>
        </ul>

        <script type="text/javascript">
            setMenu();
        </script>

    </body>

</html>