Bagaimana cara * benar-benar * membenarkan menu horizontal di HTML + CSS?

87

Anda menemukan banyak tutorial tentang bilah menu dalam HTML, tetapi untuk kasus khusus ini (meskipun IMHO generik), saya belum menemukan solusi yang layak:

#  THE MENU ITEMS    SHOULD BE    JUSTIFIED     JUST AS    PLAIN TEXT     WOULD BE  #
#  ^                                                                             ^  #
  • Ada jumlah item menu hanya teks yang bervariasi dan tata letak halaman dapat disesuaikan.
  • Item menu pertama harus rata kiri, item menu terakhir harus rata kanan.
  • Item yang tersisa harus tersebar secara optimal di bilah menu.
  • Jumlahnya bervariasi, jadi tidak ada peluang untuk menghitung sebelumnya lebar optimal.

Perhatikan bahwa TABEL tidak akan berfungsi di sini juga:

  • Jika Anda memusatkan semua TD, item pertama dan terakhir tidak sejajar dengan benar.
  • Jika Anda rata kiri dan kanan di resp pertama. item terakhir, jarak akan menjadi sub-optimal.

Bukankah aneh bahwa tidak ada cara yang jelas untuk menerapkan ini secara bersih dengan menggunakan HTML dan CSS?

penerbangan
sumber

Jawaban:

43

Pendekatan Modern - Flexboxes !

Sekarang karena CSS3 flexboxes memiliki dukungan browser yang lebih baik , beberapa dari kita akhirnya dapat mulai menggunakannya. Cukup tambahkan prefiks vendor tambahan untuk cakupan browser yang lebih luas .

Dalam contoh ini, Anda hanya perlu menyetel elemen induk displayke flexdan kemudian mengubah justify-contentproperti menjadi salah satu space-betweenatau space-arounduntuk menambahkan spasi di antara atau di sekitar item anak flexbox.

Menggunakanjustify-content: space-between - ( contoh di sini) :

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>


Menggunakanjustify-content: space-around - (contoh di sini) :

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

Hal paling sederhana yang harus dilakukan adalah dengan memaksa garis putus dengan memasukkan elemen di akhir garis yang akan menempati lebih dari ruang yang tersedia dan kemudian menyembunyikannya. Saya telah menyelesaikan ini dengan cukup mudah dengan spanelemen sederhana seperti ini:

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

Semua sampah di dalam #menu spanpemilih (sejauh yang saya temukan) diperlukan untuk menyenangkan sebagian besar browser. Ini harus memaksa lebar spanelemen menjadi 100%, yang akan menyebabkan jeda baris karena dianggap sebagai elemen sebaris karena display: inline-blockaturan. inline-blockjuga spanmemungkinkan untuk aturan gaya tingkat blok seperti widthyang menyebabkan elemen tidak sesuai dengan menu dan dengan demikian menu menjadi pemutusan baris.

Anda tentu saja perlu menyesuaikan lebar spankasus penggunaan dan desain Anda, tetapi saya harap Anda mendapatkan gambaran umum dan dapat menyesuaikannya.

