[译] 仅使用 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前端后端区块链产品设计人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划官方微博知乎专栏

相关文章
相关标签/搜索