Wie kann ich ein horizontales Menü in HTML + CSS * wirklich * rechtfertigen?

86

Sie finden viele Tutorials zu Menüleisten in HTML, aber für diesen speziellen (obwohl IMHO generischen) Fall habe ich keine vernünftige Lösung gefunden:

#  THE MENU ITEMS    SHOULD BE    JUSTIFIED     JUST AS    PLAIN TEXT     WOULD BE  #
#  ^                                                                             ^  #
  • Es gibt eine unterschiedliche Anzahl von Nur-Text-Menüelementen und das Seitenlayout ist flüssig.
  • Der erste Menüpunkt sollte linksbündig sein, der letzte Menüpunkt sollte rechtsbündig sein.
  • Die restlichen Elemente sollten in der Menüleiste optimal verteilt sein.
  • Die Anzahl variiert, daher besteht keine Möglichkeit, die optimalen Breiten vorab zu berechnen.

Beachten Sie, dass eine TABELLE auch hier nicht funktioniert:

  • Wenn Sie alle TDs zentrieren, sind das erste und das letzte Element nicht richtig ausgerichtet.
  • Wenn Sie die erste bzw. rechte Ausrichtung nach links und rechts ausrichten. Bei den letzten Elementen ist der Abstand nicht optimal.

Ist es nicht seltsam, dass es keinen offensichtlichen Weg gibt, dies mithilfe von HTML und CSS sauber zu implementieren?

Flug
quelle

Antworten:

43

Moderner Ansatz - Flexboxen !

Nun , da CSS3 flexboxes haben bessere Browser - Unterstützung , können einige von uns schließlich mit ihnen beginnen. Fügen Sie einfach zusätzliche Herstellerpräfixe hinzu, um eine bessere Browserabdeckung zu erzielen .

In diesem Fall setzen Sie einfach das übergeordnete Element displayauf flexund ändern dann die justify-contentEigenschaft in entweder space-betweenoderspace-around , um Platz zwischen oder um die untergeordneten Flexbox-Elemente hinzuzufügen.

Verwenden vonjustify-content: space-between - ( Beispiel hier) :

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>


Verwenden vonjustify-content: space-around - (Beispiel hier) :

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>

Josh Crozier
quelle
83

Am einfachsten ist es, die Linie zum Unterbrechen zu zwingen, indem Sie am Ende der Linie ein Element einfügen, das mehr als den verbleibenden verfügbaren Platz belegt, und es dann ausblenden. Ich habe dies ganz einfach mit einem einfachen spanElement wie dem folgenden erreicht:

#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>

Der gesamte Müll in der #menu spanAuswahl ist (soweit ich festgestellt habe) erforderlich, um den meisten Browsern zu gefallen. Die Breite des spanElements sollte auf 100% erzwungen werden, was zu einem Zeilenumbruch führen sollte, da es aufgrund der display: inline-blockRegel als Inline-Element betrachtet wird . inline-blockAußerdem spankönnen Stilregeln auf Blockebene verwendet werden, bei widthdenen das Element nicht in das Menü passt und das Menü daher einen Zeilenumbruch aufweist.

Sie müssen die Breite natürlich spanan Ihren Anwendungsfall und Ihr Design anpassen, aber ich hoffe, Sie haben eine allgemeine Vorstellung und können sie anpassen.

Asbjørn Ulsberg
quelle
1
Es scheint zu funktionieren, wenn Sie ein spananstelle eines hr! Es funktioniert nicht wirklich, die Personalabteilung belegt sichtbaren Raum - #menu { border: solid 1px green; }zur Bestätigung verwenden. Funktioniert auch display: inline-block;nicht im IE (... 7? CompatibilityView?) Für Elemente, die von Natur aus keine Inline-Elemente sind. HR ist ein Blockelement, daher schätze ich, dass Inline-Block für HR im IE nicht funktioniert. Wie auch immer, span.
Aneves
9
funktioniert gut, aber die Ergebnisse sind schöner, wenn Sie jedes Leerzeichen durch & ensp; (n Leerzeichen), damit Menü & Element & 1 nahe beieinander bleiben. & nbsp; funktioniert nicht in Safari.
Yitwail
15
Ich habe gerade über eine Stunde damit verbracht, meinen Kopf gegen die Wand zu schlagen, um herauszufinden, warum das bei mir nicht funktioniert hat. Die Antwort ist, dass ich Leerraum zwischen den Tags brauchte. Ich arbeite in WordPress und wp_page_menu enthält keine Zeilenumbrüche nach jedem <li> </ li>. Fügte sie mit preg_replace hinzu und es funktioniert jetzt. Fwewww
Greg Perham
1
Damit dies heutzutage funktioniert, müssen Sie display: inline-block auf den LIs in Browsern verwenden, die dies unterstützen, und display: inline mit Leerzeichen verwenden, die durch & nbsp; in Browsern, die dies nicht unterstützen. Da ie7 Inline-Block nicht unterstützt, müssen Sie Inline für Ihr Force-Wrap-Tag verwenden und einfach genug s darin schieben, um den Wrap zu erzwingen.
Joren
1
Um Jorens Kommentar zu verdeutlichen, muss der UL noch inline und der Inline-Block des LI sein.
Moss
12

