今天,我將爲你提供一個關於如何建立分層導航彈出式菜單的快速教程,該菜單能夠跨多個級別進行深層嵌套。css
做爲拋磚引玉,咱們將從一個具體的實際用例開始 —— 一個桌面應用程序的示例菜單欄。我將選擇 Chrome 瀏覽器菜單欄中的一個子列表來講明這一點。html
咱們將從一個簡單的界面和外觀入手,源自經典的 Windows™ 主題,這裏有個短視頻告訴你它長什麼樣:前端
css-nav-menu-3.mp4android
在最後,咱們會增長一些樣式,讓它有點像 MacOS™ 的感受。ios
讓咱們先了解一下菜單項一般由什麼組成。它們應該具備如下屬性:git
請注意,咱們能夠繼續向菜單添加更復雜的行爲。例如,某個菜單能夠是一個 切換 項,因此,須要某種形式的記號(✔)或與之關聯的複選框,以指示其打開/關閉狀態。github
咱們將使用 CSS classes 在 HTML 標記上指示這些屬性,並編寫一些巧妙的樣式來傳遞全部相應的行爲。web
基於上文,咱們的基本菜單 HTML 應該是什麼樣子:後端
ul
元素定義,單個菜單項固然是 li
。span
元素放置在 li
中的錨(a
)標籤內並帶有相應 CSS 類(label
或 shortcut
),因此點擊它會調用導航事件,還能夠提供一些 UI 反饋,例如在 Hover 時突出顯示菜單項。li
元素(父)中的另外一個 ul
元素中,依此類推。這個特定的菜單項包含一個子菜單,而且可以添加一些特定的樣式以使其正常工做(以及諸如 ▶ 指示符之類的可視元素,)們將向 li
此父級添加 has-children
CSS 類。li
上中添加一個名爲 separator
的相應 CSS 類來表示它。disabled
CSS 類。它的做用是使此項沒法響應鼠標事件,如懸停或點擊。nav
容器元素中。(這樣語義化很好)併爲其添加 flyout-nav
類,以獲取咱們將添加的CSS樣式的一些基本命名空間。<nav class="flyout-nav">
<ul>
<li>
<a href="#"><span class="label">File</span></a>
<ul>
<li>
<a href="#">
<span class="label">New Tab</span>
<span class="shortcut">⌘T</span>
</a>
</li>
<li>
<a href="#">
<span class="label">New Window</span>
<span class="shortcut">⌘N</span>
</a>
</li>
<li class="separator"></li>
<li class="has-children">
<a href="#">
<span class="label">Share...</span>
</a>
<ul>
<li>
<a href="#">
<span class="label">✉️ Email</span>
</a>
</li>
<li>
<a href="#">
<span class="label">💬 Messages</span>
</a>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</nav>
複製代碼
我撒了謊。咱們將使用 SCSS 代替。瀏覽器
不開玩笑了,有趣的部分來了!
默認狀況下應該 隱藏 菜單(第一級 導航菜單條
除外)。
只有在使用鼠標指針懸停相應的菜單項時,才應顯示第一級下的任何內容。你可能已經猜到了,爲了這個咱們將嚴重依賴 CSS 的 hover
僞類。
理解咱們如何使子菜單位置的正確並將其自身與父菜單項對齊也許是整個謎題中最棘手的一點。這就是 CSS 定位的一些知識來源。讓咱們看看這個。
咱們之因此選擇將子菜單 ul
元素放在「父」 li
元素中是有緣由的。固然,它有助於咱們在邏輯上適當地將分層內容的標記組合在一塊兒。它還有另外一個目的,即容許咱們輕鬆編寫一些 CSS 來相對於父元素的位置定位子元素。而後咱們將這個概念一直延伸到根元素 ul
和 li
。
爲此,咱們將使用 absolute
定位和 top
的組合,left
CSS 屬性將幫助咱們相對於其最近的非靜態定位祖先(closest non-static positioned ancestor) 定位子元素定義包含塊。非靜態(non-static)的意思是元素的 CSS position 屬性不是 static
(這默認發生在 HTML 文檔流中),但它是 relative
、absolute
、fixed
或者 sticky
其中之一。爲了確保這一點,咱們將把 position relative
分配給 li
元素,並將其子元素 ul
的 position 設置爲 absolute
。
.flyout-nav {
// 任何級別的菜單項列表
ul {
margin: 0;
padding: 0;
position: absolute;
display: none;
list-style-type: none;
}
// 菜單項
li {
position: relative;
display: block;
// 顯示上的下一級下拉列表
// 在同一高度的右邊
&:hover {
& > ul {
display: block;
top: 0;
left: 100%;
}
}
}
複製代碼
其效果以下圖所示,並在紅色框中突出顯示以供說明。爲了使圖片看起來更漂亮,咱們在圖片中添加了一些用於視覺樣式的 CSS,可是核心行爲是由上面的內容定義的。這使其在 N 層嵌套內(在實用性的限制範圍內)保持良好的工做狀態。
但有一個例外,即第一級菜單項列表(在咱們的示例中,File、Edit、View...),其子菜單項須要放在 下方 而不是右側。爲了處理這個問題,咱們添加了一些新的樣式重寫了以前的 CSS。
.flyout-nav {
// ... 其餘的東西
// 一級行爲的覆蓋(導航菜單條)
& > ul {
display: flex;
flex-flow: row nowrap;
justify-content: flex-start;
align-items: stretch;
// 應顯示第一級下拉列表
// 在同一左側位置
& > li:hover > ul {
top: 100%;
left: 0;
}
}
}
複製代碼
請注意,在這裏不必定非要使用彈性盒子 flex-box
,這只是我作的選擇。你也可使用其餘方法實現相似的行爲,例如在 ul
和 li
項上組合 display: block
和 display: inline-block
。
一旦咱們完成了對菜單項定位的基本操做,咱們將繼續編寫一些額外的樣式,如字體、大小、顏色、背景和陰影等,以使 UI 感受更好。
爲了一致性和重用,咱們採起使用一組 SCSS 變量定義和共享了這些值。像這樣...
// 變量
$page-bg: #607d8b;
$base-font-size: 16px; // 變成 1rem
$menu-silver: #eee;
$menu-border: #dedede;
$menu-focused: #1e88e5;
$menu-separator: #ccc;
$menu-text-color: #333;
$menu-shortcut-color: #999;
$menu-focused-text-color: #fff;
$menu-text-color-disabled: #999;
$menu-border-width: 1px;
$menu-shadow: 2px 2px 3px -3px $menu-text-color;
$menu-content-padding: 0.5rem 1rem 0.5rem 1.75rem;
$menu-border-radius: 0.5rem;
$menu-top-padding: 0.25rem;
複製代碼
咱們還剩下一些部分要添加合適的樣式和特性。咱們如今將會快速地把它們過一遍。
.flyout-nav {
// ... 其餘的東西
li {
// ... 其餘的東西
// 菜單項-文本、快捷方式信息和懸停效果(藍色背景)
a {
text-decoration: none;
color: $menu-text-color;
position: relative;
display: table;
width: 100%;
.label,
.shortcut {
display: table-cell;
padding: $menu-content-padding;
}
.shortcut {
text-align: right;
color: $menu-shortcut-color;
}
label {
cursor: pointer;
}
// 對於切換的菜單項
input[type='checkbox'] {
display: none;
}
input[type='checkbox']:checked + .label {
&::before {
content: '✔️';
position: absolute;
top: 0;
left: 0.25rem;
padding: 0.25rem;
}
}
&:hover {
background: $menu-focused;
.label,
.shortcut {
color: $menu-focused-text-color;
}
}
}
}
}
複製代碼
這段代碼的大部份內容都是簡單明瞭的。可是,你注意到什麼有趣的事情了嗎?關於 input[type='checkbox']
?
對於切換,咱們使用隱藏的 HTML 複選框元素來維護狀態(打開或關閉)並相應地使用 ::before
僞元素爲標籤設置樣式。咱們可使用一個簡單的 CSS 相鄰兄弟選擇器來作到這一點。
該菜單項的相應 HTML 標記以下所示:
<li>
<a href="#">
<input type="checkbox" id="alwaysShowBookmarksBar" checked="true" />
<label class="label" for="alwaysShowBookmarksBar">Always Show Bookmarks Bar</label>
<span class="shortcut">⇧⌘B</span>
</a>
</li>
複製代碼
.flyout-nav {
// ... 其餘的東西
li {
// ... 其餘的東西
// 分隔符項
&.separator {
margin-bottom: $menu-top-padding;
border-bottom: $menu-border-width solid $menu-separator;
padding-bottom: $menu-top-padding;
}
}
}
複製代碼
.flyout-nav {
// ... 其餘的東西
li {
// ... 其餘的東西
// 不要讓禁用的選項響應 hover
// 或者點擊並給它們塗上不一樣的顏色
&.disabled {
.label,
.shortcut {
color: $menu-text-color-disabled;
}
pointer-events: none;
}
}
}
複製代碼
CSS pointer-events 在這有個實用的技巧。將其設置爲 none
將變成不可選的鼠標事件目標對象。
如今咱們已經瞭解了這些構造塊,讓咱們把它們組合一塊兒。這裏有一個 CodePen 連接到咱們的多層次彈出式導航菜單的行動!
若是你不喜歡復古 Windows 的外觀,這是同一代碼的另外一個版本,對 CSS 進行了一些細微的調整,使其看起來和感受更像 MacOS。
示例:僅限於 CSS 的多級嵌套彈出式導航菜單(相似於 MacOS)
有一些事情咱們尚未處理。首先,
但願這是有用的。乾杯!
若是發現譯文存在錯誤或其餘須要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可得到相應獎勵積分。文章開頭的 本文永久連接 即爲本文在 GitHub 上的 MarkDown 連接。
掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 Android、iOS、前端、後端、區塊鏈、產品、設計、人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。