Comment justifier * vraiment * un menu horizontal en HTML + CSS?

86

Vous trouverez de nombreux didacticiels sur les barres de menus en HTML, mais pour ce cas spécifique (bien que générique à IMHO), je n'ai trouvé aucune solution décente:

#  THE MENU ITEMS    SHOULD BE    JUSTIFIED     JUST AS    PLAIN TEXT     WOULD BE  #
#  ^                                                                             ^  #
  • Il existe un nombre variable d'éléments de menu contenant uniquement du texte et la mise en page est fluide.
  • Le premier élément de menu doit être aligné à gauche, le dernier élément de menu doit être aligné à droite.
  • Les éléments restants doivent être répartis de manière optimale dans la barre de menus.
  • Le nombre varie, il n'y a donc aucune chance de pré-calculer les largeurs optimales.

Notez qu'un TABLE ne fonctionnera pas non plus ici:

  • Si vous centrez tous les TD, le premier et le dernier élément ne sont pas alignés correctement.
  • Si vous alignez à gauche et à droite le premier resp. les derniers éléments, l'espacement sera sous-optimal.

N'est-il pas étrange qu'il n'y ait pas de moyen évident de mettre en œuvre cela de manière propre en utilisant HTML et CSS?

vol
la source

Réponses:

43

Approche moderne - Flexboxes !

Maintenant que les flexbox CSS3 ont un meilleur support de navigateur , certains d'entre nous peuvent enfin commencer à les utiliser. Ajoutez simplement des préfixes de fournisseur supplémentaires pour une plus grande couverture du navigateur .

Dans ce cas, vous devez simplement définir l'élément parent displaysur flex, puis modifier la justify-contentpropriété sur space-betweenou space-aroundafin d'ajouter de l'espace entre ou autour des éléments de la flexbox enfants.

En utilisant justify-content: space-between - ( exemple ici) :

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>


Utilisationjustify-content: space-around - (exemple ici) :

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
la source
83

La chose la plus simple à faire est de forcer la ligne à se rompre en insérant un élément à la fin de la ligne qui occupera plus que l'espace disponible gauche, puis en le cachant. J'ai accompli cela assez facilement avec un spanélément simple comme celui-ci:

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

Tous les déchets à l'intérieur du #menu spansélecteur sont (pour autant que j'ai trouvé) nécessaires pour plaire à la plupart des navigateurs. Il devrait forcer la largeur de l' spanélément à 100%, ce qui devrait provoquer un saut de ligne car il est considéré comme un élément en ligne en raison de la display: inline-blockrègle. inline-blockpermet également spande bloquer les règles de style au niveau du bloc, comme celles widthqui font que l'élément ne correspond pas au menu et donc le menu à un saut de ligne.

Vous devez bien sûr ajuster la largeur du spanà votre cas d'utilisation et à votre conception, mais j'espère que vous aurez l'idée générale et que vous pourrez l'adapter.

Asbjørn Ulsberg
la source
1
Cela semble fonctionner si vous utilisez un spanau lieu d'un hr! Cela ne fonctionne pas vraiment, le HR occupe un espace visible - utilisez #menu { border: solid 1px green; }pour confirmer. De plus, display: inline-block;ne fonctionne pas sur IE (... 7? CompatibilityView?) Pour les éléments qui ne sont pas naturellement des éléments en ligne. HR est un élément de bloc, donc je suppose que le bloc en ligne ne fonctionne pas pour HR sur IE. Quoi qu'il en soit, span.
ANeves pense que SE est diabolique le
9
fonctionne très bien, mais les résultats sont plus beaux si vous remplacez chaque espace par & ensp; (n espace), de sorte que le menu et l'élément et 1 restent proches l'un de l'autre. & nbsp; ne fonctionne pas en safari.
yitwail
15
Je viens de passer plus d'une heure à me cogner la tête contre le mur, essayant de comprendre pourquoi cela ne fonctionnait pas pour moi. La réponse est que j'avais besoin d'un espace blanc entre les balises. Je travaille sous WordPress et wp_page_menu n'inclut pas les sauts de ligne après chaque <li> </li>. Ajouté à l'aide de preg_replace, et cela fonctionne maintenant. Fwewww
Greg Perham
1
Pour que cela fonctionne ces jours-ci, vous devez utiliser display: inline-block sur les LI dans les navigateurs qui le prennent en charge, et utiliser display: inline avec des espaces remplacés par & nbsp; sur les navigateurs qui ne le prennent pas en charge. De plus, étant donné que ie7 ne prend pas en charge le bloc en ligne, vous devez utiliser inline sur votre balise forcée et y insérer suffisamment de & nbsp; s pour forcer l'enroulement.
Joren
1
Pour clarifier le commentaire de Joren, l'UL doit toujours être en ligne et le bloc en ligne du LI.
Moss
12

Ok, cette solution ne fonctionne pas sur IE6 / 7, à cause du manque de support de :before/ :after, mais:

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>

La raison pour laquelle j'ai la balise a comme an inline-blockest que je ne veux pas que les mots à l'intérieur soient également justifiés, et je ne veux pas non plus utiliser d'espaces insécables.

remitbri
la source
Merci! Votre soultion est au point. Si je devais lancer la question, je la marquerais comme réponse.
Andrevinsky
J'ai eu des problèmes pour que cela fonctionne dans IE. Après quelques heures passées sur Google land, j'ai finalement trouvé ce billet de blog: kristinlbradley.wordpress.com/2011/09/15 / ... Ce qui a finalement fait l'astuce a été d'ajouter text-justify: distribute-all-lines;au ulsélecteur. Semble être des trucs IE propriétaires.
maryisdead
Je ne sais pas si cela compte, mais j'ai utilisé à la width: 100%place de margin-left: 100%: ul:after{content:""; margin-left:100%;}
Eugene Song
8

