Jak * naprawdę * uzasadnić menu poziome w HTML + CSS?


86

Znajdziesz wiele samouczków na temat pasków menu w HTML, ale w tym konkretnym (choć ogólnym przypadku IMHO) nie znalazłem żadnego przyzwoitego rozwiązania:

#  THE MENU ITEMS    SHOULD BE    JUSTIFIED     JUST AS    PLAIN TEXT     WOULD BE  #
#  ^                                                                             ^  #
  • Istnieje zmienna liczba pozycji menu zawierających tylko tekst, a układ strony jest płynny.
  • Pierwsza pozycja menu powinna być wyrównana do lewej, a ostatnia pozycja menu powinna być wyrównana do prawej.
  • Pozostałe pozycje należy optymalnie rozłożyć na pasku menu.
  • Liczba jest różna, więc nie ma szansy na wstępne obliczenie optymalnych szerokości.

Zwróć uwagę, że TABELA również tutaj nie będzie działać:

  • Jeśli wyśrodkujesz wszystkie niszczyciele czołgów, pierwszy i ostatni przedmiot nie będą poprawnie ustawione.
  • Jeśli wyrównasz do lewej i do prawej pierwszy resp. ostatnie elementy odstępy będą nieoptymalne.

Czy to nie dziwne, że nie ma oczywistego sposobu zaimplementowania tego w czysty sposób za pomocą HTML i CSS?

Odpowiedzi:


43

Nowoczesne podejście - Flexboxy !

Teraz, gdy flexboksy CSS3 mają lepszą obsługę przeglądarki , niektórzy z nas mogą wreszcie zacząć ich używać. Po prostu dodaj dodatkowe prefiksy dostawców, aby zwiększyć zasięg przeglądarek .

W tym przypadku wystarczy ustawić element nadrzędny displayna, flexa następnie zmienić justify-contentwłaściwość na albo space-betweenlub space-aroundw celu dodania odstępu między lub wokół podrzędnych elementów flexbox.

Korzystaniejustify-content: space-between - ( przykład tutaj) :

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>


Korzystaniejustify-content: space-around - (przykład tutaj) :

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

Najprostszą rzeczą do zrobienia jest wymuszenie zerwania linii poprzez wstawienie elementu na końcu linii, który zajmie więcej niż pozostała dostępna przestrzeń, a następnie ukrycie go. Osiągnąłem to dość łatwo za pomocą prostego spanelementu, takiego jak:

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

Wszystkie śmieci wewnątrz #menu spanselektora są (o ile znalazłem) wymagane do zadowolenia większości przeglądarek. Powinien wymusić szerokość spanelementu do 100%, co powinno spowodować przerwanie linii, ponieważ ze względu na display: inline-blockregułę jest uważany za element wbudowany . inline-blockumożliwia również stosowanie spanreguł stylu na poziomie bloków, takich jak to, widthktóre powoduje, że element nie pasuje do menu, a tym samym menu łamie wiersz.

Oczywiście musisz dostosować szerokość spando swojego przypadku użycia i projektu, ale mam nadzieję, że masz ogólny pomysł i możesz go dostosować.


1
Wydaje się, że działa, jeśli używasz spanzamiast hr! To naprawdę nie działa, HR zajmuje widoczną przestrzeń - użyj, #menu { border: solid 1px green; }aby potwierdzić. Ponadto display: inline-block;nie działa w IE (... 7? CompatibilityView?) Dla elementów, które nie są naturalnie elementami wbudowanymi. HR jest elementem blokowym, więc domyślam się, że inline-block nie działa dla HR w IE. W każdym razie, rozpiętość.
ANeves uważa, że ​​SE jest zły

9
działa świetnie, ale wyniki będą ładniejsze, jeśli zastąpisz każdą spację znakiem & ensp; (n spacja), tak aby Menu i pozycja i 1 pozostały blisko siebie. & nbsp; nie działa w safari.
yitwail

15
Właśnie spędziłem ponad godzinę, uderzając głową o ścianę, próbując zrozumieć, dlaczego to nie działa dla mnie. Odpowiedź jest taka, że ​​potrzebowałem spacji między tagami. Pracuję w WordPress, a wp_page_menu nie uwzględnia podziałów wierszy po każdym <li> </li>. Dodałem je za pomocą preg_replace i to już działa. Fwewww
Greg Perham

1
Aby to zadziałało w dzisiejszych czasach, musisz użyć display: inline-block na LI w przeglądarkach, które go obsługują, i użyj display: inline ze spacjami zastąpionymi przez & nbsp; w przeglądarkach, które go nie obsługują. Ponieważ ie7 nie obsługuje inline-block, musisz użyć inline na swoim tagu wymuszonego zawijania i po prostu wepchnąć do niego wystarczającą liczbę & nbsp; s, aby wymusić zawijanie.
Joren

1
Aby wyjaśnić komentarz Jorena, UL nadal musi być w linii, a blok w linii LI.
Moss

12

Ok, to rozwiązanie nie działa na IE6 / 7 z powodu braku wsparcia dla :before/ :after, ale:

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>

Powodem, dla którego mam tag jako znacznik, jest inline-blockto, że nie chcę, aby słowa w środku były również uzasadnione, a także nie chcę używać nierozdzielających spacji.


Dzięki! Twoja dusza jest do rzeczy. Gdybym miał zadać pytanie, zaznaczyłbym je jako odpowiedź.
Andrevinsky

Miałem problemy z uruchomieniem tego w IE. Po kilku godzinach spędzonych w krainie Google w końcu znalazłem ten wpis na blogu: kristinlbradley.wordpress.com/2011/09/15/… Ostatecznie udało się dodać text-justify: distribute-all-lines;do ulselektora. Wydaje się, że jest to zastrzeżony element IE.
maryisdead