Ok, diese Lösung funktioniert unter IE6 / 7 nicht, da :before/ nicht unterstützt wird :after, aber:

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>

Der Grund, warum ich das a-Tag als habe, inline-blockist, dass ich nicht möchte, dass die darin enthaltenen Wörter auch gerechtfertigt sind, und dass ich auch keine nicht unterbrechenden Leerzeichen verwenden möchte.

remitbri
quelle
Vielen Dank! Ihre Lösung ist auf den Punkt. Wenn ich die Frage einleiten würde, würde ich sie als Antwort markieren.
Andrevinsky
Ich hatte Probleme, dies im IE zum Laufen zu bringen. Nach einigen Stunden in Google Land fand ich endlich diesen Blog-Beitrag: kristinlbradley.wordpress.com/2011/09/15/… Was schließlich der Trick war, war das Hinzufügen text-justify: distribute-all-lines;zum ulSelektor. Scheint proprietäres IE-Zeug zu sein.
Maryisdead
Ich bin mir nicht sicher, ob das wichtig ist, aber ich habe width: 100%stattdessen verwendet margin-left: 100%: ul:after{content:""; margin-left:100%;}
Eugene Song
8

Habe eine Lösung. Funktioniert in FF, IE6, IE7, Webkit usw.

Stellen Sie sicher, dass Sie keine Leerzeichen einfügen, bevor Sie das schließen span.inner. IE6 wird kaputt gehen.

Sie können optional .outereine Breite angeben

.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>

mikelikespie
quelle
Für mich funktioniert diese Lösung gut mit Gecko, aber nicht mit WebKit-Browsern (getestet mit Chromium, Midori, Epiphany): Bei WebKit steht nach dem letzten Element ein Leerzeichen.
Flug
Stellen Sie sicher, dass zwischen jeder Spanne nur ein Leerzeichen und zwischen dem letzten inneren und dem Ziel zwei Leerzeichen stehen. Wenn das nicht funktioniert, spielen Sie mit dem Leerzeichen. Es gibt einen Fehler im Webkit.
Mikelikespie
1
.outer {Textausrichtung: Ausrichten} .outer span.finish {Anzeige: Inline-Block; width: 100%} .outer span.inner {display: inline-block; Leerzeichen: nowrap} Funktioniert nicht unter IE6 und IE7.
Binyamin
Geben Sie den .outer line-height: 0Kräften die Höhe der Liste, die so gerendert werden soll, als ob sie aus einer Zeile bestehen würde.
Kontur
4

Funktioniert mit Opera, Firefox, Chrome und 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;
}
Rowinski
quelle
1
Leider funktioniert dies in IE 7 aufgrund der fehlenden Unterstützung für Tabellenzellen nicht
Philipp Michael
3

noch eine andere Lösung. Ich hatte keine Möglichkeit, das HTML wie das Hinzufügen einer angesehenen Klasse usw. anzugehen, also fand ich einen reinen CSS-Weg.

Funktioniert in Chrome, Firefox, Safari. Ich weiß nichts über IE.

Test: 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>

bash2day
quelle
2

Machen Sie es <p>mit text-align: justify?

Update : Nevermind. Das funktioniert überhaupt nicht, wie ich gedacht hatte.

Update 2 : Funktioniert derzeit in keinem anderen Browser als dem IE, aber CSS3 unterstützt dies in Form vontext-align-last