Asbjørn Ulsberg
sumber
1
Tampaknya berfungsi jika Anda menggunakan a, spanbukan hr! Ini tidak benar-benar berfungsi, HR menempati ruang yang terlihat - gunakan #menu { border: solid 1px green; }untuk mengonfirmasi. Juga, display: inline-block;tidak berfungsi pada IE (... 7? CompatibilityView?) Untuk elemen yang bukan elemen sebaris secara alami. HR adalah elemen blok, jadi saya rasa blok-inline tidak berfungsi untuk HR di IE. Pokoknya, rentang.
ANeves menganggap SE itu jahat
9
berfungsi dengan baik, tetapi hasilnya akan lebih cantik jika Anda mengganti setiap ruang dengan & ensp; (n spasi), sehingga Menu & item & 1 tetap berdekatan. & nbsp; tidak bekerja di safari.
yitwail
15
Saya hanya menghabiskan lebih dari satu jam membenturkan kepala saya ke dinding, mencoba mencari tahu mengapa ini tidak berhasil untuk saya. Jawabannya adalah saya membutuhkan spasi kosong di antara tag. Saya bekerja di WordPress, dan wp_page_menu tidak menyertakan jeda baris setelah setiap <li> </li>. Menambahkannya menggunakan preg_replace, dan itu berfungsi sekarang. Fwewww
Greg Perham
1
Untuk membuatnya bekerja sekarang ini, Anda harus menggunakan display: inline-block pada LI di browser yang mendukungnya, dan menggunakan display: inline dengan spasi yang diganti dengan & nbsp; di browser yang tidak mendukungnya. Juga karena ie7 tidak mendukung pemblokiran-sebaris, Anda harus menggunakan sebaris pada tag force-wrap Anda dan cukup dorong cukup & nbsp; di dalamnya untuk memaksa pembungkus.
Joren
1
Untuk memperjelas komentar Joren, UL masih harus sebaris dan blok sebaris LI.
Moss
12

Oke, solusi ini tidak berfungsi di IE6 / 7, karena kurangnya dukungan :before/ :after, tetapi:

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>

Alasan mengapa saya memiliki tag sebagai an inline-blockadalah karena saya tidak ingin kata-kata di dalamnya dibenarkan juga, dan saya juga tidak ingin menggunakan spasi tanpa putus.

remitbri
sumber
Terima kasih! Jiwa Anda langsung ke intinya. Jika saya memulai pertanyaan, saya akan menandainya sebagai jawaban.
Andrevinsky
Saya mengalami masalah saat membuat ini berfungsi di IE. Setelah beberapa jam dihabiskan di Google lahan saya akhirnya menemukan posting blog ini: kristinlbradley.wordpress.com/2011/09/15/... Apa akhirnya melakukan trik adalah untuk menambahkan text-justify: distribute-all-lines;ke ulpemilih. Tampaknya barang IE milik sendiri.
maryisdead
Tidak yakin apakah hal ini, tapi saya menggunakan width: 100%bukannya margin-left: 100%: ul:after{content:""; margin-left:100%;}
Eugene Lagu
8

Punya solusi. Bekerja di FF, IE6, IE7, Webkit, dll.

Pastikan Anda tidak meletakkan spasi sebelum menutup file span.inner. IE6 akan rusak.

Anda secara opsional dapat memberi .outerlebar

.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
sumber
Bagi saya, solusi ini berfungsi dengan baik dengan Gecko, tetapi tidak dengan browser WebKit (diuji dengan Chromium, Midori, Epiphany): Dengan WebKit, ada spasi tambahan setelah item terakhir.
Penerbangan
Pastikan hanya ada satu karakter spasi di antara setiap rentang, dan dua karakter antara bagian dalam terakhir dan akhir. Jika itu tidak berhasil, main-main dengan spasi. Ada bug di webkit.
mikelikespie
1
.outer {text-align: justify} .outer span.finish {display: inline-block; lebar: 100%} .outer span.inner {display: inline-block; white-space: nowrap} Tidak berfungsi di IE6 dan IE7.
Binyamin
Pemberian .outer line-height: 0memaksa tinggi daftar untuk dirender seolah-olah terdiri dari satu baris.
kontur
4

Bekerja dengan Opera, Firefox, Chrome dan 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
sumber
1
Sayangnya ini tidak berfungsi di IE 7 karena dukungan sel tabel
Philipp Michael
3

solusi lain. Saya tidak punya pilihan untuk menangani html seperti menambahkan kelas dibedakan dll, jadi saya menemukan cara css murni.

Bekerja di Chrome, Firefox, Safari..tidak tahu tentang IE.

Tes: 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
sumber
2

Buat itu <p>dengan text-align: justify?

Pembaruan : Nevermind. Itu tidak berhasil sama sekali seperti yang saya pikirkan.

Pembaruan 2 : Saat ini tidak berfungsi di browser selain IE, tetapi CSS3 memiliki dukungan untuk ini dalam bentuktext-align-last

