Come posso * davvero * giustificare un menu orizzontale in HTML + CSS?


86

Trovi molti tutorial sulle barre dei menu in HTML, ma per questo caso specifico (anche se generico IMHO), non ho trovato alcuna soluzione decente:

#  THE MENU ITEMS    SHOULD BE    JUSTIFIED     JUST AS    PLAIN TEXT     WOULD BE  #
#  ^                                                                             ^  #
  • C'è un numero variabile di voci di menu di solo testo e il layout della pagina è fluido.
  • La prima voce di menu dovrebbe essere allineata a sinistra, l'ultima voce di menu dovrebbe essere allineata a destra.
  • Gli elementi rimanenti dovrebbero essere distribuiti in modo ottimale sulla barra dei menu.
  • Il numero è variabile, quindi non è possibile pre-calcolare le larghezze ottimali.

Nota che una TABELLA non funzionerà anche qui:

  • Se centrate tutti i TD, il primo e l'ultimo elemento non sono allineati correttamente.
  • Se si allinea a sinistra e si allinea a destra il primo resp. gli ultimi elementi, la spaziatura sarà subottimale.

Non è strano che non ci sia un modo ovvio per implementarlo in modo pulito utilizzando HTML e CSS?

Risposte:


43

Approccio moderno - Flexbox !

Ora che i flexbox CSS3 hanno un supporto migliore per i browser , alcuni di noi possono finalmente iniziare a usarli. Basta aggiungere altri prefissi del fornitore per una maggiore copertura del browser .

In questo caso, devi semplicemente impostare l'elemento genitore displaysu flexe quindi modificare la justify-contentproprietà su space-betweeno space-aroundper aggiungere spazio tra o intorno agli elementi flexbox figli.

Utilizzandojustify-content: space-between - ( esempio qui) :

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>


Utilizzandojustify-content: space-around - (esempio qui) :

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

La cosa più semplice da fare è forzare la rottura della linea inserendo un elemento alla fine della linea che occuperà più dello spazio disponibile a sinistra e poi nascondendolo. L'ho realizzato abbastanza facilmente con un semplice spanelemento come questo:

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

Tutta la spazzatura all'interno del #menu spanselettore è (per quanto ho trovato) necessaria per soddisfare la maggior parte dei browser. Dovrebbe forzare la larghezza spandell'elemento al 100%, il che dovrebbe causare un'interruzione di riga poiché è considerato un elemento in linea a causa della display: inline-blockregola. inline-blockrende anche spanpossibile regole di stile a livello di blocco come il widthche fa sì che l'elemento non si adatti in linea con il menu e quindi il menu interrompe la riga.

Ovviamente devi regolare la larghezza del spantuo caso d'uso e design, ma spero che tu abbia un'idea generale e possa adattarla.


1
Sembra funzionare se usi a spaninvece di hr! Non funziona davvero, le risorse umane occupano uno spazio visibile - utilizzare #menu { border: solid 1px green; }per confermare. Inoltre, display: inline-block;non funziona su IE (... 7? CompatibilityView?) Per elementi che non sono elementi naturalmente inline. Le risorse umane sono un elemento di blocco, quindi immagino che il blocco in linea non funzioni per le risorse umane su IE. Comunque, span.
ANeves pensa che SE sia il male

9
funziona alla grande, ma i risultati sono più belli se sostituisci ogni spazio con & ensp; (n spazio), in modo che Menu e voce e 1 rimangano vicini. & nbsp; non funziona in safari.
yitwail

15
Ho appena passato più di un'ora a sbattere la testa contro il muro, cercando di capire perché questo non funzionava per me. La risposta è che avevo bisogno di uno spazio bianco tra i tag. Lavoro in WordPress e wp_page_menu non include interruzioni di riga dopo ogni <li> </li>. Li ho aggiunti usando preg_replace e ora funziona. Fwewww
Greg Perham

1
Per farlo funzionare in questi giorni devi usare display: inline-block sugli LIs nei browser che lo supportano e utilizzare display: inline con spazi sostituiti da & nbsp; sui browser che non lo supportano. Inoltre, poiché ie7 non supporta il blocco inline, devi utilizzare inline sul tag force-wrap e spingere abbastanza & nbsp; s in esso per forzare il wrap.
Joren

1
Per chiarire il commento di Joren, l'UL deve ancora essere in linea e il blocco in linea dell'IL.
Moss

12

Ok, questa soluzione non funziona su IE6 / 7, a causa della mancanza di supporto di :before/ :after, ma:

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>

Il motivo per cui ho il tag a inline-blockè perché non voglio che anche le parole all'interno siano giustificate e non voglio nemmeno usare spazi unificatori.


Grazie! La tua anima è fino al punto. Se dovessi iniziare la domanda, la contrassegnerei come una risposta.
Andrevinsky

Ho avuto problemi a farlo funzionare in IE. Dopo alcune ore trascorse in Google Land ho finalmente trovato questo post sul blog: kristinlbradley.wordpress.com/2011/09/15/… Quello che alla fine ha fatto il trucco è stato aggiungere text-justify: distribute-all-lines;al ulselettore. Sembra essere roba proprietaria di IE.
maryisdead

