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

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

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

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

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

Разве не странно, что нет очевидного способа реализовать это в чистом виде с помощью HTML и CSS?

Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Введение в CSS
Введение в CSS
CSS является неотъемлемой частью трех основных составляющих front-end веб-разработки.
Как выровнять Div по центру?
Как выровнять Div по центру?
Чтобы выровнять элемент <div>по горизонтали и вертикали с помощью CSS, можно использовать комбинацию свойств и значений CSS. Вот несколько методов,...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
Toor - Ангулярный шаблон для бронирования путешествий
Toor - Ангулярный шаблон для бронирования путешествий
Toor - Travel Booking Angular Template один из лучших Travel & Tour booking template in the world. 30+ валидированных HTML5 страниц, которые помогут...
87
0
83 989
13
Перейти к ответу Данный вопрос помечен как решенный

Ответы 13

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

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

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

Это было быстро! Я думал, что мое собственное решение было уникальным, но вы придумали что-то похожее всего за несколько минут.

flight 08.09.2008 16:22

Сейчас в 2016 году text-align-lastработает в любых браузерах кроме Safari. Это должен быть возможный противник подход flexbox или взломанный диапазон.

hakatashi 13.05.2016 22:25

Для браузеров на базе 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's 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's 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>

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

Обратите внимание, что вы могли бы использовать здесь и список, если бы вы установили display: inline для элементов списка ... списки более обычны для меню.

Andrew Vit 17.02.2009 13:12

@ Андрей Вит: Вы правы. Это в основном то, что предлагает решение asbjornu.

flight 07.04.2010 15:03

@flight, если вы думаете, что мой ответ - это решение, не могли бы вы отметить его как единое целое? Сейчас похоже, что ваша проблема не решена. Если это не так, было бы неплохо, если бы вы предоставили нам решение, которое вы нашли, или отметили любой из предоставленных ответов как решение вашей проблемы. :-)

Asbjørn Ulsberg 27.09.2011 00:21

Я знаю, что в исходном вопросе указан 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>

Самый простой способ - заставить строку разрываться, вставив в конец строки элемент, который будет занимать больше, чем оставшееся доступное пространство, а затем скрыть его. Я довольно легко справился с этим с помощью простого элемента 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>

Весь мусор внутри селектора #menu span (насколько я обнаружил) необходим, чтобы угодить большинству браузеров. Он должен установить ширину элемента span на 100%, что должно вызвать разрыв строки, поскольку он считается встроенным элементом в соответствии с правилом display: inline-block. inline-block также позволяет span блокировать правила стиля уровня, такие как width, из-за чего элемент не помещается в соответствие с меню и, следовательно, меню прерывается.

Разумеется, вам необходимо настроить ширину span в соответствии с вашим вариантом использования и дизайном, но я надеюсь, что вы уловили общую идею и сможете ее адаптировать.

Кажется, работает, если вы используете span вместо hr! It's not really working, the HR is occupying visible space - use #menu { border: solid 1px green; } to confirm. Also, display: inline-block; does not work on IE (...7? CompatibilityView?) for elements that aren't naturally inline elements. HR is a block element, so I'm guessing inline-block doesn't work for HR on IE. Anyway, span.
ANeves thinks SE is evil 07.04.2010 15:30

Кроме того, сделайте его width: 100%; вместо width: 900px;. Отредактируйте это и переключите hr на промежуток времени, и вы получите голос, который, скорее всего, будет отмечен как ответ. ;)

ANeves thinks SE is evil 07.04.2010 15:36

Я не тестировал свои изменения, но думаю, что теперь мое решение должно отражать ваши мысли.

Asbjørn Ulsberg 13.04.2010 15:55

@sr pt правильный IE7 и ниже. Совместимость с IE7 тоже не должна работать.

corymathews 14.04.2010 23:03

отлично работает, но результаты будут лучше, если вы замените каждое пробел на & ensp; (n пробел), чтобы Menu & item & 1 оставались рядом. & nbsp; не работает в сафари.

yitwail 09.08.2011 23:39

@yitwail, о каких пространствах ты думаешь? Думаю, с вашей репутацией вы сможете отредактировать мой ответ и внести предлагаемые улучшения?

Asbjørn Ulsberg 30.08.2011 03:06

Обратите внимание, что вам не нужен дополнительный элемент <span>, вы также можете использовать псевдокласс :after и свойство content, чтобы получить тот же эффект. Вероятно, не будет работать в IE <9.