J'ai une solution. Fonctionne dans FF, IE6, IE7, Webkit, etc.

Assurez-vous de ne pas mettre d'espaces avant de fermer le fichier span.inner. IE6 va casser.

Vous pouvez éventuellement donner .outerune largeur

.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
la source
Pour moi, cette solution fonctionne bien avec Gecko, mais pas avec les navigateurs WebKit (testés avec Chromium, Midori, Epiphany): avec WebKit, il y a un espace après le dernier élément.
vol du
Assurez-vous qu'il n'y a qu'un seul caractère d'espacement entre chaque travée et deux entre le dernier intérieur et l'arrivée. Si cela ne fonctionne pas, tripotez les espaces. Il y a un bogue dans le webkit.
mikelikespie
1
.outer {text-align: justify} .outer span.finish {display: inline-block; width: 100%} .outer span.inner {display: inline-block; white-space: nowrap} Ne fonctionne pas sur IE6 et IE7.
Binyamin du
Donner aux .outer line-height: 0forces la hauteur de la liste à rendre comme si elle était constituée d'une seule ligne.
kontur
4

Fonctionne avec Opera, Firefox, Chrome et 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
la source
1
Malheureusement, cela ne fonctionne pas dans IE 7 en raison de l'absence de prise en charge des cellules de table
Philipp Michael
3

encore une autre solution. Je n'avais aucune option pour aborder le html comme ajouter une classe distinguée, etc., donc j'ai trouvé un moyen purement css.

Fonctionne sous Chrome, Firefox, Safari..ne sais pas sur 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
la source
2

Faites-en un <p>avectext-align: justify ?

Mise à jour : Nevermind. Cela ne fonctionne pas du tout comme je le pensais.

Mise à jour 2 : ne fonctionne dans aucun navigateur autre que IE pour le moment, mais CSS3 prend en charge cela sous la forme detext-align-last

Jordi Bunster
la source
C'était rapide! Je pensais que ma propre solution était unique, mais vous êtes venu avec quelque chose de similaire pour commencer en quelques instants.
vol
Désormais, en 2016, text-align-last fonctionne dans tous les navigateurs autres que Safari . Cela devrait être l'adversaire possible de l'approche flexbox ou du hack span .
hakatashi
1

Pour les navigateurs basés sur Gecko, j'ai proposé cette solution. Cette solution ne fonctionne pas avec les navigateurs WebKit (par exemple, Chromium, Midori, Epiphany), ils affichent toujours un espace après le dernier élément.

Je mets la barre de menu dans un paragraphe justifié . Le problème est que la dernière ligne d'un paragraphe justifié ne sera pas justifiée, pour des raisons évidentes. Par conséquent, j'ajoute un élément invisible large (par exemple une img) qui garantit que le paragraphe fait au moins deux lignes de long.

Maintenant, la barre de menus est justifiée par le même algorithme que le navigateur utilise pour justifier le texte brut.

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>

Remarque: remarquez-vous que j'ai triché? Pour ajouter l'élément de remplissage d'espace, je dois deviner la largeur de la barre de menus. Cette solution n’est donc pas complètement aux règles.

vol
la source
Notez que vous auriez pu utiliser une liste ici aussi, si vous définissez display: inline sur les éléments de liste ... les listes sont plus conventionnelles pour les menus.
Andrew Vit
@Andrew Vit: Vous avez raison. C'est également ce que suggère la solution d'asbjornu.
vol le
@flight, si vous pensez que ma réponse est une solution, pouvez-vous la marquer comme telle? À l'heure actuelle, il semble que votre problème n'est pas résolu. Si ce n'est pas le cas, ce serait bien si vous nous fournissiez la solution que vous avez trouvée ou que vous marquiez l'une des réponses fournies comme la solution à votre problème. :-)
Asbjørn Ulsberg
1

Le texte n'est justifié que si la phrase provoque naturellement un saut de ligne. Donc, tout ce que vous avez à faire est naturellement de forcer un saut de ligne et de cacher ce qui se trouve sur la deuxième ligne:

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'élément li.break doit être sur la même ligne que le dernier élément de menu et doit contenir du contenu (dans ce cas un espace insécable), sinon dans certains navigateurs, s'il n'est pas sur la même ligne, vous verrez des petits un espace supplémentaire à la fin de votre ligne, et s'il ne contient aucun contenu, il est ignoré et la ligne n'est pas justifiée.

Testé dans IE7, IE8, IE9, Chrome, Firefox 4.

Diaren W
la source
0

si aller avec javascript c'est possible (ce script est basé sur 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>

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

et le dernier 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
la source
0

Balisage plus simple, testé dans 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>

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

Exemple en direct .

Litek
la source
-1

Ceci peut être réalisé parfaitement par quelques mesures soigneuses et le sélecteur du dernier enfant.

ul li {
margin-right:20px;
}
ul li:last-child {
margin-right:0;
}
Jason Paul
la source
4
Gardez à l'esprit que le texte ne rend pas le même partout, un pixel éteint et vous verrez un menu cassé. Cela ne fonctionnerait qu'avec des éléments de largeur fixe comme des images.
fregante
-2

Je sais que la question d'origine spécifiait HTML + CSS, mais elle ne disait pas spécifiquement pas de javascript ;)

En essayant de garder le css et le balisage aussi propres que possible, et aussi sémantiquement significatifs que possible (en utilisant un UL pour le menu), j'ai proposé cette suggestion. Probablement pas idéal, mais cela peut être un bon point de départ:

<!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
la source