响应式菜单概念

Avatar of Tim Pietrusky
Tim Pietrusky

DigitalOcean 为您旅程的每个阶段提供云产品。 立即开始使用 $200 免费积分!

以下文章由 Tim Pietrusky 撰写。 我认识 Tim,因为他 在 CodePen 上 做了很多工作,并且他也是那里的一个乐于助人的社区成员。 他写信给我,向我展示了这篇关于响应式菜单的客座文章,我很乐意与您分享。 这不仅是一个及时的概念,而且其中一个概念改进了一种我们在过去在这里介绍过的巧妙的 CSS 技巧。

在响应式设计方面,我们面临着各种技术,这些技术可以帮助我们最佳地处理如何为小屏幕更改导航菜单。 资源似乎无穷无尽。 这就是我要向您展示四个主要概念以及讨论所有概念的优点和缺点的原因。

其中三个是用纯 CSS 制成的,一个使用一行 JavaScript。

开始之前

在本篇文章中提供的代码中,我没有使用任何供应商前缀,以使 CSS 更易于查看和理解。 更复杂的 CSS 示例使用 SCSS。 每个示例都托管在 CodePen 上,如果您愿意,您可以在那里看到编译后的 CSS。

本文中的所有菜单概念都基于这种简单的 HTML 结构,我称之为 **基本菜单**。 role 属性 用于指定特定概念(全水平、选择、自定义下拉菜单和画布外)。

<nav role="">
  <ul>
    <li><a href="#">Stream</a></li>
    <li><a href="#">Lab</a></li>
    <li><a href="#">Projects</a></li>
    <li><a href="#">About</a></li>
    <li><a href="#">Contact</a></li>
  </ul>
</nav>​

为了解决小屏幕问题,我在所有概念中都使用相同的 媒体查询

@media screen and (max-width: 44em) {

}

1. 全水平

这是一种最简单的方法,因为您只需要在小屏幕上使 列表元素 充满整个宽度。

<nav role="full-horizontal">
  <ul>
    <li><a href="#">Stream</a></li>
    <li><a href="#">Lab</a></li>
    <li><a href="#">Projects</a></li>
    <li><a href="#">About</a></li>
    <li><a href="#">Contact</a></li>
  </ul>
</nav>​
@media screen and (max-width: 44em) {
  nav[role="full-horizontal"] {
    ul > li {
      width: 100%;
    }
  }
}

这是它在小屏幕上使用自定义样式的样子。

full-horz

优点

  • 无需 JavaScript
  • 无需额外 HTML
  • 简单 CSS

缺点

  • 占用过多屏幕空间

演示

在 CodePen 上

2. 选择

这个概念在小屏幕上隐藏了基本菜单,并显示了一个 select 菜单代替。

为了实现这一点,我们需要扩展基本标记并添加一个 select。 为了使 select 工作,我们还添加了一些 JavaScript,它在 select 上的 onchange 事件发生时更改 window.location.href。

<nav role="select">
  <!-- basic menu goes here -->
  
  <select onchange="if (this.value) window.location.href = this.value;">
    <option value="#">Stream</option>
    <option value="#">Lab</option>
    <option value="#">Projects</option>
    <option value="#">About</option>
    <option value="#">Contact</option>
  </select>
</nav>​

我们在较大屏幕上隐藏 select。

nav[role="select"] {
  > select {
    display:none;  
  }
}

在小屏幕上,我们隐藏基本菜单并显示 select。 为了帮助用户识别这是一个菜单,我们还在添加一个带有“Menu”文本的 伪元素

@media screen and (max-width: 44em) {
  nav[role="select"] {
    ul {
      display: none;
    }

    select {
      display: block;
      width: 100%;
    }

    &:after {
      position: absolute;
      content: "Menu";
      right: 0;
      bottom: -1em;
    }
  }
}

这是它在小屏幕上使用自定义样式的样子。

num-2

优点

  • 不需要太多空间
  • 使用原生控件

缺点

  • 需要 JavaScript
  • 重复内容
  • 并非所有浏览器都支持 select 的样式设置

演示

在 CodePen 上

3. 自定义下拉菜单

这个概念在小屏幕上隐藏了基本菜单,并显示了一个 inputlabel(用于 Checkbox Hack)代替。 当用户点击标签时,基本菜单显示在其下方。

<nav role="custom-dropdown">
    <!-- Advanced Checkbox Hack (see description below) -->
    
    <!-- basic menu goes here -->
</nav>​

Checkbox Hack 的问题

默认 Checkbox Hack 存在两个问题

  1. 在移动 Safari(iOS < 6.0)上不起作用。 由于错误,在 iOS < 6.0 上无法点击标签以切换输入。 唯一 解决方法 是在标签中添加一个空 onclick。
  2. 在默认 Android 浏览器(Android <= 4.1.2)上不起作用。 曾经有一个 WebKit 相邻/通用兄弟 & 伪类 错误,它阻止了伪类与 相邻 (+) 或 通用 (~) 兄弟组合器一起使用。