Husky 16.10.2011 20:27

Или вместо того, чтобы заменить пробелы на &nbsp;, вы можете установить text-align:left; на элементы.

Litek 18.05.2012 14:39

Я просто более часа бился головой о стену, пытаясь понять, почему это не сработало для меня. Ответ заключается в том, что мне нужно было пустое пространство между тегами. Я работаю в WordPress, и wp_page_menu не включает разрывы строк после каждого <li> </li>. Добавил их с помощью preg_replace, и теперь он работает. Фуууу

Greg Perham 30.05.2012 09:43

Чтобы заставить это работать в наши дни, вы должны использовать display: inline-block для LI в браузерах, которые его поддерживают, и использовать display: inline с пробелами, замененными на & nbsp; в браузерах, которые его не поддерживают. Кроме того, поскольку ie7 не поддерживает встроенный блок, вы должны использовать встроенный в свой тег принудительного переноса и просто засунуть в него достаточно & nbsp; s, чтобы принудительно перенести его.

Joren 09.08.2012 00:22

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

Asbjørn Ulsberg 09.08.2012 17:27

Чтобы прояснить комментарий Джорена, UL по-прежнему должен быть встроенным, а LI - встроенным блоком.

Moss 04.09.2013 06:55

Это прекрасно работает! Однако мне не удается добавить верхние / нижние отступы к своим li. Заполнение скрыто как переполнение. Есть ли способ добавить к элементам видимые отступы?

Manuela Hutter 17.06.2014 17:12

Выглядит отлично! Какова же цель стиля position: relative; в теге span?

elju 05.04.2015 03:58

Важное примечание: между элементами li должны быть пробелы (\t или \n или space). Это означает, что внутри шаблона .. ?><li>…</li><?php предотвратит срабатывание этой уловки. Добавьте пустой символ между </li> и <?php

Stphane 11.12.2015 19:14

Есть решение. Работает в 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>

На мой взгляд, это решение отлично работает с Gecko, но не с браузерами WebKit (протестировано с Chromium, Midori, Epiphany): в WebKit после последнего элемента есть конечный пробел.

flight 07.04.2010 14:55

Убедитесь, что между каждым промежутком есть только один символ пробела и два между последним внутренним и концом. Если это не сработает, поиграйте с пробелами. В webkit есть ошибка.

mikelikespie 08.04.2010 02:50

.outer {выравнивание текста: выравнивание} .outer span.finish {отображение: встроенный блок; ширина: 100%} .outer span.inner {дисплей: встроенный блок; white-space: nowrap} Не работает в IE6 и IE7.

Binyamin 05.07.2010 16:00

Назначение .outerline-height: 0 принудительно отображает высоту списка, как если бы он состоял из одной строки.

kontur 14.03.2014 14:40

если использовать javascript, то это возможно (этот сценарий основан на 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>

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

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.

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

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

Имейте в виду, что текст не везде отображается одинаково, иначе вы увидите неработающее меню. Это будет работать только с элементами фиксированной ширины, такими как изображения.

fregante 17.10.2011 05:12

Хорошо, это решение не работает в 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>

Причина, по которой у меня есть тег a как inline-block, заключается в том, что я не хочу, чтобы слова внутри также были оправданы, и я также не хочу использовать неразрывные пробелы.

Спасибо! Ваше решение по существу. Если бы я задавал вопрос, я бы отметил его как ответ.

Andrevinsky 26.11.2014 14:05

У меня были проблемы с тем, чтобы это работало в IE. После нескольких часов, проведенных в стране Google, я наконец нашел это сообщение в блоге: kristinlbradley.wordpress.com/2011/09/15/… Что в итоге помогло, так это добавление text-justify: distribute-all-lines; в селектор ul. Кажется, это проприетарный материал для IE.

maryisdead 12.12.2014 18:14

Не уверен, имеет ли это значение, но я использовал width: 100% вместо margin-left: 100%: ul:after{content:""; margin-left:100%;}

Eugene Song 04.03.2015 04:53

Более простая разметка, протестированная в 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;
}

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

Работает с 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;
}

К сожалению, это не работает в IE 7 из-за отсутствия поддержка ячеек таблицы

Philipp Michael 22.01.2013 20:32

еще одно решение. У меня не было возможности заняться 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's 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>
Ответ принят как подходящий

Современный подход - Флексбоксы!

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

В этом случае вы просто установите 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>

Использование justify-content: space-around - (пример здесь):

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

Другие вопросы по теме