标签是一种简单的设计模式,其中一行链接显然是可点击的导航,当点击一个链接时,会显示新的内容。当然,有很多变体,但它是那里最普遍的导航设计模式之一。当以水平行排列时,它也是那里最不适合小屏幕的设计模式之一。
不过,我们可以让它工作。
在不采取任何措施帮助小屏幕上的标签的情况下,您会遇到某种问题
- 您让页面“缩小”,标签变成了微小的点击目标
- 您让页面宽度等于设备宽度,然后…
- 标签没有足够的空间,因此被截断了。
- 标签换行,看起来很奇怪,并占用太多空间。

基本上:我们需要在这里做一些事情来使标签更适合小屏幕。
之前已经做过
流行的解决方案包括
- Brad Frost 的 切换导航
- “画布外”导航选项 例如这样。
- 将标签转换为下拉菜单.
我想用一些特定的目标再次实现它。
目标
这是计划
- 当有空间时显示正常的“标签”,当没有空间时显示下拉菜单。
- 始终显示并突出显示“当前”标签。
- 适用于所有内容都在页面上且内容面板隐藏/显示的 #hash 标签。
- 适用于链接标签,其中标签链接到不同的 URL。
- HTML 语义化。
- HTML 版本不变。
- JavaScript 版本不变。
- 您可以链接到特定的标签。
因此,我们不仅要解决设计问题,还要解决功能问题。
像“画布外”样式这样的模式在这里不起作用,因为我们试图显示当前标签,而不是隐藏它。转换为 <select>
下拉菜单不起作用,因为那是不同的 HTML 和不同的 JavaScript。
HTML
标签是导航,因此使用 <nav>
。role
应该由标签隐含,但您不能总是依赖它,因此我们添加了 role
。我们在最外层元素(<nav>
)上使用一个类用于 CSS。导航项本身位于列表中 因为这是最好的。每个链接都有一个 #hash 目标或一个有效的 URL。
<nav role='navigation' class="transformer-tabs">
<ul>
<li><a href="#tab-1">Important Tab</a></li>
<li><a href="#tab-2" class="active">Smurfvision</a></li>
<li><a href="#tab-3">Monster Truck Rally</a></li>
<li><a href="http://google.com">Go To Google →</a></li>
</ul>
</nav>
没有多余的东西。
标签视图 CSS
每个“状态”(标签或下拉菜单)的标签有一组特定的 CSS。您可以通过先设置下拉菜单的样式,然后使用最小宽度媒体查询重新排列以使其看起来像标签,或者通过先设置标签的样式,然后使用最大宽度媒体查询重新排列为下拉菜单,来“优先考虑移动设备”;或者您可以“优先考虑桌面设备”。它们差异很大,我个人觉得没有太大优势,但我建议您与网站其他地方的设计保持一致。
本演示使用桌面优先和 SCSS。
.transformer-tabs {
ul {
list-style: none;
padding: 0;
margin: 0;
border-bottom: 3px solid white;
}
li {
display: inline-block;
padding: 0;
vertical-align: bottom;
}
a {
display: inline-block;
color: white;
text-decoration: none;
padding: 0.5rem;
&.active {
border-bottom: 3px solid black;
position: relative;
bottom: -3px;
}
}
}
只是一行链接,下方有一条线。带有“active”类的锚链接会获得不同的颜色边框,该边框与边缘到边缘的边框重叠。vertical-align
使所有链接在同一基线上对齐,当活动链接获得边框而其他链接没有时。
下拉视图 CSS
我们需要找到一个视口宽度,在该宽度下标签的外观会失效,并在那里放置一个媒体查询。本演示使用 700px。
.transformer-tabs {
...
@media (max-width: 700px) {
ul {
border-bottom: 0;
overflow: hidden;
position: relative;
background: linear-gradient(#666, #222);
&::after {
content: "☰"; /* "Three Line Menu Navicon" shows up */
position: absolute;
top: 8px;
right: 15px;
z-index: 2;
pointer-events: none;
}
}
li {
display: block; /* One link per "row" */
}
a {
position: absolute; /* Stack links on top of each other */
top: 0;
left: 0;
width: 100%;
height: 100%;
&.active {
border: 0;
z-index: 1; /* Active tab is on top */
background: linear-gradient(#666, #222);
}
}
}
}
当媒体查询生效时,会得到以下结果

活动标签仍然很明显(它是唯一显示的标签),并且显示了一个 三线菜单导航图标,它正逐渐成为显示更多导航的通用符号。
JavaScript 结构
我们需要的功能
- 如果它是 #hash 链接,则选择它会显示具有该 ID 的面板并隐藏当前显示的面板。
- 更改 URL 以指示该 #hash,但不影响历史记录(没有恼人的后退按钮)。
- 直观地指示现在处于活动状态的标签(适当地切换类)。
- 如果是链接标签,则允许该链接工作。
- 如果页面加载时带有与标签匹配的哈希值,则转到该标签。
- 在小屏幕的下拉状态下,允许在选择下拉菜单时将其打开/关闭。
基于这些需求的一些结构
var Tabs = {
init: function() {
this.bindUIfunctions();
this.pageLoadCorrectTab();
},
bindUIfunctions: function() {
},
changeTab: function(hash) {
},
pageLoadCorrectTab: function() {
},
toggleMobileMenu: function(event, el) {
}
}
Tabs.init();
选择时更改标签
我们唯一需要在选择时更改标签的时间是当所选标签是 #hash 链接时。否则,它是一个链接标签,应该只遵循该链接。(并且“选择”是指单击或点击或使用 Tab 键选择并激活或任何操作。)因此,我们的 changeTab 函数可以只接受该哈希值并使用它。
changeTab: function(hash) {
// find the link based on that hash
var anchor = $("[href=" + hash + "]");
// find the related content panel
var div = $(hash);
// activate correct anchor (visually)
anchor.addClass("active").parent().siblings().find("a").removeClass("active");
// activate correct div (visually)
div.addClass("active").siblings().removeClass("active");
// update URL, no history addition
window.history.replaceState("", "", hash);
// Close menu, in case in dropdown state
anchor.closest("ul").removeClass("open");
},
处理标签点击
我们将在这里使用标准事件委托以提高效率。任何“点击”(对点击操作也适用),假设它不是已经激活的标签,并且它是一个 #hash 链接,都只会将该哈希值传递给 changeTab 函数。
// Delegation
$(document)
.on("click", ".transformer-tabs a[href^='#']:not('.active')", function(event) {
Tabs.changeTab(this.hash);
event.preventDefault();
})
… 以及 preventDefault()
,以防止页面尴尬地跳动。
切换下拉菜单
如果媒体查询生效,并且标签处于其下拉状态,则当点击 .active
标签时,我们可以切换下拉菜单的“打开”和“关闭”。由于我们的样式,我们知道活动标签覆盖了整个可点击区域。
$(document)
// ... first click handler, chaining for efficiency
.on("click", ".transformer-tabs a.active", function(event) {
Tabs.toggleMobileMenu(event, this);
event.preventDefault();
});
toggleMobileMenu 函数非常简单。但我仍然喜欢我们将其抽象成自己的函数,以防有一天它需要做更多的事情,我们不会让代码变得杂乱无章。
toggleMobileMenu: function(event, el) {
$(el).closest("ul").toggleClass("open");
}
.open
类通过一些 CSS 更改直观地打开菜单。
.transformer-tabs {
...
@media (max-width: 700px) {
ul {
...
&.open {
a {
position: relative;
display: block;
}
}
}
}
...
}
删除这些标签上的绝对定位并将它们设为块级元素,使菜单向下扩展并将下面的内容也向下推。这就是使其成为下拉菜单的原因。

页面加载时使用 #hash 加载正确的标签
事实证明这非常容易。只需查看 URL 中的哈希值并将其传递给 changeTab 函数即可。
pageLoadCorrectTab: function() {
this.changeTab(document.location.hash);
},
这就是为什么我们将功能抽象成我们可以重复使用的函数,而不是让代码变得杂乱无章。
这不仅仅是理论
我在修复了CodePen 上的标签之后写下这篇文章,之前这些标签有点糟糕,直到我做了这个改进。CodePen 上既有 #hash 链接标签,也有真正的链接标签,因此需要满足这些需求。
想改进它吗?
也许一个菜单不会向下推挤下方内容的版本会很酷,展开的下拉菜单只需置于内容之上即可。也许一个可以处理大量链接(超过小屏幕所能容纳的)的版本,并带有一些滚动或分页功能。也许一个带有一些动画/过渡效果的版本。
我们在此构建的演示的一个问题是,除非你选择一个标签,否则无法关闭下拉菜单。你可以选择当前标签来关闭它而无需执行任何操作,但你不能只点击三线菜单来关闭它。这是因为你无法(据我所知)将点击事件绑定到伪元素上。在 CodePen 上,我只是为三线菜单使用了<span>
,以便你可以这样切换它,但这确实意味着需要额外的标记。
演示
注意:“转到 Google →” 是一个链接标签,仅用于测试。它在 CodePen 上由于沙盒 iframe 的原因而无法工作。在正常情况下,它可以工作。
查看 Chris Coyier 在 CodePen 上的笔 变形标签 (@chriscoyier)
上周我不得不处理这个确切的问题。我发现了一些其他人之前尝试解决此问题的示例。
http://foundation.zurb.com/docs/components/section.html
http://jquerytools.org/demos/tabs/accordion.html
对于小屏幕上的标签,我更喜欢上面的手风琴方法。值得注意的是,jQuery Mobile 使用了手风琴的概念,并且做得非常好。
我喜欢 Chris 所做的,这令人印象深刻,但其中一些感觉有点笨拙:我必须按下以显示导航,然后按下我的标签,然后不知何故我的新标签最终出现在顶部……只是有点出乎意料,找不到更好的词来形容。
如果你还没有,请查看 jQuery Mobile 的解决方案。
http://jquerymobile.com/demos/1.3.0-rc.1/docs/content/content-collapsible-set.html
(滚动到页面底部并查看最后的示例)
手风琴方法得到了很多喜爱。我并不反对它,但我认为它高度依赖于具体情况。在 CodePen 的情况下,如果我要采用手风琴样式,它绝对需要一开始就处于“全部关闭”状态,这样你至少可以一瞥所有可用的选项。我不赞成将导航推到页面底部的做法,因为你需要向下滑动才能发现它。然后在全部关闭的情况下,你根本没有显示任何内容,这肯定不适用于 CodePen 首页等情况。所以……我有点喜欢下拉菜单方法。
如果每个标签的内容都很少,我可以看到手风琴方法有效。
如果用户必须滚动才能找到菜单,则在标签的顶部/底部添加一个链接,将用户带到其他菜单项,可能就足够了。
将更好的转换标签(桌面)转换为手风琴(移动)……因为隐藏它不太直观。
不知道可以通过这种方式实现三线导航图标,谢谢!
另外,抱歉挑剔,但在你的第二个 SCSS 代码段中,你忘记关闭注释了,并且在这不仅仅是理论下写了“[…] CodePen,其中的有点糟糕”。
感谢你的发现!
这是我如何做到的
它并不完美,但它有点用。标签越多,它们堆叠在一起就越笨拙。只有 6 个或更少的标签,我觉得它工作得还可以。
我喜欢下拉菜单的想法。几个月前,我为我的团队构建了一个不同的版本。我的目标是在大屏幕上使用标签,在小屏幕上使用手风琴:。
下班回家后,我需要仔细检查一下你的代码。
太棒了!你是第一个不额外放置导航的人。我喜欢 Dirk Ginader 的“可访问标签”方法,其中导航是使用 JS 从标题构建的。在我看来,其他所有脚本都做错了。
大家对 Metro……嗯……Modern UI 方法有什么看法?从本质上讲,这种方法显示了可以水平放置多少个标签,然后将省略号作为最后一个标签,该标签是包含剩余无法容纳的标签选项的下拉菜单。
我首先承认,这似乎需要大量的理论 JavaScript 工作,但我的日常工作从全面的 Web 开发变成了 SharePoint 布道者,因此指出此选项似乎是我的职责。
嗨,Chris,
你认为将菜单放置在页面底部,并从顶部提供一个指向它的链接的方法怎么样?
通常情况下,我会使用下拉菜单,但我的一个客户想要一个响应式设计的网站,但需要一个始终展开的菜单。
谢谢!
嗨,Chris,
我喜欢你的方法。但我想我主要会将标签更改为手风琴。它是一个已知的 UI,并且比你的方法需要更少的点击次数。
但每次查看标签脚本时,让我感到困惑的一点是每个人都在构建导航。在我看来,最好让 JS 构建导航,因为这些标签只能与 JS 一起使用。我喜欢 Dirk Ginader 的“可访问标签”方法,其中导航是使用 JS 从标题构建的。据我所知,此脚本在此细节上是独一无二的。我不明白为什么。
我在此评论中写了我对风琴的看法。
在这种情况下,如果你要通过 Javascript 构建导航,最好的(我敢说语义化的?)标记将是
dl
,每个dt
都是一个导航项,dt
是标签内容。如果没有 Javascript 或样式,内容仍然以可读的方式呈现,并与充当标题的dt
逻辑地组合在一起。你可以轻松地添加手风琴样式,并使用 JS 为标签或下拉布局构建导航,并且只需使用一个选项或一个类在布局之间切换。我过去也做过类似的事情,但没有 Chris 的代码那么干净。可能值得重新审视。
非常感谢!这个我会添加到我的书签列表中。我想我很快就会需要它。
只有我看到了吗?http://puu.sh/5almw.png
我在 Safari 6.0.5 中也看到了这个问题。除此之外,Chris 的工作非常棒!
没有渐变的回退。我为演示添加了一个。
嗨,Chris,
我非常喜欢这种方法。我认为它对于移动网站来说是一个很棒的解决方案。我在一个正在开发的网站上使用了这段代码。我将其用于一个包含大量不同信息的商品详情页面。你不需要“所有选项卡都关闭”的状态,因为你希望尽快向用户提供信息。
不过,你的解决方案存在一个问题。当你在一个网站上使用多个选项卡框时,选项卡控件只会触发第一个选项卡框的内容div。我 fork 了你的笔并在 js 部分进行了编辑 http://codepen.io/maxkarkowski/pen/wJmcH
如果我打开选项卡并想要关闭它,我“被迫”转到“重要选项卡”。
或者选择您当前所在的选项卡。
为了解决这个问题,你需要一个额外的元素(可能是三线图标),并将菜单的切换绑定到该元素上。这是一个可以接受的折衷方案。
我想看看如何使 https://jqueryui.jqueryjs.cn/tabs/ 响应式的示例。
尤其是在它们被广泛使用的情况下。
我不得不处理一个类似的问题,我需要在较小的屏幕上切换界面。当时我找不到好的解决方案,所以我提出了自己的解决方案。
https://github.com/dmmendez/content-switcher/
此解决方案能够在选项卡/下拉菜单/手风琴之间切换,只需使用简单的部分标记即可。当然,我不是手风琴的忠实粉丝,但如果需要,它就在那里。
我的方法更多地使用的是分段方法,而不是本文中显示的导航方法。当时,我选择分段方法是因为我需要将其转换为手风琴而不是下拉菜单,并且分段给了我更大的灵活性来做到这一点。
感谢这篇文章。
关于 Ainos 的评论
我使用了与 Chris 建议不同的方法,我不得不说它效果很好。
你可以在 Codepen 上查看它。
在一个 最近的项目 中,我们在小屏幕上将选项卡按钮做得又大又堆叠,添加了一个向下箭头图标,并在点击时滚动到选项卡内容。到目前为止,用户似乎反应良好。
注释1:网站是挪威语的,可以像阅读乱数文字一样阅读它吗?
注释2:点击滚动在桌面端很烦人,但谁会在桌面端 <768px 的情况下浏览呢?
注释3:网站使用 Weebly,因此标记远非完美。
我更喜欢用纯 CSS 来做。如果你在页脚中有另一个顶部导航的副本,这很容易做到,这种情况经常发生。
在较小的屏幕上查看时,使用媒体查询隐藏你的顶部导航并替换为指向页脚菜单的超链接(使用下拉菜单图标)。为页脚菜单设置样式,使其看起来像一个美观的移动友好菜单。
在我的当前工作网站上查看它的实际效果。 http://net-evidence.com
我习惯使用 CSS,但这绝对是我今晚要尝试的东西,我正在为我的下一个项目寻找一些不同的东西。谢谢。
我正在结合 Foundation 使用它,除了 MobileToggle 之外,其他一切正常。有什么建议吗?