Як я * справді * обґрунтую горизонтальне меню в HTML + CSS?


86

Ви знайдете безліч підручників на рядках меню в HTML, але для цього конкретного (хоча загального) випадку я не знайшов жодного гідного рішення:

#  THE MENU ITEMS    SHOULD BE    JUSTIFIED     JUST AS    PLAIN TEXT     WOULD BE  #
#  ^                                                                             ^  #
  • Існує різна кількість пунктів меню, що містять лише текст, і макет сторінки непомітний.
  • Перший пункт меню повинен бути вирівняний за лівим краєм, останній пункт меню - за правим.
  • Решта елементів слід оптимально розподілити по рядку меню.
  • Кількість варіюється, тому немає шансів заздалегідь розрахувати оптимальну ширину.

Зверніть увагу, що ТАБЛИЦЯ також не буде працювати тут:

  • Якщо ви відцентруєте всі TD, перший та останній елементи неправильно вирівняні.
  • Якщо ви вирівнюєте ліворуч та праворуч перше відп. для останніх елементів інтервал буде неоптимальним.

Чи не дивно, що немає очевидного способу реалізувати це чистим способом, використовуючи HTML і CSS?

Відповіді:


43

Сучасний підхід - Flexboxes !

Тепер, коли флексбокси 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>


83

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


1
Здається, це працює, якщо ви використовуєте spanзамість hr! Це насправді не працює, HR займає видимий простір - використовуйте #menu { border: solid 1px green; }для підтвердження. Крім того, display: inline-block;не працює в IE (... 7? CompatibilityView?) Для елементів, які, природно, не є вбудованими елементами. HR - це елемент блоку, тому я припускаю, що вбудований блок не працює для HR в IE. У всякому разі, проліт.
ANeves вважає, що SE є злим

9
чудово працює, але результати кращі, якщо замінити кожен пробіл на & ensp; (n пробіл), так що меню та елемент & 1 залишатимуться близько. & nbsp; не працює в сафарі.
yitwail

15
Я просто витратив більше години, б'ючись головою об стіну, намагаючись зрозуміти, чому це не працює для мене. Відповідь полягає в тому, що мені потрібен пробіл між тегами. Я працюю в WordPress, і wp_page_menu не включає розриви рядків після кожного <li> </li>. Додав їх за допомогою preg_replace, і це працює зараз. Fwewww
Грег

1
Щоб це працювало в наші дні, вам потрібно використовувати display: inline-block на LI у браузерах, які його підтримують, і використовувати display: inline з пробілами, заміненими на & nbsp; у браузерах, які цього не підтримують. Крім того, оскільки ie7 не підтримує вбудований блок, вам доведеться використовувати inline на своєму тегу примусового перегортання і просто запихати в нього достатньо & nbsp; s, щоб примусити перегортання.
Йорен

1
Щоб пояснити коментар Йорена, UL все ще має бути вбудованим, а вбудований блок LI.
Мосс

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>

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


Дякую! Ваша думка до суті. Якби я ініціював запитання, я б позначив його як відповідь.
Андревінський

У мене були проблеми з тим, щоб це працювало в IE. Після кількох годин, проведених у Google land, я нарешті знайшов цю публікацію в блозі: kristinlbradley.wordpress.com/2011/09/15/... Що, нарешті, вдалося зробити, щоб додати text-justify: distribute-all-lines;до ulселектора. Здається, власні речі IE.
maryisdead

Не впевнений, чи це має значення, але я використав width: 100%замість margin-left: 100%: ul:after{content:""; margin-left:100%;}
Євген Сонг

8

Отримав рішення. Працює у 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 після останнього елемента залишається простір.
рейс

Переконайтеся, що між кожним прольотом є лише один пробіл, а між останнім внутрішнім та фінішним - два. Якщо це не працює, возитися з пробілами. У вебкіті є помилка.
mikelikespie

1
.outer {text-align: justify} .outer span.finish {display: inline-block; ширина: 100%} .outer span.inner {display: inline-block; white-space: nowrap} Не працює на IE6 та IE7.
Біньямін

Надаючи .outer line-height: 0силам висоту списку, щоб вона відображалася так, ніби вона складається з одного рядка.
контур


3

ще одне рішення. У мене не було можливості вирішити 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>


2

Зробити це за <p>допомогою text-align: justify?

Оновлення : Неважливо. Це взагалі не працює, як я думав.

Оновлення 2 : Зараз не працює в жодному браузері, крім IE, але CSS3 підтримує це у виглядіtext-align-last


Це було швидко! Я вважав, що моє власне рішення є унікальним, але ви придумали щось подібне, щоб почати його всього за кілька хвилин.
рейс

Зараз у 2016 році text-align-last працює в будь-яких браузерах, крім Safari . Це повинно бути можливим супротивником підходу flexbox чи хакерів .
хакаташі

1

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

@Andrew Vit: Ви маєте рацію. Це переважно те, що пропонує також рішення asbjornu.
рейс

@flight, якщо ти вважаєш, що моя відповідь є рішенням, чи не можеш ти позначити її як одну? Зараз схоже, що ваша проблема не вирішена. Якщо це не так, було б непогано, якщо б ви надали нам рішення, яке ви знайшли, або позначили будь-який із наданих відповідей як рішення вашої проблеми. :-)
Асбьорн Ульсберг

1

Текст виправданий лише в тому випадку, якщо речення природно спричиняє розрив рядка. Отже, все, що вам потрібно зробити, це, природно, примусити розрив рядка і приховати, що на другому рядку:

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. зайвий простір у кінці вашого рядка, і якщо він не містить вмісту, він ігнорується, і рядок не виправданий.

Перевірено в IE7, IE8, IE9, Chrome, Firefox 4.


0

якщо піти з 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>

0

Простіша розмітка, протестована в 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;
}

Живий приклад .


-1

Цього можна досягти за допомогою ретельних вимірювань та селектора останньої дитини.

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

4
Майте на увазі, текст не відображається скрізь однаково, піксель відключається, і ви побачите непрацездатне меню. Це буде працювати лише з елементами фіксованої ширини, як зображення.
fregante

-2

Я знаю, що в оригінальному запитанні вказано 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>
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.