Nie jestem pewien, czy to ma znaczenie, ale width: 100%zamiast margin-left: 100%: ul:after{content:""; margin-left:100%;}
Eugene Song

8

Mam rozwiązanie. Działa w FF, IE6, IE7, Webkit itp.

Upewnij się, że nie wstawiłeś żadnych spacji przed zamknięciem span.inner. IE6 się zepsuje.

Możesz opcjonalnie podać .outerszerokość

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


Dla mnie to rozwiązanie działa dobrze z Gecko, ale nie z przeglądarkami WebKit (testowane z Chromium, Midori, Epiphany): W przypadku WebKit po ostatnim elemencie znajduje się spacja.
lot

Upewnij się, że między każdym zakresem jest tylko jeden znak odstępu i dwa między ostatnim wewnętrznym a końcem. Jeśli to nie zadziała, baw się spacjami. Webkit zawiera błąd.
mikelikespie

1
.outer {text-align: justify} .outer span.finish {display: inline-block; szerokość: 100%} .outer span.inner {display: inline-block; white-space: nowrap} Nie działa w IE6 i IE7.
Binyamin,

Podanie .outer line-height: 0wymusza, aby wysokość listy była renderowana tak, jakby składała się z jednej linii.
kontur

4

Działa z przeglądarką Opera, Firefox, Chrome i 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
Niestety to nie działa w IE 7 z powodu braku obsługi komórek tabeli
Philipp Michael

3

jeszcze inne rozwiązanie. Nie miałem opcji, aby zająć się HTML, dodając wyróżnioną klasę itp., Więc znalazłem czysty sposób CSS.

Działa w Chrome, Firefox, Safari… nie wiem o 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

Zrobić to <p>z text-align: justify?

Aktualizacja : Nevermind. To w ogóle nie działa, jak myślałem.

Aktualizacja 2 : Obecnie nie działa w żadnej przeglądarce innej niż IE, ale CSS3 obsługuje to w postacitext-align-last


To było szybkie! Myślałem, że moje własne rozwiązanie jest wyjątkowe, ale w ciągu kilku chwil wymyśliłeś coś podobnego na początek.
lot

Teraz w 2016 text-align-last działa w każdej przeglądarce innej niż Safari . Powinien to być potencjalny przeciwnik podejścia flexbox lub hack span .
hakatashi

1

W przypadku przeglądarek opartych na Gecko wymyśliłem to rozwiązanie. To rozwiązanie nie działa z przeglądarkami WebKit, chociaż (np. Chromium, Midori, Epiphany), nadal pokazują one spację na końcu ostatniego elementu.

Umieściłem pasek menu w uzasadnionym akapicie . Problem polega na tym, że z oczywistych powodów ostatnia linijka uzasadnionego akapitu nie będzie uzasadniona. Dlatego dodaję szeroki niewidoczny element (np. Img), który gwarantuje, że akapit ma co najmniej dwie linie długości.

Teraz pasek menu jest wyrównywany przez ten sam algorytm, którego używa przeglądarka do justowania zwykłego tekstu.

Kod:

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

Uwaga: Czy zauważyłeś, że oszukiwałem? Aby dodać element wypełniający spację, muszę zgadnąć co do szerokości paska menu. Więc to rozwiązanie nie jest całkowicie zgodne z zasadami.


Zauważ, że mógłbyś użyć listy również tutaj, jeśli ustawisz display: inline na list-items ... listy są bardziej konwencjonalne dla menu.
Andrew Vit

@ Andrew Vit: Masz rację. To głównie sugeruje rozwiązanie Asbjornu.
lot

@flight, jeśli uważasz, że moja odpowiedź jest rozwiązaniem, czy możesz ją oznaczyć jako jedną? W tej chwili wygląda na to, że Twój problem nie został rozwiązany. Jeśli tak nie jest, byłoby miło, gdybyś dostarczył nam rozwiązanie, które znalazłeś, lub oznaczył jedną z udzielonych odpowiedzi jako rozwiązanie problemu. :-)
Asbjørn Ulsberg

1

Tekst jest justowany tylko wtedy, gdy zdanie w naturalny sposób powoduje podział wiersza. Wszystko, co musisz zrobić, to naturalnie wymusić przerwanie linii i ukryć to, co jest w drugiej linii:

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>

Element li.break musi znajdować się w tej samej linii, co ostatnia pozycja menu i musi zawierać jakąś zawartość (w tym przypadku nierozdzielającą spację), w przeciwnym razie w niektórych przeglądarkach, jeśli nie znajduje się w tej samej linii, zobaczysz małe dodatkowa spacja na końcu linii, a jeśli nie zawiera treści, jest ignorowana, a linia nie jest wyjustowana.

Przetestowano w IE7, IE8, IE9, Chrome, Firefox 4.


0

jeśli chcesz korzystać z javascript, który jest możliwy (ten skrypt jest oparty na 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>

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

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

Prostsze znaczniki, przetestowane w przeglądarce 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>

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

Przykład na żywo .


-1

Można to doskonale osiągnąć poprzez staranne pomiary i selektor ostatniego dziecka.

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

4
Pamiętaj, że tekst nie wszędzie jest renderowany tak samo, piksel wyłączony i zobaczysz zepsute menu. Działa to tylko z elementami o stałej szerokości, takimi jak obrazy.
fregante

-2

Wiem, że oryginalne pytanie określało HTML + CSS, ale nie było w nim wyraźnie napisane brak javascript ;)

Starając się, aby css i znaczniki były tak czyste, jak to tylko możliwe, i jak najbardziej znaczące semantycznie (używając UL jako menu), wpadłem na tę sugestię. Prawdopodobnie nie jest idealny, ale może być dobrym punktem wyjścia:

<!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>
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.