Jordi Bunster
sumber
Cepat sekali! Saya pikir solusi saya sendiri unik, tetapi Anda menemukan sesuatu yang mirip untuk memulai hanya dalam beberapa saat.
Penerbangan
Sekarang di 2016 text-align-last berfungsi di browser apa pun selain Safari . Ini harus menjadi lawan yang mungkin dari pendekatan flexbox atau retasan span .
hakatashi
1

Untuk peramban berbasis Gecko, saya menemukan solusi ini. Solusi ini tidak bekerja dengan browser WebKit, meskipun (misalnya Chromium, Midori, Epiphany), mereka masih menunjukkan spasi setelah item terakhir.

Saya meletakkan bilah menu di paragraf yang dibenarkan . Masalahnya adalah bahwa baris terakhir dari paragraf yang dibenarkan tidak akan dibenarkan, karena alasan yang jelas. Oleh karena itu saya menambahkan elemen tak terlihat lebar (misalnya sebuah img) yang menjamin bahwa paragraf tersebut setidaknya dua baris.

Sekarang bilah menu dibenarkan oleh algoritma yang sama yang digunakan browser untuk membenarkan teks biasa.

Kode:

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

Komentar: Apakah Anda memperhatikan bahwa saya curang? Untuk menambahkan elemen pengisi ruang, saya harus menebak-nebak tentang lebar bilah menu. Jadi solusi ini tidak sepenuhnya tergantung pada aturan.

penerbangan
sumber
Perhatikan bahwa Anda juga dapat menggunakan daftar di sini, jika Anda menyetel display: sebaris pada daftar-item ... daftar lebih konvensional untuk menu.
Andrew Vit
@ Andrew Vit: Anda benar. Itulah yang sebagian besar disarankan oleh solusi asbjornu.
Penerbangan
@flight, jika menurut Anda jawaban saya adalah solusi, bisakah Anda menandainya sebagai salah satu? Saat ini sepertinya masalah Anda belum terpecahkan. Jika tidak, alangkah baiknya jika Anda memberi kami solusi yang Anda temukan atau tandai salah satu jawaban yang diberikan sebagai solusi untuk masalah Anda. :-)
Asbjørn Ulsberg
1

Teks hanya dibenarkan jika kalimat secara alami menyebabkan jeda baris. Jadi yang perlu Anda lakukan adalah secara alami memaksa pemutusan baris, dan menyembunyikan apa yang ada di baris kedua:

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>

Elemen li.break harus berada di baris yang sama dengan item menu terakhir dan harus berisi beberapa konten (dalam hal ini spasi non-breaking), sebaliknya di beberapa browser, jika tidak di baris yang sama maka Anda akan melihat beberapa konten kecil. spasi ekstra di akhir baris Anda, dan jika tidak berisi konten maka akan diabaikan dan baris tidak dibenarkan.

Diuji di IE7, IE8, IE9, Chrome, Firefox 4.

Diaren W.
sumber
0

jika menggunakan javascript itu mungkin (skrip ini berdasarkan 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>

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

dan html terakhir

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

Markup yang lebih sederhana, diuji di 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>

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

Contoh langsung .

Litek
sumber
-1

Ini dapat dicapai dengan sempurna dengan beberapa pengukuran yang cermat dan pemilih anak terakhir.

ul li {
margin-right:20px;
}
ul li:last-child {
margin-right:0;
}
Jason Paul
sumber
4
Ingatlah bahwa teks tidak ditampilkan sama di mana-mana, satu piksel mati dan Anda akan melihat menu yang rusak. Ini hanya akan bekerja dengan elemen lebar tetap seperti gambar.
fregante
-2

Saya tahu pertanyaan asli menentukan HTML + CSS, tetapi tidak secara khusus mengatakan tidak ada javascript ;)

Mencoba untuk menjaga css dan markup sebersih mungkin, dan semantik semantik mungkin untuk (menggunakan UL untuk menu) saya datang dengan saran ini. Mungkin tidak ideal, tetapi ini bisa menjadi titik awal yang baik:

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