[譯] 僅使用 HTML 和 CSS 建立多級嵌套彈出式導航菜單

僅使用 HTML 和 CSS 建立多級嵌套彈出式導航菜單

alt

今天,我將爲你提供一個關於如何建立分層導航彈出式菜單的快速教程,該菜單能夠跨多個級別進行深層嵌套。css

做爲拋磚引玉,咱們將從一個具體的實際用例開始 —— 一個桌面應用程序的示例菜單欄。我將選擇 Chrome 瀏覽器菜單欄中的一個子列表來講明這一點。html

咱們將從一個簡單的界面和外觀入手,源自經典的 Windows™ 主題,這裏有個短視頻告訴你它長什麼樣:前端

css-nav-menu-3.mp4android

在最後,咱們會增長一些樣式,讓它有點像 MacOS™ 的感受。ios

基礎

讓咱們先了解一下菜單項一般由什麼組成。它們應該具備如下屬性:git

  • Label:(必選)這基本上是菜單項的顯示名稱
  • Target:(可選)超連接,將用戶帶到一個頁面,做爲對單擊菜單項的響應。咱們如今將堅持它只是連接。在頁面中添加更多的動態特性須要用到JavaScript,咱們暫時不須要這麼作。這是你之後能夠隨時輕鬆添加的東西。
  • Shortcut:(可選)在咱們的例子中,顯示一個可用於此菜單項的快捷鍵組合。例如,「文件 > 新建」在Mac上會是 「Cmd + N」(⌘N)。
  • Children:(可選)指的是此菜單項的子菜單。想一想咱們的菜單和子菜單的形式 遞歸結構,從視覺效果來講,具備子菜單的菜單項上還應具備箭頭圖標 (▶)指示懸停時它能夠展開。
  • Disabled:(可選)指示菜單項是否能夠進行交互。
  • 一個概念 Type 參數嗎?(可選)能夠用這個模擬不一樣類型的菜單項。好比,菜單列表中的一些條目應該只起分隔符的做用。

請注意,咱們能夠繼續向菜單添加更復雜的行爲。例如,某個菜單能夠是一個 切換 項,因此,須要某種形式的記號(✔)或與之關聯的複選框,以指示其打開/關閉狀態。github

咱們將使用 CSS classes 在 HTML 標記上指示這些屬性,並編寫一些巧妙的樣式來傳遞全部相應的行爲。web

構建 HTML

基於上文,咱們的基本菜單 HTML 應該是什麼樣子:後端

  1. 菜單列表由 HTML ul 元素定義,單個菜單項固然是 li
  2. labelshortcut 將做爲 span 元素放置在 li 中的錨(a)標籤內並帶有相應 CSS 類(labelshortcut),因此點擊它會調用導航事件,還能夠提供一些 UI 反饋,例如在 Hover 時突出顯示菜單項。
  3. 當菜單項目包含一欄 子菜單(Children)們將該子菜單放在當前菜單 li 元素(父)中的另外一個 ul 元素中,依此類推。這個特定的菜單項包含一個子菜單,而且可以添加一些特定的樣式以使其正常工做(以及諸如 ▶ 指示符之類的可視元素,)們將向 li 此父級添加 has-children CSS 類。
  4. 對於像這樣的子項 分隔符,咱們將在 li 上中添加一個名爲 separator 的相應 CSS 類來表示它。
  5. 菜單項能夠被 禁用,在這種狀況下,咱們將添加相應的 disabled CSS 類。它的做用是使此項沒法響應鼠標事件,如懸停或點擊。
  6. 咱們將把全部東西包裝在一個 HTML 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>
複製代碼

在 CSS 中添加行爲

我撒了謊。咱們將使用 SCSS 代替。瀏覽器

不開玩笑了,有趣的部分來了!

默認狀況下應該 隱藏 菜單(第一級 導航菜單條 除外)。

只有在使用鼠標指針懸停相應的菜單項時,才應顯示第一級下的任何內容。你可能已經猜到了,爲了這個咱們將嚴重依賴 CSS 的 hover僞類

排列菜單和子菜單元素

理解咱們如何使子菜單位置的正確並將其自身與父菜單項對齊也許是整個謎題中最棘手的一點。這就是 CSS 定位的一些知識來源。讓咱們看看這個。

咱們之因此選擇將子菜單 ul 元素放在「父」 li 元素中是有緣由的。固然,它有助於咱們在邏輯上適當地將分層內容的標記組合在一塊兒。它還有另外一個目的,即容許咱們輕鬆編寫一些 CSS 來相對於父元素的位置定位子元素。而後咱們將這個概念一直延伸到根元素 ulli

爲此,咱們將使用 absolute 定位和 top 的組合,left CSS 屬性將幫助咱們相對於其最近的非靜態定位祖先(closest non-static positioned ancestor) 定位子元素定義包含塊。非靜態(non-static)的意思是元素的 CSS position 屬性不是 static(這默認發生在 HTML 文檔流中),但它是 relativeabsolutefixed 或者 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,這只是我作的選擇。你也可使用其餘方法實現相似的行爲,例如在 ulli 項上組合 display: blockdisplay: inline-block

UI 美化

一旦咱們完成了對菜單項定位的基本操做,咱們將繼續編寫一些額外的樣式,如字體、大小、顏色、背景和陰影等,以使 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;
複製代碼

咱們還剩下一些部分要添加合適的樣式和特性。咱們如今將會快速地把它們過一遍。

Anchors、Labels 和 Shortcuts —— 真正的視覺元素
.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 連接到咱們的多層次彈出式導航菜單的行動!

示例:僅限於CSS的多級嵌套彈出式導航菜單

更漂亮的主題

若是你不喜歡復古 Windows 的外觀,這是同一代碼的另外一個版本,對 CSS 進行了一些細微的調整,使其看起來和感受更像 MacOS。

示例:僅限於 CSS 的多級嵌套彈出式導航菜單(相似於 MacOS)

什麼無論用?

有一些事情咱們尚未處理。首先,

  • 若是你對此很是挑剔的話,雖然大多數效果都很好,但刻意只使用 CSS 的方法有侷限性,與現實世界的 Windows 和 MacOS 應用程序菜單不一樣,咱們的菜單會在鼠標移出外部時當即隱藏。爲了使用起來更方便,一般咱們想要作的是在點擊以後再隱藏(老是能夠用一點 JS 來實現)。
  • 若是菜單中的項目列表太長怎麼辦?以書籤列表爲例。在某些狀況下,可能須要將其限制在可滾動視圖中,例如按視口高度的某個百分比表示。歸根結底,它取決你正在構建的用戶體驗,但我也想把這些講清楚。

但願這是有用的。乾杯!

若是發現譯文存在錯誤或其餘須要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可得到相應獎勵積分。文章開頭的 本文永久連接 即爲本文在 GitHub 上的 MarkDown 連接。


掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章
相關標籤/搜索