h1 ~ p { color: black; }
h1:hover ~ p { color: red; }

这没有影响,因为 checkbox hack 使用了伪类 :checked 与通用兄弟一起使用。 由于此问题已在 WebKit 535.1(Chrome 13)中 修复,而 Android 4.1.2 上的实际 WebKit 是 534.30,因此正常的 checkbox hack 在迄今为止的任何 Android 设备上均不起作用。

最佳 解决方法 是在 body 元素上添加一个仅限 WebKit 的虚假动画。

所有这些组合创造了 **高级 Checkbox Hack**

<!-- Fix for iOS -->
<input type="checkbox" id="menu">
<label for="menu" onclick></label>
/* Fix for Android */
body { 
  -webkit-animation: bugfix infinite 1s; 
}
@-webkit-keyframes bugfix { 
  from { padding: 0; } 
  to { padding: 0; } 
}

/* default checkbox */
input[type=checkbox] {
  position: absolute;
  top: -9999px;
  left: -9999px;
}

label { 
  cursor: pointer;
  user-select: none;
}

参考:高级 Checkbox Hack

对于大屏幕,我们隐藏标签

nav[role="custom-dropdown"] {
  label {
    display: none;
  }
}

对于小屏幕,我们隐藏基本菜单并显示标签。 为了帮助用户识别这是一个菜单,我们还在标签中添加了一个带有“≡”文本(转换为“\2261”以用作伪元素上的内容)的伪元素。 当用户点击输入时,基本菜单显示,列表元素扩展到整个宽度。

@media screen and (max-width: 44em) {
  nav[role="custom-dropdown"] {
    ul {
      display: none;
      height: 100%;
    }

    label {
      position: relative;
      display: block;
      width: 100%;
    }

    label:after {
        position: absolute;
        content: "\2261";
    }
    
    input:checked ~ ul {
      display: block;
    
      > li {
        width: 100%;
      }        
    }
  }
}

这是菜单在小屏幕上使用自定义样式的样子。

closed
关闭
open
打开

优点

  • 关闭时不需要太多空间
  • 自定义样式
  • 无需 JavaScript

缺点

  • 语义不佳(输入/标签)
  • 额外 HTML

演示

在 CodePen 上

4. 画布外

这个概念在小屏幕上隐藏了基本菜单,并显示了一个 HTML 输入和标签(用于高级 Checkbox Hack,有关更多信息,请参阅 3. 自定义下拉菜单)代替。 当用户点击标签时,基本菜单从左侧飞入,内容移到右侧 - 屏幕被分成两部分:菜单约 80% 和内容约 20%(取决于分辨率和 css 单位)。

<input type="checkbox" id="menu">
<label for="menu" onclick></label>

<!-- basic menu goes here -->

<div class="content">
  <!-- content goes here -->
</div>

在较大屏幕上,我们隐藏标签。

label {
  position: absolute;
  left: 0;
  display: none;
}

在小屏幕上,我们将基本菜单隐藏在视窗之外并显示标签/输入。 为了隐藏菜单,我们指定一个宽度($menu_width)并为其添加一个负位置。 为了帮助用户识别这是一个菜单,我们还在标签中添加了一个带有“≡”文本(转换为“\2261”以用作伪元素上的内容)的伪元素。
当用户点击输入时,基本菜单从左侧飞入,内容移到右侧。

@media screen and (max-width: 44em) {
  $menu_width: 20em;

  body {
    overflow-x: hidden;
  }
    
  nav[role="off-canvas"] {
    position: absolute;
    left: -$menu_width;
    width: $menu_width;
    
    ul > li {
      width: 100%;
    }
  }

  label {
    display: block;
  }

  label:after {
    position: absolute;
    content: "\2261";
  }

  input:checked ~ nav[role="off-canvas"] {
    left: 0;
  }

  input:checked ~ .content {
    margin-left: $menu_width + .5em;
    margin-right: -($menu_width + .5em);
  }
}​

这是菜单在小屏幕上使用自定义样式的样子。

offcanvas-closed
关闭
offcanvas-open
打开

优点

  • 关闭时不需要太多空间
  • 自定义样式
  • 无需 JavaScript
  • 来自 Facebook/Google+ 应用程序的约定

缺点

  • 语义不佳(输入/标签)
  • 额外 HTML
  • 对 body 的绝对位置 = 感觉像是固定位置

演示

在 CodePen 上

它在 IE 上有效吗?

上面使用的所有技术都具有一个目标:为现代浏览器创建响应式菜单! 并且由于任何移动设备上都没有 IE 8 或更低版本,因此我们无需担心它。