Non sono sicuro che sia importante, ma ho usato al width: 100%posto di margin-left: 100%: ul:after{content:""; margin-left:100%;}
Eugene Song

8

Ho una soluzione. Funziona in FF, IE6, IE7, Webkit, ecc.

Assicurati di non inserire spazi bianchi prima di chiudere il file span.inner. IE6 si romperà.

Opzionalmente puoi dare .outeruna larghezza

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


Per me, questa soluzione funziona bene con Gecko, ma non con i browser WebKit (testati con Chromium, Midori, Epiphany): con WebKit, c'è uno spazio finale dopo l'ultimo elemento.
volo

Assicurati che ci sia un solo carattere di spazio vuoto tra ogni intervallo e due tra l'ultimo interno e il traguardo. Se questo non funziona giocherellare con gli spazi bianchi. C'è un bug nel webkit.
mikelikespie

1
.outer {text-align: justify} .outer span.finish {display: inline-block; larghezza: 100%} .outer span.inner {display: inline-block; white-space: nowrap} Non funziona su IE6 e IE7.
Binyamin

Dando le .outer line-height: 0forze che l'altezza della lista venga resa come se fosse composta da una riga.
kontur

4

Funziona con Opera, Firefox, Chrome e 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;
}

1
Sfortunatamente questo non funziona in IE 7 a causa della mancanza del supporto table-cell
Philipp Michael

3

ancora un'altra soluzione. Non avevo alcuna opzione per affrontare l'html come l'aggiunta di classi distinte ecc., Quindi ho trovato un modo puro CSS.

Funziona in Chrome, Firefox, Safari ... non so di 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>


2

Farlo <p>con text-align: justify?

Aggiornamento : non importa. Non funziona affatto come pensavo.

Aggiornamento 2 : al momento non funziona in altri browser diversi da IE, ma CSS3 lo supporta sotto forma ditext-align-last


È stato veloce! Pensavo che la mia soluzione fosse unica, ma in pochi istanti hai escogitato qualcosa di simile con cui iniziare.
volo

Ora nel 2016 text-align-last funziona con qualsiasi browser diverso da Safari . Questo dovrebbe essere il possibile avversario dell'approccio flexbox o dello span hack .
hakatashi

1

Per i browser basati su Gecko, ho trovato questa soluzione. Questa soluzione non funziona con i browser WebKit, tuttavia (ad esempio Chromium, Midori, Epiphany), mostrano ancora lo spazio finale dopo l'ultimo elemento.

Ho inserito la barra dei menu in un paragrafo giustificato . Il problema è che l'ultima riga di un paragrafo giustificato non sarà giustificata, per ovvie ragioni. Quindi aggiungo un elemento invisibile largo (ad esempio un img) che garantisce che il paragrafo sia lungo almeno due righe.

Ora la barra dei menu è giustificata dallo stesso algoritmo utilizzato dal browser per giustificare il testo normale.

Codice:

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

Nota: hai notato che ho tradito? Per aggiungere l'elemento di riempimento dello spazio, devo fare qualche supposizione sulla larghezza della barra dei menu. Quindi questa soluzione non è completamente conforme alle regole.


Nota che potresti aver usato un elenco anche qui, se imposti display: inline sugli elementi dell'elenco ... gli elenchi sono più convenzionali per i menu.
Andrew Vit,

@ Andrew Vit: Hai ragione. Questo è soprattutto ciò che suggerisce anche la soluzione di Asbjornu.
volo

@flight, se pensi che la mia risposta sia una soluzione, puoi contrassegnarla come tale? In questo momento sembra che il tuo problema sia irrisolto. In caso contrario, sarebbe bello se ci fornissi la soluzione che hai trovato o contrassegnassi una delle risposte fornite come soluzione al tuo problema. :-)
Asbjørn Ulsberg

1

Il testo è giustificato solo se la frase provoca naturalmente un'interruzione di riga. Quindi tutto ciò che devi fare è forzare naturalmente un'interruzione di riga e nascondere cosa c'è sulla seconda riga:

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>

L'elemento li.break deve essere sulla stessa riga dell'ultima voce di menu e deve contenere del contenuto (in questo caso uno spazio unificatore), altrimenti in alcuni browser, se non è sulla stessa riga, vedrai dei piccoli spazio aggiuntivo alla fine della riga e se non contiene contenuto viene ignorato e la riga non è giustificata.

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


0

se andare con javascript è possibile (questo script è basato su 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>

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

e l'ultimo 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

Markup più semplice, testato 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>

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

Esempio dal vivo .


-1

Ciò può essere ottenuto perfettamente con alcune misurazioni attente e il selettore dell'ultimo figlio.

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

4
Tieni presente che il testo non viene visualizzato lo stesso ovunque, un pixel è spento e vedresti un menu rotto. Funzionerebbe solo con elementi a larghezza fissa come le immagini.
fregante

-2

So che la domanda originale specificava HTML + CSS, ma non diceva specificamente nessun javascript ;)

Cercando di mantenere il css e il markup il più puliti possibile e semanticamente significativi (utilizzando un UL per il menu), mi è venuto in mente questo suggerimento. Probabilmente non è l'ideale, ma potrebbe essere un buon punto di partenza:

<!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>
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.