Jordi Bunster
quelle
Das war schnell! Ich dachte, meine eigene Lösung sei einzigartig, aber Sie haben sich in wenigen Augenblicken etwas Ähnliches ausgedacht.
Flug
Jetzt im Jahr 2016 text-align-last funktioniert in allen anderen Browsern als Safari . Dies sollte der mögliche Gegner des Flexbox-Ansatzes oder des Span-Hacks sein .
Hakatashi
1

Für Gecko-basierte Browser habe ich diese Lösung entwickelt. Diese Lösung funktioniert nicht mit WebKit-Browsern (z. B. Chromium, Midori, Epiphany), sie zeigen jedoch nach dem letzten Element noch Leerzeichen an.

Ich habe die Menüleiste in einen begründeten Absatz eingefügt . Das Problem ist, dass die letzte Zeile eines begründeten Absatzes aus offensichtlichen Gründen nicht gerechtfertigt wird. Daher füge ich ein weites unsichtbares Element hinzu (z. B. ein Bild), das garantiert, dass der Absatz mindestens zwei Zeilen lang ist.

Jetzt wird die Menüleiste durch denselben Algorithmus gerechtfertigt, den der Browser zum Ausrichten von einfachem Text verwendet.

Code:

<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>

Bemerkung: Merkst du, dass ich betrogen habe? Um das Leerzeichenelement hinzuzufügen, muss ich die Breite der Menüleiste erraten. Diese Lösung hängt also nicht vollständig von den Regeln ab.

Flug
quelle
Beachten Sie, dass Sie auch hier eine Liste hätten verwenden können, wenn Sie die Anzeige eingestellt haben: Inline auf den Listenelementen ... Listen sind für Menüs konventioneller.
Andrew Vit
@ Andrew Vit: Du hast recht. Das ist meistens auch die Lösung von asbjornu.
Flug
@flight, wenn du denkst, meine Antwort ist eine Lösung, kannst du sie bitte als eine markieren? Im Moment sieht es so aus, als ob Ihr Problem ungelöst ist. Wenn dies nicht der Fall ist, wäre es schön, wenn Sie uns die gefundene Lösung zur Verfügung stellen oder eine der bereitgestellten Antworten als Lösung für Ihr Problem markieren würden. :-)
Asbjørn Ulsberg
1

Text ist nur gerechtfertigt, wenn der Satz natürlich einen Zeilenumbruch verursacht. Alles, was Sie tun müssen, ist natürlich einen Zeilenumbruch zu erzwingen und zu verbergen, was in der zweiten Zeile steht:

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>

Das li.break-Element muss sich in derselben Zeile wie das letzte Menüelement befinden und Inhalte enthalten (in diesem Fall ein nicht unterbrechendes Leerzeichen). Andernfalls werden in einigen Browsern kleine Elemente angezeigt, wenn es sich nicht in derselben Zeile befindet Zusätzlicher Platz am Ende Ihrer Zeile. Wenn dieser keinen Inhalt enthält, wird er ignoriert und die Zeile ist nicht gerechtfertigt.

Getestet in IE7, IE8, IE9, Chrome, Firefox 4.

Diaren W.
quelle
0

Wenn Sie mit Javascript arbeiten möchten, ist dies möglich (dieses Skript basiert auf 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>

und die 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>

und das letzte 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>
Raksmey
quelle
0

Einfacheres Markup, getestet in 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>

und 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;
}

Live Beispiel .

Litek
quelle
-1

Dies kann perfekt durch einige sorgfältige Messungen und den Selektor für das letzte Kind erreicht werden.

ul li {
margin-right:20px;
}
ul li:last-child {
margin-right:0;
}
Jason Paul
quelle
4
Denken Sie daran, dass Text nicht überall gleich gerendert wird, ein Pixel entfernt und Sie ein kaputtes Menü sehen würden. Dies würde nur mit Elementen mit fester Breite wie Bildern funktionieren.
Fregante
-2

Ich kenne die ursprüngliche Frage, in der HTML + CSS angegeben ist, aber es wurde nicht ausdrücklich kein Javascript angegeben ;)

Beim Versuch, das CSS und das Markup so sauber wie möglich und semantisch so aussagekräftig wie möglich zu halten (unter Verwendung eines UL für das Menü), kam ich auf diesen Vorschlag. Wahrscheinlich nicht ideal, aber es kann ein guter Ausgangspunkt sein:

<!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>
David Heggie
quelle