精通 CSS(二)
原文:
zh.annas-archive.org/md5/6E7477B42C94A8805922EA40B81890C7译者:飞龙
第五章:创建主导航和下拉菜单
在本章中,我们将为网站的主要导航构建所有功能和展示。这一章非常深入,因为构建我们的主导航涉及伪类;静态、绝对、相对和固定定位;以及 CSS 动画。
开始导航
在本节中,我们将首先创建尽可能干净的 HTML,然后插入基本的 CSS 以启动它。以下是我们最终网站应该看起来的样子;这是我们的目标:
我们有一个典型的水平导航栏。其中一些项目有下拉菜单。我们还在导航栏的左侧有一个鲨鱼标志,它很好地悬挂在那里。
构建菜单的语义化 HTML
让我们立即输入我们需要的 HTML。我们将从这个漂亮的大 HTML 注释开始。您可能已经注意到,我喜欢这些大家伙。这是因为更容易快速定位到我需要的代码部分:
<!--
===============
Nav
===============
-->
我们将把所有内容包装在 HTML5 的nav元素中,并应用grouping类,因为我们将在其中浮动所有内容。最终将需要一个清除浮动,以防容器坍塌:
<!--
===============
Nav
===============
-->
<nav class="grouping">
</nav>
现在让我们添加一个figure元素,它将包裹我们的鲨鱼图像:
<nav class="grouping">
<figure>
<img src="img/sharky.png" alt="sharky">
</figure>
</nav>
接下来,我们将开始一个带有primary-nav类的无序列表。很久以前,人们确定使用列表进行导航非常语义化,因为它本质上是一个链接列表:
<nav class="grouping">
<figure>
<img src="img/sharky.png" alt="sharky">
</figure>
<ul class="primary-nav">
</ul>
</nav>
我们将从四个列表项开始。我们将在每个列表项中放置一个锚点标记:
<nav class="grouping">
<figure>
<img src="img/sharky.png" alt="sharky">
</figure>
<ul class="primary-nav">
<li><a href="#"></a></li>
<li><a href="#"></a></li>
<li><a href="#"></a></li>
<li><a href="#"></a></li>
</ul>
</nav>
当我们将其应用到我们的网站时,我们将得到一个鲨鱼图像和四个链接,全部垂直堆叠:
我们需要将这四个链接水平排列,就像块一样。我们将使用浮动和其他一些属性来实现这一点。
使用 CSS 来设计导航
在我们的 CSS 中,首先我们将找到导航的大块注释:
/****************
nav
****************/
然后我们将定位.primary-nav类。让我们使用一种特殊类型的后代选择器,它只定位列表项的第一级:
.primary-nav > li
这很重要。我们这样做是因为稍后我们将在这些列表项中嵌套另一个无序列表以获得一个下拉菜单。假设我们创建相同的选择器,但没有大于号符号:
.primary-nav li
这将定位primary-nav内的任何和所有li标签--子代、孙代、曾孙代等。如果您只想定位直接子代,请使用此选择器;它被称为子组合器:
.primary-nav > li
元素之间的大于号确保我们只定位直接子代。让我们也将这些列表项浮动到左侧,然后刷新浏览器:
.primary-nav > li {
float: left;
}
以下是前面代码的输出:
这是一个开始;还有很多工作要做。
让我们使用相同类型的子组合器来仅定位.primary-nav的直接子项中的直接子项锚点:
.primary-nav > li {
float: left;
}
.primary-nav > li > a {
}
所以我们将在顶部添加25px的填充,左右各0。我们还会添加一个宽度;每个宽度将为150px,并且我们会给每个添加一个 1 像素实线的border-left并将它们颜色设置为灰色:
.primary-nav > li > a {
padding: 25px 0;
width: 150px;
border-left: 1px solid #ada791;
}
我们看到它现在开始松散地类似于我们最终的导航:
我们现在将把整个规则集放在我们的 CSS 中,放在主要导航选择器下面:
.primary-nav > li > a {
padding: 25px 0;
width: 150px;
border-left: 1px solid #ada791;
}
nav li a{
font-family: Arial, Helvetica, sans-serif;
color: #766e65;
text-transform: uppercase;
font-size: 15px;
text-align: center;
-webkit-transition: 0.15s background-color linear;
transition: 0.15s background-color linear;
}
这是一个更熟悉的后代选择器,适用于我们将应用于主导航项目以及下拉导航项目的一些样式。这是一种很好的DRY(不要重复自己)方法,这样我们就不必以后为下拉菜单重新编写这段代码。让我们更仔细地检查这个规则集。基本上,我们将font-family设置为Arial:
font-family: Arial, Helvetica, sans-serif;
我们有这个文本颜色:
color: #766e65;
我们使用了text-transform: uppercase。这将确保我们可以在 HTML 中为导航项输入小写字母,并将每个字母转换为大写字符。这样,如果我们以后决定普通情况比全部大写更好,那么我们只需要在一个地方进行更改,而不是更新整个 HTML:
text-transform: uppercase;
接下来,我们有一个字体大小:
font-size: 15px;
我们还将文本对齐到中心:
text-align: center;
我们也添加了一个过渡,就像在上一章讨论的那样。这是为了过渡背景颜色:
-webkit-transition: 0.15s background-color linear;
transition: 0.15s background-color linear;
这是我们保存更改并刷新浏览器时得到的结果:
我们有一些问题。一个问题是我们的锚点标签是内联元素,所以问题是它们实际上并不像块级元素一样行为。因此,我们可以做的一件事是将它们也浮动到左侧。为此,在.primary-nav > li > a规则集中添加float: left属性:
.primary-nav > li > a {
float: left;
padding: 25px 0;
width: 150px;
border-left: 1px solid #ada791;
}
以下是上述代码的输出:
这看起来好多了。
现在让我们来定位焦点和悬停状态。在我们的最后一个规则集下面,我们将添加另一个规则集:
nav li a:focus,
nav li a:hover,
nav li a.active {
background-color: #eb2428;
color: #fff;
}
这将不仅针对焦点和悬停状态,还将针对active类。这不是一个“状态”,就像焦点或悬停状态一样。这是一个我们将应用于元素的类,表示您正在访问该页面。它将与悬停状态相同。接下来,我们将背景颜色设置为红色,文本颜色设置为白色。现在,当我们刷新时,我们得到了悬停和焦点状态,这很好:
我们现在唯一需要做的就是弄清楚导航栏的位置,并将整个东西推到右边,因为现在它正好位于我们的图像下方。所以让我们将整个导航栏浮动到右侧。让我们这样做:
.primary-nav {
float: right;
}
以下是上述代码的输出:
正如您所看到的,这效果相当不错。整个导航都位于鲨鱼下方。我们可以通过将鲨鱼浮动到左侧来解决这个问题,但是如果我们使用绝对定位,我们还可以实现一些不错的功能,这是我们稍后在本章中将要介绍的。
最后,让我们通过添加白色背景并将我们的图像限制为160px的宽度来稍微整理一下这个导航。
/****************
nav
****************/
nav {
background-color: #fff;
}
nav img {
width: 160px;
}
这是没有白色背景的网站,我们的鲨鱼相当大:
当我们刷新网站时,我们将得到我们想要的白色背景和一个较小的鲨鱼:
好了,我们已经为导航的第一层构建了 HTML 和大部分 CSS。接下来,您将学习如何使用伪类来解决导航中的某些问题。
使用伪类
您已经学会了如何向元素添加类以应用特殊样式。您总是需要进入 HTML 添加类。有时这可能会成为一个问题。例如,当内容通过内容管理系统动态生成时,您可能无法编辑任何元素,因为它可能不存在于静态 HTML 文件中。这时就需要使用伪类。伪类允许您根据元素在 HTML 中的位置和其他特性来定位元素。在本节中,我们将看一下first-child伪类,它可以帮助我们样式化我们的导航。然后我们将看一下其他几个伪类,例如last-child和nth-child。
最后,我不希望主菜单,也就是第一个菜单,有border-left,因为它是第一个元素。所以,我想摆脱它:
第一个子元素
为了在我们的 CSS 中定位第一个元素,我们将在锚点元素后添加first-child。所以我们将复制这个选择器,并将其粘贴在自身下面:
.primary-nav > li > a {
float: left;
padding: 25px 0;
width: 150px;
border-left: 1px solid #ada791;
}
然后我们将:first-child添加到选择器中,删除属性,并添加border-left,值设置为none:
.primary-nav > li > a:first-child {
border-left: none;
}
保存这个,转到网站,然后刷新页面:
结果并不是我们可能期望的。实际上,我们从导航中删除了每个项目的左边框。这是因为首先,所有锚点都是它们直接父元素li的子元素。所以我们实际上应该以不同的方式处理这个问题。
快速查看我们 HTML 中的nav。锚点是li内的第一个元素;没有第二个元素。所以,如果我们想要定位ul内的第一个元素,它不会是锚点,而是列表项,即<li>:
<nav class="grouping">
<figure>
<img src="img/sharky.png" alt="sharky">
</figure>
<ul class="primary-nav">
<li><a href="#">Home</a></li>
<li><a href="#">Movies</a></li>
<li><a href="#">Species</a></li>
<li><a href="#">Chum</a></li>
</ul>
</nav>
在我们的 CSS 中,我们实际上要将伪类从这里移动:
.primary-nav > li > a:first-child {
我们将它从a中移除,并将它附加到li,如下所示:
.primary-nav > li:first-child > a {
现在我们所有的导航元素上都有border-left属性,除了第一个:
关于first-child的一件事是,它必须是出现在父元素内的第一个元素。所以即使我们特别将li作为主导航的第一个子元素,如果我们在第一个li标签之前在ul内有其他东西,那么我们的选择器就不起作用了。让我们看看这个。让我们在ul元素的子元素中添加一个h2:
<nav class="grouping">
<figure>
<img src="img/sharky.png" alt="sharky">
</figure>
<ul class="primary-nav">
<h2>not valid html</h2>
<li><a href="#">Home</a></li>
<li><a href="#">Movies</a></li>
<li><a href="#">Species</a></li>
<li><a href="#">Chum</a></li>
</ul>
</nav>
这不是有效的 HTML,但是为了好玩,注意我们在第一个li标签上重新获得了左侧边框:
这是因为它不再是第一个孩子。h2现在是第一个孩子。这是在使用first-child伪类时的一个常见错误。
最后一个孩子
现在让我们看看last-child伪类。让我们创建一个新的选择器:
.primary-nav > li:last-child > a {
}
我们将通过将背景颜色设置为亮粉色,文本颜色设置为白色,使示例更加明显:
.primary-nav > li:last-child > a {
background-color: deeppink
color: #fff;
}
现在我们的最后一个孩子也应用了这些属性:
我更喜欢first-child,因为它在 IE7 及更早版本中有更深的支持,而last-child的支持从 IE9 开始。
nth-child 伪类
nth-child类允许我们选择其父元素内的任何元素。让我们进入 CSS 并将last-child更改为nth-child(2):
.primary-nav > li:nth-child(2) > a {
background-color: deeppink;
color: #fff;
}
保存代码并刷新网站:
所以,在我们的网站上,粉色实际上应该应用于h2和 HOME,因为h2是ul内的第一个元素,而 HOME 是第二个。
如果你是一个 JavaScript 人,nth-child不是从零开始的,所以第一个不是零:第一个是一。
考虑到这一点,让我们将nth-child设置为1:这本质上与使用first-child相同:
.primary-nav > li:nth-child(1) > a {
background-color: deeppink;
color: #fff;
}
让我们快速从我们的 HTML 中去掉这个h2标签:
<nav class="grouping">
<figure>
<img src="img/sharky.png" alt="sharky">
</figure>
<ul class="primary-nav">
<!--<h2>not valid html</h2>-->
<li><a href="#">Home</a></li>
<li><a href="#">Movies</a></li>
<li><a href="#">Species</a></li>
<li><a href="#">Chum</a></li>
</ul>
</nav>
我们现在看到粉色保留在第一个导航项上:
您还可以使用关键字odd和even。所以如果我在那里加入even或odd,你会得到应用这些属性的数字二和四:
.primary-nav > li:nth-child(even) > a {
background-color: deeppink;
color: #fff;
}
刷新网站,你会得到以下结果:
这是一个很好的技术,可以为表格或列表添加斑马条纹,以增加可读性。
nth-of-type 伪类
还有nth-of-type。在我们的 CSS 中,将nth-of-type(2)添加到primary-nav选择器中:
.primary-nav > li:nth-of-type(2) > a {
background-color: deeppink;
color: #fff;
}
nth-of-type和nth-child之间的区别在于,nth-of-type预先限定只查找它附加的元素。例如,在我们的例子中,我们已经将nth-of-type附加到了一个li,所以它只匹配li标签:
.primary-nav > li:nth-of-type(2) > a {
background-color: deeppink;
color: #fff;
}
让我们看看这个实例。让我们重新添加我们的h2标签:
<nav class="grouping">
<figure>
<img src="img/sharky.png" alt="sharky">
</figure>
<ul class="primary-nav">
<h2>not valid html</h2>
<li><a href="#">Home</a></li>
<li><a href="#">Movies</a></li>
<li><a href="#">Species</a></li>
<li><a href="#">Chum</a></li>
</ul>
</nav>
它不会只是指ul内的任何孩子。我们现在看到第二个li标签应用了这些属性:
所以nth-of-type比nth-child更精确。nth-child和nth-of-type的浏览器支持从 IE9 和更高版本开始,以及其他主要浏览器。
在这一部分,我们学习了一些伪类,可以根据它们在 HTML 中的顺序来定位元素。然而,这些并不是我们迄今为止使用的第一个伪类。我主要指的是基于状态的伪类,比如hover和focus,这些我们迄今为止已经使用了很多。在下一节中,我们将转变方向,讨论 CSS 定位以进一步提升我们的导航。
绝对定位
在这一部分,我们将开始研究不同的 CSS 定位属性,以及它们的补充偏移属性。首先,我们将绝对定位鲨鱼标志,然后使用固定定位来处理整个导航栏。
绝对定位鲨鱼
我们已经把菜单放好了,但是鲨鱼明显是在导航栏的上面。我们需要它水平对齐,或多或少。我们需要修复鲨鱼,使其悬挂在导航栏上方。我们还希望整个导航栏保持固定在浏览器窗口的顶部:
所以让我们去我们的 CSS 中,给nav figure选择器添加position: absolute。在nav规则集下面创建一个新的选择器。我们将其称为nav figure,并给它一个position属性,值为absolute:
/****************
nav
****************/
nav {
background-color: #fff;
}
nav figure{
position: absolute;
}
nav img {
width: 160px;
}
立刻看起来好多了:
让我们谈谈我们刚刚做的事情。所有元素,默认情况下都是static定位。静态元素遵循正常流,这意味着块级元素只是简单地堆叠在一起,只要它们没有浮动。将position改为absolute会将其从正常流中移出。它的块级特性消失了,其他元素对它没有任何影响。它可以被视为存在于另一个平面或层次上。一旦绝对定位,你就可以开始使用偏移属性,比如top、right、bottom和left。
让我们这样做。给nav figure元素添加两个属性,即top和left:
nav figure{
position: absolute;
top: -50px;
left: 50px;
}
这些将会像margin-top和margin-left一样起作用。如果你查看结果,你应该会看到鲨鱼距离左边有50px,距离顶部有-50px:
那么当我们将top属性与bottom交换,将left属性与right交换时会发生什么:
nav figure{
position: absolute;
bottom: : -50px;
right: 50px;
}
它实际上将鲨鱼移动到了页面的底部和右侧!
这张图片更清楚地展示了偏移属性与绝对定位的结合工作原理。偏移属性现在是基于浏览器视口的,但通常我们不想这样做;相反,我们希望通过将父元素设置为position: relative来基于父元素定位。
图片的父元素是nav选择器,所以让我们将其设置为相对定位:
nav {
background-color: #fff;
position: relative;
}
nav figure{
position: absolute;
bottom: -50px;
right: 50px;
}
你可以看到,即使现在我们距离右边有50px,因为导航栏一直延伸到右边缘,而我们距离导航栏底部有-50px,因为鲨鱼在那里延伸到导航栏下方:
position: relative声明为子元素建立了一个坐标系,具有position: absolute的子元素。
让我们把鲨鱼移回它应该在的位置:
nav figure{
position: absolute;
top: -20px;
left: 50px;
}
鲨鱼很好地重叠在我们的导航栏上。它现在坐在我们的标题上面,有点好笑,但我们马上就会回到这个问题:
首先,通过添加position: fixed来使整个导航栏固定在顶部。
使用固定定位来处理导航栏
让我们将导航栏的position属性从absolute改为fixed,看看结果如何:
nav {
background-color: #fff;
position: fixed;
}
以下是上述代码的输出:
fixed值,例如relative,仍然像坐标系一样,用于任何绝对定位的子元素或后代元素,但它也有一些超能力。现在,这些超能力完全破坏了我们的导航。问题在于:position: relative仍然保留其块元素的特性,而position: fixed在从正常流中移除时失去了许多这些块特性,导航现在看起来有点滑稽:它没有延伸到浏览器窗口的全宽。让我们通过一些偏移属性来修复这个问题。
我们实际上可以通过设置 left: 0 和 right: 0 来拉伸导航。让我们还添加 top: 0 来确保它被定位在顶部:
nav {
background-color: #fff;
position: fixed;
left: 0;
right: 0;
top: 0;
}
看起来更好。而且,因为导航的位置设置为固定,当我们滚动页面时,导航内的所有内容都固定在顶部,其他所有内容都在其下面移动:
但是,如果您滚动到顶部,您会发现网站标题现在位于导航栏后面。这是因为导航不再是正常流的一部分:
让我们通过向intro-content和我们的go-premium按钮添加margin-top来修复这个问题。我们将转到我们的go-premium规则集,并将margin-top的值添加为150px:
.go-premium {
width: 300px;
float: left;
margin-top: 150px;
}
我们还将转到我们的intro-content规则集,并添加margin-top为125px:
.intro-content {
width: 600px;
margin-right: 60px;
float: left;
margin-top: 125px;
}
现在看起来非常好:
因此,您已经了解了相对、绝对和固定定位。您还了解到每个元素的默认位置是静态的。relative位置为子元素创建了一个坐标系。absolute位置允许您将元素移动到自己的宇宙中,并根据最近的相对定位的父元素进行积极定位。fixed位置将使元素基于浏览器的视口而粘性,而不是基于任何相对定位的元素。absolute和fixed元素都将作为坐标系,相对于其他子元素。在下一节中,我们将看看如何构建下拉菜单,其中我们将再次使用绝对定位。
构建下拉菜单
让我们创建一个纯 CSS 下拉菜单!我们将首先添加标记,然后添加 CSS。
创建基本的 HTML 列表
通常,在构建诸如通常隐藏在视图中的下拉菜单之类的组件时,我会将其构建得好像它没有被隐藏一样。然后,一旦完成并完全样式化,我会创建下拉行为。这也是我们在这里要做的。所以让我们在我们现有的index.html文档中创建 HTML。我们将转到我们的导航栏的无序列表,如下所示:
<nav class="grouping">
<figure>
<img src="img/sharky.png" alt="Shark">
</figure>
<ul class="primary-nav grouping">
<li><a href="#">Home</a></li>
<li><a href="shark-movies.html">Movies</a></li>
<li><a href="#">Species</a></li>
<li><a href="#">Chum</a></li>
</ul>
</nav>
最佳实践是在无序列表内构建菜单,其中每个菜单项都是列表项内的锚点。对于下拉菜单,我们需要在具有下拉菜单的li内部嵌套另一个ul标签。我们将在这里嵌套它:
<li><a href="shark-movies.html">Movies</a></li>
但首先,我们将为任何将有下拉菜单的导航项添加一个特殊的类has-submenu:
<li class="has-submenu"><a href="shark-movies.html">Movies</a></li>
这样,通过has-submenu类,我们可以在 CSS 中专门针对这些li标签及其后代。在这个电影li标签内部,我们将创建一个新的带有li标签的ul,并在这些li标签内部放入一个锚标签。以下是下拉菜单的标记:
<nav class="grouping">
<figure>
<img src="img/sharky.png" alt="Shark">
</figure>
<ul class="primary-nav grouping">
<li><a href="#">Home</a></li>
<li class="has-submenu"><a href="shark-movies.html">Movies</a>
<ul>
<li><a href="#">Jaws</a></li>
<li><a href="#">Sharknado</a></li>
<li><a href="#">Open Water</a></li>
</ul>
</li>
<li><a href="#">Species</a></li>
<li><a href="#">Chum</a></li>
</ul>
</nav>
下拉菜单中有三个子菜单:
我们只需要对其进行样式化,使其看起来像我们最终的菜单。
样式化下拉菜单
我们需要适当地样式化下拉菜单,以适应我们现有的菜单。这是我们要达到的效果:
我想要将下拉菜单的样式与主导航的样式分开。我们将通过在主导航下面创建这个大的下拉菜单注释来实现这一点:
/****************
Drop Down Menu
****************/
下拉菜单可以在这里有自己的小节。所以让我们首先只针对has-sub menu内部的ul。为了使子菜单放置在白色导航栏之外,让我们将其绝对定位并且距离top为70px:
/****************
Drop Down Menu
****************/
.has-submenu ul{
position: absolute;
top: 70px;
}
这给我们带来了以下效果:
现在我们只需要样式化下拉菜单,使其看起来像它应该的样子。请注意,在我们的网站上,没有一个li标签像我们的主导航一样向左浮动。这是因为,正如你记得的那样,我们使用了一种后代选择器,只针对primary-nav的直接子li。我们不需要取消之前的样式。让我们回过头来看看,如果我们不这样做会发生什么。
这是子组合选择器的位置:
.primary-nav > li {
float: left;
}
.primary-nav > li > a {
float: left;
padding: 25px 0;
width: 150px;
border-left: 1px solid #ada791;
}
为了进行快速测试,让我们从两个选择器中删除大于号符号:
.primary-nav li {
float: left;
}
.primary-nav li a {
float: left;
padding: 25px 0;
width: 150px;
border-left: 1px solid #ada791;
}
这是它的样子:
请注意,我们在顶部菜单中的所有样式都在子菜单中重复。这是我们想要避免的,因为我们不希望编写额外的 CSS 来取消整个菜单向左浮动并在不需要的地方添加边框。因此,让我们将那些大于号符号添加回我们的.primary-nav选择器中:
.primary-nav > li {
float: left;
}
.primary-nav > li > a {
float: left;
padding: 25px 0;
width: 150px;
border-left: 1px solid #ada791;
}
好吧,让我们给.has-submenu添加白色背景和边框。在底部、左侧和右侧添加边框。我们不希望顶部有边框,所以我们将使用border-bottom、border-left和border-right,而不是使用border的简写:
/****************
Drop Down Menu
****************/
.has-submenu ul {
position: absolute;
top: 70px;
background-color: #fff;
border-bottom: 1px solid #ada791;
border-left: 1px solid #ada791;
border-right: 1px solid #ada791;
}
现在开始类似下拉菜单:
一个明显的问题是宽度。我们需要给它一个width为150px,以匹配其父元素的宽度。另外,让我们给bottom-left和bottom-right角添加border-radius:
/****************
Drop Down Menu
****************/
.has-submenu ul{
position: absolute;
top: 70px;
background-color: #fff;
border-bottom: 1px solid #ada791;
border-left: 1px solid #ada791;
border-right: 1px solid #ada791;
width: 150px;
border-radius: 0 0 15px 15px
}
请注意border-radius的简写。它与边距和填充的简写非常相似。第一个值是左上角,然后顺时针方向。因此,第二个值是右上角,第三个是右下角,第四个是左下角。
现在我们有了所需的width和border-radius:
有一件奇怪的事情是,我们的导航项的文本看起来并不是居中对齐的。锚元素的文本是居中对齐的。如果你右键单击a标签的文本,然后选择“检查”,你就可以看到这一点:
问题在于li标签占据了整个宽度,而a标签是内联元素,只占据它们需要的宽度。让我们添加一个新的选择器:.has-submenu a,使用display: block和padding,上下各为20px:
.has-submenu a{
display: block;
padding: 20px 0;
}
下拉菜单看起来好多了:
我们的悬停状态从主导航中继承过来,这很好。唯一的问题是我们最后的悬停状态——Open Water——隐藏了圆角:
修复悬停状态
有两种方法可以解决悬停在Open Water子菜单项上时丢失圆角的问题。第一种方法是使用last-child伪类,你在前面的几节中学到了,来定位a选择器和子菜单的最后一个li选择器。这应该可以正常工作,但如果我们想要更深层次的浏览器支持,我们需要使用另一种技术,在ul元素——父元素上使用overflow: hidden。我倾向于在这里使用overflow: hidden的方法,因为它很简洁,并且具有更深层次的浏览器支持。
/****************
Drop Down Menu
****************/
.has-submenu ul{
position: absolute;
top: 70px;
background-color: #fff;
border-bottom: 1px solid #ada791;
border-left: 1px solid #ada791;
border-right: 1px solid #ada791;
width: 150px;
border-radius: 0 0 15px 15px;
overflow: hidden;
}
如果我们现在查看浏览器,问题已经解决了:
现在我们只剩下静态菜单。它总是打开的。我们需要创建一个下拉行为,当你将鼠标悬停在 MOVIES 导航项上时,它会出现。一种方法是默认隐藏下拉菜单,然后使用hover和focus伪类来显示它。
默认情况下使用display: none隐藏下拉菜单。让我们首先使用display: none隐藏整个ul标签:
/****************
Drop Down Menu
****************/
.has-submenu ul{
position: absolute;
top: 70px;
background-color: #fff;
border-bottom: 1px solid #ada791;
border-left: 1px solid #ada791;
border-right: 1px solid #ada791;
width: 150px;
border-radius: 0 0 15px 15px;
overflow: hidden;
display: none;
}
我们可以通过创建一个新的选择器.has-submenu:hover ul,只在has-submenu悬停时才针对ul进行定位:
/****************
Drop Down Menu
****************/
.has-submenu ul{
position: absolute;
top: 70px;
background-color: #fff;
border-bottom: 1px solid #ada791;
border-left: 1px solid #ada791;
border-right: 1px solid #ada791;
width: 150px;
border-radius: 0 0 15px 15px;
display: none;
}
.has-submenu a {
display: block;
padding: 20px 0;
}
.has-submenu:hover ul {
display: block;
}
根据这个规则集,当你悬停在电影菜单上时,里面的ul元素将被显示出来。然后,因为我们在之前的选择器中添加了display: none——非悬停状态,默认情况下,ul标签,也就是下拉菜单,不会被显示出来。现在默认情况下没有子菜单可用:
现在当我们悬停在电影菜单上时,子菜单就会出现:
我还要提到的一件事是,display: none存在无法让屏幕阅读器宣布内容的问题。
还有另一种选择:使用屏幕外隐藏技术,这需要额外的工作,但是非常值得。
使用屏幕外隐藏技术隐藏下拉菜单
基本思想是绝对定位一个元素,远离可见屏幕,这样它就不可见了,但屏幕阅读器仍然可以宣布它。关于无障碍还有很多要学习的地方。我建议你首先查看这篇文章css-tricks.com/places-its-tempting-to-use-display-none-but-dont/,至少了解一下如何使用屏幕外隐藏技术,然后再从无障碍方面继续学习:
Web 无障碍是一个非常深入的话题,它值得有一本专门的书来讲述。所以我在这里无法全面地讲述它。不过,我们可以用类似下面的方法来替代使用display: none来隐藏我们的下拉菜单,以使其更具有可访问性:
.accessibly-hidden {
position: absolute;
top: -9999px;
left: -9999px;
}
这将隐藏内容,对于有视力的用户来说,但对于屏幕阅读器用户来说,仍然会宣布内容。
我们已经很顺利地完成了下拉菜单的设置。这次效果非常完美。你第一次尝试创建下拉菜单可能不会这么顺利,但使用这些技巧,你可以避免一些可能遇到的问题。
我最大的两个建议是:
-
首先构建下拉菜单,就好像它将一直可见,然后在样式设置好并且看起来不错之后隐藏它。
-
由于推荐的方法是在无序列表中使用无序列表,因此值得仔细设置你的规则集以避免混淆。例如,一个适用于父
ul和子ul(即ul li)的样式规则集;另一个只适用于父ul和li选择器,使用子组合器(即ul > li);最后,一个只适用于子ul的规则集(即.has-submenu ul)。这样,你就不必为子ul创建一堆可能令人困惑的覆盖样式。
导航的下一个部分需要我们实际创建下拉效果;我们将使用 CSS 动画来实现这一点。
CSS 动画(第一部分)
我们的主导航现在正在成形,我们的下拉功能几乎完成了。下拉菜单的最后一点润色是 CSS 动画,以使下拉菜单向下平滑动画。动画非常有趣,现代浏览器,包括 Chrome,Firefox,Opera 和从 IE10 开始的浏览器都支持它们。IE9 仍然会显示下拉菜单,但它只会简单地出现/消失。动画与过渡非常相似,但我们可以对静态元素进行动画处理,并使用不同的动画属性和关键帧来控制动画。我们稍后会更深入地讨论这个问题。所以在这一部分,我们要做的是:我们将在要进行动画处理的元素的选择器中定义animation-name、animation-duration和animation-timing-function。之后,我们将继续定义我们要进行动画处理的关键帧。
定义动画名称、持续时间和时间函数
让我们回顾一下我们的下拉菜单的 CSS:
/****************
Drop Down Menu
****************/
.has-submenu ul{
position: absolute;
top: 70px;
background-color: #fff;
border-bottom: 1px solid #ada791;
border-left: 1px solid #ada791;
border-right: 1px solid #ada791;
width: 150px;
border-radius: 0 0 15px 15px;
overflow: hidden;
display: none;
}
.has-submenu a {
display: block;
padding: 20px 0;
}
.has-submenu:hover ul {
display: block;
}
让我们以如下方式定位has-submenu的hover状态:
.has-submenu:hover ul {
display: block;
}
现在,我们将使用非前缀的/W3C 标准属性名称,并在最后添加所需的前缀。因此,要进行动画,我们使用animation-name,并将slideDown作为动画名称:
.has-submenu:hover ul {
display: block;
animation-name: slideDown;
}
我可以随意命名这个动画,只要不使用任何空格。就像类名一样,我也不能以数字开头。此外,关键字none不能用作动画名称,因为它是保留的用于移除动画的特殊关键字。接下来,我们将以秒为单位指定动画持续时间和动画的时间函数:
.has-submenu:hover ul {
display: block;
animation-name:slideDown;
animation-duration: .25s;
animation-timing-function: ease;
}
对于timing-function,我使用了ease,但您还可以指定linear,ease-in,ease-out和ease-in-out函数,这些函数与我们用于transitions的相同时间函数。这段代码本身不会做任何事情。我们必须指定在使用@keyframes进行动画时会发生什么。因此,在最后一个规则集下面,我们将添加一个带有我们之前想出的动画名称slideDown的@keyframes at-rule:
@keyframes slideDown {
}
在花括号内,我们将指定from和to的时间偏移量:
@keyframes slideDown {
from {}
to {}
}
花括号内的任何内容都将是动画的起点,花括号内的任何内容都将是动画的终点。我们可以在动画中放置几个属性;让我们从translateY变换函数开始,值为负 100%:
@keyframes slideDown {
from {transform: translateY(-100%);}
to {}
}
这将使无序列表向上移动负 100%,使其成为起点。百分比是元素的高度。50%会使其向下移动一半的元素高度,而100%会使其向下移动整个元素的高度。因此,-100%将使其垂直向上移动整个元素的高度。在这里,translateY函数对我们来说是新的。它很像translate,只是它只用于垂直平移。translateX函数可以进行水平平移。在to的花括号内,我们将把translateY设置为0%:
@keyframes slideDown {
from {transform: translateY(-100%);}
to {transform: translateY(0%);}
}
我们现在可以看到菜单向下动画:
设置额外的关键帧
到目前为止,我们的动画完全可以用transition来实现,因为没有真正引入任何新内容。但是动画的强大之处在于我们可以设置额外的关键帧。让我们将 CSS 中的from和to更改为分别为0%和100%,如下所示:
@keyframes slideDown {
0% {transform: translateY(-100%);}
100% {transform: translateY(0%);}
}
我们不仅可以添加开始和结束,还可以在这两个点之间添加任意数量的停止。让我们添加一个新的关键帧,比如90%,其translateY为10%:
@keyframes slideDown {
0% {transform: translateY(-100%);}
90% {transform: translateY(10%);}
100% {transform: translateY(0%);}
}
我们正在将下拉菜单的位置从-100%到10%进行翻译,在 0.25 秒的前 90%。然后,在 0.25 秒的最后 10%,垂直移动从10%到0%。这使动画在最后有一点跳动或弹跳:
我们不仅可以添加多个关键帧,还可以在每个关键帧中添加多个属性。所以让我们将opacity添加到我们的动画中。假设我们从不可见的关键帧开始,最终不透明度为1,即完全可见。我们不会在 90%关键帧上动画不透明度:
@keyframes slideDown {
0% {transform: translateY(-100%);opacity: 0;}
90% {transform: translateY(10%);}
100% {transform: translateY(0%);opacity: 1;}
}
菜单现在向下动画并淡入:
供应商前缀
为了完成我们的下拉动画,让我们通过添加必要的供应商前缀来获得最大的浏览器支持:
/****************
Drop Down Menu
****************/
@-webkit-keyframes slideDown {
0% {-webkit-transform: translateY(-100%); opacity: 0; }
90% {-webkit-transform: translateY(10%);}
100% {-webkit-transform: translateY(0%); opacity: 1; }
}
@keyframes slideDown {
0% {transform: translateY(-100%); opacity: 0; }
90% {transform: translateY(10%);}
100% {transform: translateY(0%); opacity: 1; }
}
.has-submenu ul{
position: absolute;
top: 70px;
background-color: #fff;
border-bottom: 1px solid #ada791;
border-left: 1px solid #ada791;
border-right: 1px solid #ada791;
width: 150px;
border-radius: 0 0 15px 15px;
overflow: hidden;
display: none;
}
.has-submenu a {
display: block;
padding: 20px 0;
}
.has-submenu:hover ul {
display: block;
-webkit-animation-name: slideDown;
animation-name: slideDown;
-webkit-animation-duration: 2.5s;
animation-duration: 2.5s;
-webkit-animation-timing-function: ease;
animation-timing-function: ease;
}
@keyframes动画都需要-webkit-供应商前缀,以及transform,animation-name,animation-duration和animation-timing-function属性。
当我们来到这一部分的结尾时,我们的下拉菜单动画已经就位。CSS 动画在 IE10 及更高版本中受支持,因此较旧版本的 IE 和其他较旧的浏览器不会显示动画,但它们仍然可以访问菜单和其所有内容。在我们的情况下,由于这只是整体体验的额外触摸,如果较旧的浏览器错过了这一点,这并不是一个严重的问题;它们仍然可以获得他们需要的所有核心内容。在下一节中,我们将继续使用 CSS 动画,通过尝试我们的鲨鱼标志来创建一个更加强大的动画。
CSS 动画(第二部分)
我们的主导航下拉菜单的滑动动作已经完成。现在让我们通过尝试我们的鲨鱼标志和探索其他动画属性,如delay,iteration-count,fill-mode以及animation(这是简写)来深入研究 CSS 动画。
动画延迟,迭代次数和填充模式
让我们为鲨鱼图像添加一个动画,以便从不同的角度看动画可以做什么,并且每次页面加载时都会发生。我们将其命名为crazyShark:
nav figure {
position: absolute;
top: -20px;
left: 50px;
animation-name: crazyShark;
animation-duration: .25s;
animation-timing-function: ease;
}
@-webkit-keyframes crazyShark {
}
nav img {
width: 160px;
}
让我们添加一堆同时平移和旋转鲨鱼图像的@keyframes动画:
@keyframes crazyShark {
0% {transform: translate(90%, 70%);}
33% {transform: translate(40%, 20%) rotate(90deg);}
66% {transform: translate(10%);}
100% {transform: translate(0%) rotate(0deg);}
}
现在,让我们去我们的动画属性,并将持续时间从0.25秒更改为1秒:
animation-duration: 1s;
鲨鱼真的在四处移动,因此我们的动画被命名为crazyShark:
请注意,我使用的translate语法与我们之前使用的略有不同。由逗号分隔的两个值分别用于x和y坐标,而当x和y坐标相同时,可以使用一个单一值:
/*translate shorthands for translateX and translateY*/
transform: translate(40%, 20%);
/*2 values (x first, y second) when both values are different*/
transform: translate(10%);
/*1 value is used when x and y coordinates are the same*/
还有其他几个动画属性,其中两个是animation-delay和animation-iteration-count。我发现这两个都很有用:
nav figure {
position: absolute;
top: -20px;
left: 50px;
animation-name: crazyShark;
animation-duration: 1s;
animation-timing-function: ease;
animation-delay: 2s;
animation-iteration-count: 2;
}
现在,动画开始前将有 2 秒的延迟,我不会尝试在书本格式中说明这一点。然后它应该完全动画两次:
如果我们愿意,我们也可以无限重复动画;我们可以只添加infinite关键字而不是一个数字,鲨鱼将永远继续前进。我绝对不会尝试在书本格式中说明这一点! 让我们摆脱animation-delay和animation-iteration-count:
nav figure {
position: absolute;
top: -20px;
left: 50px;
animation-name: crazyShark;
animation-duration: 1s;
animation-timing-function: ease;
}
动画填充模式
animation-fill-mode属性告诉被动画化的元素在动画开始前和动画完成后该做什么。使用animation-fill-mode填充动画之前和/或之后的空间。我们现在不需要animation-fill-mode属性。因为鲨鱼动画在页面加载时开始,然后将鲨鱼降落到其静态位置-我们说不要平移和旋转:
100% {transform: translate(0%) rotate(0deg);}
但是,如果我们以x为 10%,y为 70%,旋转为 10 度结束动画会怎么样呢?
100% {transform: translate(10%, 70%) rotate(10deg);}
如果您应用这个并转到网站,您会注意到鲨鱼似乎在第一个标题附近结束动画,然后跳回到其原始位置。这由以下两个截图说明:
动画的最后,鲨鱼:
鲨鱼在动画结束后会瞬间传送到其静态位置:
我们可以使用animation-fill-mode: forwards来修复这个问题:
nav figure {
position: absolute;
top: -20px;
left: 50px;
animation-name: crazyShark;
animation-duration: 1s;
animation-timing-function: ease;
animation-delay: 2s;
animation-iteration-count: 2;
animation-fill-mode: forwards;
}
现在,在动画结束后,鲨鱼将保持在那个位置而不会跳回到其原始位置:
太棒了!
animation-fill-mode属性的值为backwards将确保被动画化的元素在动画开始之前就填充到其起始位置。both关键字是填充起始和结束位置的一种方式。
让我们将我们的规则集减少到这三个动画属性:
animation-name: crazyShark;
animation-duration: 1s;
animation-timing-function: ease;
让我们也调整和减少整个动画。这样,我们的疯狂鲨鱼将变得稍微不那么疯狂,但仍然足够疯狂:
@keyframes crazyShark {
0% {transform: translate(90%, 70%);opacity: 0;}
33% {transform: translate(40%, 20%) rotate(90deg);}
66% {transform: translate(10%, 50%);}
100% {transform: translate(0%) rotate(0deg);opacity: 1;}
}
我们将为每个动画属性添加供应商前缀。但在这之前,让我们使用动画属性的简写来使我们的编码生活变得更容易,将所有动画属性合并成一行。
使用动画简写
在我们的nav figure规则集中加入这些声明:
animation-name: crazyShark;
animation-duration: 1s;
animation-timing-function: ease;
从animation-name和底部两个声明中删除-name,这样我们就剩下了这个:
animation: crazyShark;
现在,我们将添加1s和ease:
animation: crazyShark 1s ease;
现在我们应该得到的结果如下:
nav figure {
position: absolute;
top: -20px;
left: 50px;
animation: crazyShark 1s ease;
}
此外,您可以将所有不同的动画属性放入一个简写中。无论您以什么顺序放置它们,只要animation-duration在animation-delay之前。以下是一种可能的方式,可以在一个方便的简写中使用我们讨论过的所有动画属性:
animation: [name] [duration] [timing-function] [delay] [fill-mode] [iteration-count];
现在我们已经有了简写,这将使添加供应商前缀版本变得更容易一些。
供应商前缀
让我们添加-webkit-前缀版本的animation属性:
nav figure {
position: absolute;
top: -20px;
left: 50px;
-webkit-animation: crazyShark 1s ease;
animation: crazyShark 1s ease;
}
我们将对@keyframes做同样的处理:
@-webkit-keyframes crazyShark {
0% {-webkit-transform: translate(90%, 70%);opacity: 0;}
33% {-webkit-transform: translate(40%, 20%);}
66% {-webkit-transform: translate(10%, 50%);}
100% {-webkit-transform: translate(0%);opacity: 1;}
}
@keyframes crazyShark {
0% {transform: translate(90%, 70%);opacity: 0;}
33% {transform: translate(40%, 20%);}
66% {transform: translate(10%, 50%);}
100% {transform: translate(0%);opacity: 1;}
}
请注意,我在@keyframes前面加了@-webkit-keyframes,以及在transform前面加了-webkit-transform。
有关动画的其他信息
有关 CSS 动画的更多信息,我建议查看我的文章,“CSS 动画并不那么难。”,网址为richfinelli.com/css-animati…:
总之,我们已经探讨了其他动画属性,比如animation-delay,animation-iteration-count和animation-fill-mode,在创建一个花哨的、过度的动画的过程中。我们还将所有这些属性简化为一个方便的简写。我们还为每个属性添加了-webkit-前缀版本,以获得更好的浏览器支持。在本章的下一节和最后一节中,我们将为整个导航栏添加box-shadow,并修复下拉菜单的一个错误,即z-index。
完成导航
我们几乎完成了我们的主导航,但还有一些小事情要做。首先,我们将解决一个z-index问题,我稍后会详细说明。然后,我们需要在我们的导航栏底部添加box-shadow以完成设计。
修复 Z 索引问题
首先,我们将使用z-index属性来修复一个错误。当您悬停在 MOVIES 导航项上时,会出现一个下拉菜单。您会注意到一些事情:
首先,下拉菜单中的一个导航项被突出显示了——而实际上不应该。其次,导航实际上是在 MOVIES 导航项的顶部进行动画。
我们可以将动画速度减慢到2.5 秒,以便更容易查看这个问题:
.has-submenu:hover ul {
display: block;
-webkit-animation: slideDown 2.5s ease;
animation: slideDown 2.5s ease;
}
这样可以更容易地看到下拉菜单是在 MOVIES 菜单项的顶部下拉的。
这就是我们的问题,这就是为什么我们最终会出现下拉菜单项中的一个被突出显示的原因。
在我们的 CSS 文件中:
/****************
Drop Down Menu
****************/
.has-submenu ul{
position: absolute;
top: 70px;
background-color: #fff;
border-bottom: 1px solid #ada791;
border-left: 1px solid #ada791;
border-right: 1px solid #ada791;
width: 150px;
border-radius: 0 0 15px 15px;
overflow: hidden;
display: none;
}
.has-submenu a {
display: block;
padding: 20px 0;
position: relative;
}
.has-submenu:hover ul {
display: block;
-webkit-animation: slideDown 2.5s ease;
animation: slideDown 2.5s ease;
}
这个错误,可以说,可以通过一个叫做z-index的新属性来修复。z-index属性设置了重叠元素的堆叠顺序。我们的下拉菜单出现在顶部,因为它在主导航项电影的锚标签之后。自然地,绝对定位的元素会出现在没有设置position属性的元素的上面。这就是为什么下拉菜单出现在主导航栏的顶部。z-index的值是一个数字。它可以应用于设置为relative、absolute或fixed位置的元素,以及透明度小于 1 或应用了transform的元素,以及其他一些情况。只要我们的下拉菜单——也就是z-index小于其容器——我们就可以继续。转到.has-submenu a选择器,让我们应用position:relative声明。这样,元素将接受z-index。我们将添加一个z-index为10:
.has-submenu a {
display: block;
padding: 20px 0;
position: relative;
z-index: 10;
}
在.has-submenu ul上,我们不需要应用position:relative,因为它已经设置为position: absolute;它将接受z-index为5,小于 10。所以理论上,我们应该已经解决了我们的 bug:
.has-submenu ul{
position: absolute;
z-index: 5;
top: 70px;
保存并查看我们的网站。在全速运行时,当你在导航项目上悬停时,没有一个菜单项会被突出显示,下拉菜单会出现在主导航栏后面。现在只是为了确保,再次减慢动画速度。你应该看到它出现在 MOVIES 菜单的后面,这很好:
让我们也把animation-duration改回.5s:
.has-submenu:hover ul {
display: block;
-webkit-animation: slideDown .5s ease;
animation: slideDown .5s ease;
}
添加 box-shadow
让我们谈谈box-shadow属性。在我们的最终网站上,你可以看到我们的主导航下面有这个阴影:
让我们回到我们的 CSS,找到我们的nav选择器。box-shadow是一个 CSS3 属性:
nav {
background-color: #fff;
position: fixed;
left: 0;
right: 0;
top: 0;
z-index:1;
box-shadow: 0 8px;
}
我们针对nav元素并使用非前缀版本,这在所有主要浏览器中都受支持,从 IE9 及以上版本开始。我们不必回头去添加任何供应商前缀,因为规范已经足够成熟,所有浏览器现在都支持非前缀版本。我们添加的前两个值是x和y。我们将x值设置为0,将y值设置为8px;这将使box-shadow属性向下发散:
box-shadow: 0 8px;
如果我使用负值,那么子菜单将从导航栏顶部发散。我们希望它从导航栏底部发散。
接下来是模糊值。我们将把它设置为15px:
box-shadow: 0 8px 15px;
如果我把模糊值保持为0,我们将得到一个硬的 8 像素边框。模糊是使它看起来更像阴影而不是边框的原因。
我们要使用的最终值是颜色。我们将使用一个名为rgba的新颜色值,这是 CSS3 颜色值。然后我们添加0, 0, 0。这意味着红色、绿色和蓝色都将为零,这意味着它们的输出将是黑色。变量a指的是 alpha 通道,我们将把它设置为.1:
box-shadow: 0 8px 15px rgba(0, 0, 0, .1);
所以如果你去网站上检查并取消检查开发者工具中的 box-shadow,你会看到box-shadow属性的效果。这是没有这个属性时的样子:
这张图显示了我们的网站应用了 box-shadow:
有时候,对于这些 CSS 属性,查看开发者工具是很好的。让我们看看如果我们改变它们的值会是什么样子。我们可以看看增加或减少模糊后 box-shadow 的效果。在下面的截图中,我们看到将值从15px增加到26px后的效果——你可以看到模糊消失了:
如果减少模糊,比如0px,它会变成硬化的阴影:
我认为大约15px是恰到好处的。你也可以看到它在更高的不透明度下是什么样子——更高的 alpha 通道。如果我们把 alpha 通道从.1改为.5,阴影会变得更暗:
我认为.1是合适的。这个效果很好。
总结
这是一个广泛的章节;我们涵盖了很多内容。我们构建并设计了我们的菜单。你学会了伪类,以及我们如何使用它们来针对 HTML 中元素的位置。我们熟悉了定位属性,使用absolute定位我们的鲨鱼图标。我们为我们的菜单建立了一个下拉菜单,并为它添加了动画。我们探索了动画属性,比如animation delay、iteration count和fill-mode,并将它们应用到我们的鲨鱼图标上。最后,我们通过修复z-index问题和使用box-shadow属性完成了导航的最终设计。在下一章中,我们将看看我最喜欢的主题之一,响应式网页设计,因为我们要为不同的设备尺寸准备这个网站。
第六章:变得响应式
到目前为止,我们几乎所有的东西都是固定尺寸。我们的布局有固定宽度,我们的图片有固定宽度,我们的菜单也有固定宽度。但是当使用手机、平板电脑和其他各种设备尺寸时,这样做并不会带来良好的体验。幸运的是,响应式网页设计可以将我们的静态网站转变为流体、设备友好的网站。
开创一切的书籍- 响应式网页设计,作者Ethan Marcotte,2011年。他概述了响应式网页设计的三个主要技术支柱:
-
流体网格,
-
灵活的图片,以及
-
媒体查询。
我们将讨论响应式网页设计的三个基本 CSS 基础,然后讨论如何在较小的屏幕尺寸下构建主导航的适配,最后是viewport元标签。
流体网格
在这一部分,我们将讨论响应式网页设计的三个主要组成部分之一,即流体网格或基于百分比的布局。我们将看看如何将固定宽度布局转换为流体网格,为此,您需要学习将像素转换为百分比的公式。
将像素转换为百分比
现在,我们有一个固定宽度布局,如下面的屏幕截图所示:
如果您缩小浏览器,您会看到它会分解成更小的尺寸,如下面的屏幕截图所示:
创建流体网格是解决这个问题的第一步。目标是将所有基于像素的宽度以及左右边距和左右填充转换为百分比。我们现在先忽略主导航,但稍后我们会回到它。我们将从div标签开始,这是我用来包装大部分内容的wrapper类。让我们将属性width更改为max-width。这表示该元素可以比960px宽,但不能超过这个宽度。让我们还将宽度设置为90%:
.wrapper {
width:960px;
width: 90%;
margin: 0 auto;
}
因此,根据这段代码,我们将宽度设置为其父元素的 90%,而父元素没有宽度。因此,它将是浏览器窗口的 90%。这将使其在960px以下的宽度上两侧有 5%的间距。让我们在浏览器中查看网站。您可以刷新浏览器并再次缩小它。下面的屏幕截图显示了它没有显著的影响,看起来相当糟糕:
我们还想在wrapper内部创建这些元素的百分比。由于我们从固定像素宽度开始,我们需要将所有像素转换为百分比。
计算百分比宽度
根据Ethan Marcotte的响应式网页设计,有一个将基于像素的布局转换为基于百分比的布局的公式:目标/上下文=结果。目标是元素的期望宽度。上下文通常是其父元素的宽度。结果是我们可以插入到我们的 CSS 中的百分比。
如果我们看看intro-content部分内的 HTML,我们可以看到wrapper类和其中的两个div标签,即intro-content和go-premium,如下面的代码片段所示:
<section class="grouping">
<div class="wrapper">
<div class="intro-content">
<h1>Old Chompy</h1>
<h2>Dedicated to sharks and other aquatic species</h2>
<p>Lorem ipsum dolor sit amet...</p>
</div><!-- end intro-content -->
<div class="go-premium">
<a href="#" class="call-to-action">Go Premium</a>
<p class="reasons">So many awesome features...
<a href="#">Learn more »</a>
</p>
</div><!-- end go-premium -->
</div><!-- end wrapper -->
</section><!-- end section -->
回到我们的 CSS,我们的第一个元素是intro-content,它是出现在包装器内部的部分,如下面的代码片段所示:
.intro-content {
width: 600px;
margin-right: 60px;
float: left;
}
这里的目标是 600 像素,上下文是 960 像素。因此我们的计算是 600 除以 960,等于 0.625。我们将这个值作为我们的宽度插入,并添加一个百分比作为我们的度量单位,并将小数点移动两位,使其变为 62.5%:
.intro-content {
width: 62.5%; /* 600/960 */
margin-right: 60px;
float: left;
}
正如您所看到的,声明结束时的我的注释告诉我,元素最初宽度为 600 像素,父元素最初宽度为 960 像素。
margin-right属性也需要是百分比。公式仍然是一样的——目标除以上下文等于结果。我们的目标是 60 像素,我们的上下文仍然是 960 像素——父元素,即wrapper类。60 除以 960 得到 0.0625。我们将这个转换为百分比,将小数点移动两位,得到6.25%:
.intro-content {
width: 62.5%; /* 600/960 */ margin-right: 6.25%; /* 60/960px */
float: left;
}
接下来是我们的呼吁行动按钮的容器,go-premium:
.go-premium {
width: 300px;
float: left;
margin-top: 150px;
}
由于宽度是300px,它也需要转换为百分比。所以让我们做同样的事情,在这种情况下,300 除以 960——我们仍然有同样的父元素。这是 0.3125。将小数点移动两位,加上百分比,然后将其放在 CSS 注释中,以备将来使用:
.go-premium {
width: 31.25%; /* 300/960 */
float: left;
margin-top: 150px;
}
现在我认为我们准备好在浏览器中查看这个了。如果我稍微缩小浏览器窗口,布局不会立即破裂:
但是如果我再稍微缩小浏览器窗口,那么最终它开始看起来非常糟糕:
但是,我们确实取得了一些进展,因为我们的布局开始变得流体。介绍内容和呼吁行动按钮随着浏览器窗口的变小而变窄。最终,它们将开始重叠,但没关系;至少我们为这个顶部部分建立了一个流体基础。
现在让我们看看它下面的三列;它们在窗口变小时有点破碎:
所以让我们看看secondary-section类中的 HTML。这三列在一个div标签中,类名为wrapper,最初也是 960 像素宽(但现在是最大宽度为 960px,宽度为 90%):
<section class="secondary-section grouping">
<div class="wrapper">
<div class="column">
<figure>
<img src="img/octopus-icon.png" alt="Octopus">
</figure>
<h2>The Octopus</h2>
<p>...</p>
<a href="#" class="button">Tenticals »</a>
</div>
<div class="column">
<figure><img src="img/crab-icon.png" alt="Crab"></figure>
<h2>The Crab</h2>
<p>...</p>
<a href="#" class="button">Crabby »</a>
</div>
<div class="column">
<figure><img src="img/whale-icon.png" alt="Whale"></figure>
<h2>The Whale</h2>
<p>...</p>
<a href="#" class="button">Stuart »</a>
</div>
</div><!-- end wrapper -->
</section>
我们将继续使用它作为我们的上下文,同时将我们的.column宽度从像素转换为百分比。一直到我们的 CSS 底部,我们看到每列宽度为300px:
/****************
3 columns
****************/
.column {
float: left;
width: 300px;
margin-left: 30px;
}
.column:first-child {
margin-left: 0;
}
让我们在这里应用我们的公式。我们已经知道 300 除以 960 等于 31.25%,因为这是我们刚刚使用的确切计算:
.column {
float: left;
width: 31.25%; /* 300/960 */
margin-left: 30px;
}
margin-left属性是30px,所以我们实际上要复制并粘贴 31.25%到这里,但是我们会移动小数点一位,并添加一个注释说明 30 除以 960:
.column {
float: left;
width: 31.25%; /* 300/960 */
margin-left: 3.125%; /* 30/960 */
}
我们在第一列上有一个margin-left属性的值为0。我们不必将 0 更改为百分比,因为 0、0 像素和 0%都是完全相同的东西——什么都没有:
.column:first-child {
margin-left: 0;
}
顺便说一句,我从来没有改变过任何高度、顶部和底部边距或填充,因为这些对我们来说并不重要。所以现在,如果我们刷新这个部分并使其变小,我们会看到我们的三列会与浏览器窗口一起按比例缩小:
现在我们主页的一切都是流体的,除了我们的导航,我现在打算保持原样。我想要完全不同的处理方式,所以我会将其保持为固定宽度。
将填充更改为百分比
我们从来不必将填充左右更改为百分比,因为我们没有任何填充,但是这样做的过程非常相似。您仍然使用相同的公式——目标除以上下文等于结果。但是上下文现在有点不同;它是元素本身的宽度,而不是父元素的宽度,就像宽度和边距一样。唯一的例外是,如果元素本身没有定义宽度,您可以使用其父元素的宽度或通过确定父元素的宽度来确定元素本身的宽度:
如果您使用box-sizing属性和 border-box 值,填充将不再计入元素的框模型宽度。因此,您可以将其保留为像素长度,只需将宽度和边距转换为百分比,因此box-sizing: border-box肯定会很有帮助。
鲨鱼电影页面上的流体网格
让我们搜索一些其他非百分比宽度/边距/填充。所以我们不用担心任何与垂直距离相关的东西,比如height、margin-top、margin-bottom、padding-top或padding-bottom。我们也不用担心任何值为0的东西。
我们将在wrapper规则集中遇到auto的左右边距:
.wrapper {
max-width: 960px;
width: 90%;
margin: 0 auto;
}
这不需要转换成百分比,因为auto会根据可用空间自动计算宽度,所以它和百分比一样好。
我们担心以下声明块中的margin属性:
.content-block .figure {
float: left;
margin: 30px;
border: 15px solid #fff;
overflow: hidden;
}
这个规则集有一个margin为30px;它使用了单值语法。这意味着上下左右的边距都是30px。我们只想改变左右边距。所以我们可以使用双值语法。第一个值是指上下边距,第二个值是指左右边距。
margin: 30px 30px;
记住content-block .figure是围绕我们的图片的元素,如下图所示。所以我们实际上是在尝试将margin-right和margin-left转换为百分比:
如果我们在shark-movies.html中查看,我们会发现图片在wrapper中:
<section id="jaws" class="content-block style-1 wave-border grouping">
<div class="wrapper">
<a href="#" class="figure">
<img src="img/jaws.jpg" alt="Jaws movie">
</a>
<h1>Jaws</h1>
<p>...</p>
<a href="" class="button button-narrow button-alt float-right">Learn More</a>
</div><!-- end of wrapper -->
</section>
所以,我们知道wrapper是 960 像素。到目前为止,我们的上下文很容易确定,因为我们的上下文一直是wrapper的宽度。
30 除以 960 得到 03.125,即 3.125%,所以我们会保存这个:
.content-block .figure {
float: left; margin: 30px 3.125%; /* 30/960 */
border: 15px solid #fff;
overflow: hidden;
}
在我们的网站上,除了导航之外,所有硬像素长度都是百分比!并不是所有东西都必须是百分比才能实现响应式网页设计。我们做出了判断,决定我们会处理导航而不使用百分比宽度。这对于流体网格和响应式网页设计的许多其他决定都是正确的;真的没有一种大小适合所有的解决方案。您网站的每个组件都需要从桌面到移动端进行彻底思考,甚至更好的是,从移动端到桌面。因此,第一步,创建流体网格,目前已经完成。这是一个重要的步骤,因为它确保我们的设计将开始在所有屏幕尺寸上很好地适应。在下一节中,我们将看一下灵活的图片。
灵活的图片
我们已经创建了一个流体网格,这是响应式网页设计的第一个基础。基础二是响应式图片或灵活图片。我们希望我们的图片,或者至少某些图片,能像我们的 divs 和 sections 一样行为。我们希望它们是流体的或灵活的。
从我们的网站来看,我们可以注意到章鱼、螃蟹和鲸鱼的三张图片随着它们所在的列变小。另一方面,顶部的鲨鱼无论浏览器宽度如何,大小似乎都保持不变:
我们导航中的图片不是灵活的。我们列中的三张图片是灵活的。我们将看一下导航中的图片,看看原因。但首先,让我们来看看保证响应式图片的三个因素:
-
将
img标签放在一个容器中。最语义化的容器通常是figure标签,但它当然可以是任何元素。 -
使容器变得流体;给它一个百分比宽度。
-
给所有
img标签或至少要成为流体或灵活的img标签分配max-width属性为100%。
章鱼、螃蟹和鲸鱼的图片
现在让我们来看看我们 HTML 文件中的一张图片。我们可以看到章鱼图片在一个容器中。容器是figure元素:
<figure>
<img src="img/octopus-icon.png" alt="Octopus">
</figure>
figure元素没有定义宽度,但它是一个占据整个容器宽度的块级元素。所以我们可以将figure宽度看作100%。它在column div 中:
<div class="column">
<figure>
<img src="img/octopus-icon.png" alt="Octopus">
</figure>
<h2>The Octopus</h2>
<p>...</p>
<a href="#" class="button">Tenticals »</a>
</div>
如果我们在我们的 CSS 列中查看,我们会看到列宽为31.25%:
/****************
3 columns
****************/
.column {
float: left;
width: 31.25%; /* 300/960 */
margin-left: 3.125%; /* 30/960 */
}
所以我们完成了第一步——我们的图像在容器内。然后我们有第二步——父元素是流动的。第三步是为所有图像分配最大宽度。
让我们滚动到我们的 CSS 文件的顶部。实际上,我在重置中有这个选择器,如下一个屏幕截图所示。它针对img、iframe、video和object,几乎所有类型的媒体。我已经为这个选择器分配了最大宽度为 100%:
img, iframe, video, object {
max-width: 100%;
}
我将这个选择器作为我在每个项目中使用的重置或基本样式层的一部分。所以,玩得开心,让我们删除那个属性:
img, iframe, video, object {
/* max-width: 100%;*/
}
如果我们保存并查看我们的网站,当我们缩小浏览器窗口时,图像不会变小;它们将保持相同的大小并挤在一起:
当我们重新添加max-width: 100%声明时,这些图像再次变得灵活。这表明任何图像的最大宽度只能是其容器的 100%。因此,随着容器变小,图像的宽度也会变小:
img, iframe, video, object {
max-width: 100%;
}
鲨鱼图片
鲨鱼图像不会变小有两个原因。让我们来检查一下。我们可以看到鲨鱼图像确实有一个直接的容器元素——一个figure标签。但是该容器是故意不是流动的:
如果您点击图像容器,即nav标签,您会看到它会扩展到浏览器的全宽,说明容器不是流动的:
如果我们检查图像本身,我们会看到它被分配了160px的宽度,这肯定会阻止它成为流动的:
就我个人而言,我不喜欢在图片上设置宽度。即使我不希望这张图片是流动的,我也不希望它有宽度。在这种情况下,让我们做一些清理工作,并更改图片,使得图像元素的宽度为160px,这是图像的容器,而不是图像本身。
nav img {
width: 160px;
}
这更多地是我的个人偏好:
nav figure {
position: absolute;
top: -20px;
left: 50px;
width: 160px;
-webkit-animation: crazyShark 1s ease;
animation: crazyShark 1s ease;
}
/* nav img {
width: 160px;
} */
我们故意将鲨鱼图像保留为固定宽度,因为它不一定需要缩小或增大以使设计具有响应性。我们将在本章后面单独处理页眉部分。
在鲨鱼电影页面上缩小图像
让我们看一下电影页面上的图像。当我们调整浏览器大小时,它们不会缩小。它们有固定的宽度:
我认为它们应该缩小;它们在较小的浏览器尺寸下有点太大了。我们电影页面上的所有三个图像之所以不会缩小,是因为它们的父元素没有定义宽度。让我们使用 Ethan Marcotte 的公式-目标除以上下文等于结果。我们知道图像、标题 1、段落和了解更多按钮所占区域的上下文仍然是 960 像素宽,因为它在wrapper内部:
那么围绕图像的锚点标签的宽度是多少?如果我们查看我们的 CSS,我们有.content-block .figure,那里没有定义宽度:
.content-block .figure {
float: left;
margin: 30px 3.125%; /* 30/960 */
border: 15px solid #fff;
overflow: hidden;
}
如果我们看一下.figure内部的图像,那里也没有定义宽度:
.content-block .figure img {
float: left;
-webkit-transition: transform .25s ease-in-out;
transition: transform .25s ease-in-out;
}
因此,我们必须利用 Chrome DevTools 的功能来确定围绕img元素的a元素的宽度是多少。如果我们悬停在图像本身上,我们会看到图像是 200 像素乘以 200 像素:
如果我们实际上突出显示锚点,如下面的屏幕截图所示,DevTools 告诉我们宽度为 230 像素。您可以看到它在图像正上方的弹出气泡中。我们的宽度是 230 - 图像的 200 像素加上 15 像素的左边框和 15 像素的右边框。这是有道理的。
现在我们要做的是,当我们将像素值转换为百分比时,我们要使用 230 作为我们的目标。我们还将不得不使用box-sizing: border-box。记住,正如你在第一章中学到的CSS 基础中的盒模型和块与内联元素部分中所学到的,如果你将一个元素设置为box-sizing: border-box,那么border和padding会被计算到你定义的width中:
.content-block .figure {
float: left;
margin: 30px 3.125%; /* 30/960 */
border: 15px solid #fff;
overflow: hidden;
box-sizing: border-box;
}
230 除以 960 等于 0.23958333333333,所以我们将其转换为百分比,得到23.98333333333%:
.content-block .figure {
float: left;
margin: 30px 3.125%; /* 30/960 */
border: 15px solid #fff;
overflow: hidden;
box-sizing: border-box;
width: 23.958333333333%; /*230/960*/
}
现在,如果我们刷新浏览器并将其缩小,我们会看到我们的图片变小了。现在可能看起来有点奇怪,但信不信由你,这正是我们想要的,所以很棒!
现在让我们回到 CCS 代码。我们正在使用的这种技术非常有帮助,但我们可能会在整个代码中重复使用box-sizing: border-box。让我们从.content-block .figure规则集中完全删除它,并以稍微不同的方式将其添加到我们的重置中,应该在哪里。让我们将这个添加到我们样式表的重置部分:
html {
box-sizing: border-box;
}
*, *:before, *:after {
box-sizing: inherit;
}
作为我们的重置的一部分,每个元素都将获得box-sizing: border-box。我们可以看到我们已经将它添加到我们的 HTML 元素中,并使用了通用(星号)选择器,正如你从我们的特异性规则部分中记得的那样,它适用于所有元素。我们只在html元素上应用box-sizing: border-box,但其他所有东西都将获得box-sizing: inherit。html是每个元素的父元素;因此,你正在继承border-box属性到每个元素。好了,我们从灵活的图片中稍微偏离了一下,但我们需要这样做来创造一个积极的前进路径。
因此,总结一下,我们的图片现在是灵活的,但在非常小的浏览器宽度下(比如手机或平板电脑等更小的设备),我们的网站并不完美。在下一节中,我们将找出如何使用媒体查询来处理这个问题。
媒体查询
响应式网页设计的前两个基础只能让你走得更远。最重要的基础是媒体查询。媒体查询基本上是你的 CSS 中的“if”语句或条件逻辑。例如,如果浏览器的宽度小于 500 像素,我们可以根据这些条件应用不同的规则集。媒体查询非常强大,因为在某些点上,我们的网站真的会崩溃并且看起来很糟糕,我们需要用它来修复这个问题。在本节中,我们将找出什么是媒体查询,并使用它来修复网站的剩余问题,特别是在更窄的宽度下。
还要考虑的一件事是,由于我们要缩小浏览器窗口来模拟平板电脑或移动设备,我们将没有太多的空间来查看 DevTools。你可以点击 3 个垂直点的图标,打开下拉菜单将 DevTools 移到右边:
Chrome 会不时地更新 UI,所以图标可能对你来说看起来不一样。
现在我们可以将浏览器窗口缩小到任何更小的宽度,仍然有足够的空间来使用开发者工具:
快速提示
如果你使用 Chrome DevTools 并缩小浏览器窗口,在右上角它会显示浏览器视口的宽度和高度。因此,在下面的图片中,你可以看到它提供了关于宽度的信息,为691px。
我们网站目前的问题是,如果达到更窄的宽度,导航栏将会在鲨鱼后面,呼吁行动按钮将被挤压到网站标题中,三列也太窄了:
所以我们将跳过导航栏,最后再回来处理。主要的网站标题和呼吁行动按钮是浮动的。在 1023 像素时,让我们使用媒体查询“取消浮动”这两个部分,并将它们堆叠在一起。
媒体查询的解剖
我将把所有媒体查询放在样式表的底部。您不必这样做,但我会这样做。媒体查询始终以@media开头。然后,它们有两个部分。第一部分是媒体类型。例如:
@media screen
您还可以插入print,screen,all和其他一些选项。print仅适用于打印样式表,而screen仅适用于计算机屏幕(但不适用于打印输出)。all将适用于两者。如果我们完全省略媒体类型 - 这是完全可以接受的 - 它将默认为all。
媒体查询的第二部分称为媒体特征,并确定何时使用媒体查询。我们需要将其与@media screen分开,使用“and”一词。例如:
@media screen and (max-width: 1023px) { }
同样,这是类似于 JavaScript 中的 if 语句的条件逻辑。如果媒体类型和媒体特征都评估为 true,那么媒体查询内部的内容将被应用(是的,我们很快将在媒体查询的大括号内放置内容)。
(max-width: 1023px)表示如果浏览器窗口为 1,023px 或更低,则此媒体查询将应用(或评估为 true)。一旦屏幕宽度超过 1,023px,那么媒体查询将不再应用(或评估为 false)。您还可以使用min-width;它具有相反的效果,适用于所有大于 1,023px 的内容:
@media screen (min-width: 1023px) { }
实际上,您也可以使用任何长度值以及许多其他值。通常,max-width和min-width与我们要做的事情非常契合,我们现在将坚持使用max-width。注意末尾的大括号。这几乎就像我们刚刚构建了一个 CSS 规则集:
@media screen (max-width: 1023px) { }
在媒体查询的大括号内,我们可以开始编写纯粹的 CSS,只有在浏览器窗口同时(1)是屏幕和(2)1,023px 或更少时才会应用。让我们将float: none和width: auto都添加到intro-content和go-premium:
@media screen and (max-width: 1023px){
.intro-content {
float: none;
width: auto;
}
.go-premium{
float: none;
width: auto;
}
}
auto值是width属性的默认值,因此它实际上使这些块元素跨越整个可用宽度,这将是wrapper的 100%。auto关键字意味着它将自动计算值。auto根据其配对的属性具有不同的值。在这种情况下,它基本上与“100%”相同。
现在我们可以看到我们的介绍内容不再浮动,宽度是全宽,而go-premium按钮也是一样的:
但是,go-premium按钮和intro-content之间有一个很大的空间,我们需要摆脱它:
为了解决这个问题,我们将添加:
@media screen and (max-width: 1023px){
.intro-content {
float: none;
width: auto;
padding-bottom: 0;
margin-bottom: 30px;
}
.go-premium {
float: none;
width: auto;
}
}
在go-premium按钮本身上,我们将删除顶部边距:
@media screen and (max-width: 1023px){
.intro-content {
float: none;
width: auto;
padding-bottom: 0;
margin-bottom: 30px;
}
.go-premium {
float: none;
width: auto;
margin-top: 0;
}
}
我们已经有了我们的标题,副标题,文本和按钮,一切看起来都很好:
考虑 iPad 和其他平板电脑尺寸
我们选择 1,023 作为断点,因为这只是低于横向放置的 iPad 的宽度的一个像素。
这样,我们的媒体查询将适用于所有小于 1,024p 的宽度和设备。截至 2017 年,我会猜测 iPad 是 - 如果不是最受欢迎的平板电脑 - 其中一个最受欢迎的平板电脑。我可以更肯定地说 iPad 绝对不是唯一受欢迎的平板电脑。事实上,令人惊讶的是,有多少不同的平板设备和宽度,因此您可能不希望分别使用 1,024 和 768 作为媒体查询的基础。找出您的布局通常从何处开始中断或看起来有趣,并确定媒体查询的逻辑放置位置。然后,在 iPad 和任何其他设备或模拟器上测试您的网站,以确保您的网站看起来不错。在我们的情况下,我们将使用 1,023 作为基线,因为在 1,024 时,布局仍然看起来不错。
将我们的三列添加到媒体查询
现在,我们只需要将所有的 CSS 添加到我们的媒体查询中,以使网站的其余部分看起来不错,从三列区域开始。正如您在下面的截图中所看到的,这些区域太紧凑了:
为较小的设备创建内容管道是一种标准做法,摆脱任何多列浮动布局。因此,我们将再次从.column中删除浮动,并通过指定auto关键字使宽度成为父元素的全宽。让我们转到 CSS 的底部并更新媒体查询:
@media screen and (max-width: 1023px){
.intro-content {
float: none;
width: auto;
padding-bottom: 0;
margin-bottom: 30px;
}
.go-premium {
float: none;
width: auto;
margin-top: 0;
}
.column {
float: none;
width: auto;
}
}
现在当我们转到我们的网站时,这三列中的每一列都将占满整个宽度,不会浮动:
问题是,我们可能应该将这段文字和图像本身居中。因此,首先我们将在媒体查询中首先针对.column figure进行定位以居中图像:
@media screen and (max-width: 1023px){
.intro-content {
float: none;
width: auto;
padding-bottom: 0;
margin-bottom: 30px;
}
.go-premium {
float: none;
width: auto;
margin-top: 0;
}
.column {
float: none;
width: auto;
}
.column figure {
margin: 0 auto;
max-width: 250px;
width: 100%;
}
}
通过使用auto关键字设置左右边距,并且不仅设置width: 100%而且还设置max-width: 25px,我们能够使图像居中:
添加width: 100%确保如果wrapper容器小于 250px(图像的最大尺寸),则图像宽度将是其容器的100%;在非常窄的宽度下起到一种安全保障作用。
接下来,让我们简单地使用text-align: center来居中标题:
@media screen and (max-width: 1023px){
.intro-content {
float: none;
width: auto;
padding-bottom: 0;
margin-bottom: 30px;
}
.go-premium {
float: none;
width: auto;
margin-top: 0;
}
.column {
float: none;
width: auto;
}
.column figure {
margin: 0 auto;
max-width: 250px;
width: 100%;
} .column h2 {
text-align: center;
}
}
看起来不错:
我们的响应式设计还可以采用另一种方法-移动优先方法。
移动优先方法
通常,这是最佳实践。我们希望在同一时间或在桌面体验之前考虑较小的显示屏,正如名称所示。这样,内容和设计可以在设计和构建过程中充分考虑移动设备及其所有约束,因此可以完全实现。Mobile first 不仅涉及我们如何编写 CSS。但这是移动优先的代码部分的一般思路:将所有针对最小设备的 CSS 放在任何媒体查询之外。然后使用媒体查询来针对越来越大的设备。
这意味着使用min-width媒体查询为较大的显示屏添加额外的 CSS。例如,我们的布局默认情况下不会有任何浮动;相反,它将是一个单一的内容隧道,这通常是移动设备的标准。然后我们的媒体查询,使用min-width而不是max-width,将添加浮动,这些浮动将应用于更宽的屏幕宽度以创建多列布局。
有关移动优先方法的更多信息,请查看 Luke Wroblewski 关于该主题的定义书籍-Mobile First-可在abookapart.com网站上找到:
解决导航问题
现在让我们谈谈这个导航。解决导航问题并不像使用媒体查询解决我们刚刚解决的问题那么容易。在某些宽度下,我们的鲨鱼会挡住导航,而且在某个时候,我们还需要对下拉菜单做些处理。此外,如果我们要添加更多的导航项,那么在更宽的宽度下,我们将遇到这个问题。
我们遇到了解决导航的第一个真正挑战。出现了一些特定的响应式设计模式。让我们谈谈 Brad Frost 策划的网站-bradfrost.github.io/this-is-res…:
这基本上是一组用于响应式网页设计的模式。它有很多不同的处理导航的方式供您探索。让我们看看模式下的第一个,称为切换。在较宽的宽度下,菜单看起来有点像我们的;顶部只是一些导航项:
在较窄的浏览器宽度下,导航项被替换为菜单链接:
当您单击链接时,它会展开并隐藏菜单:
这通常是我们想要在我们的网站上做的事情;这将需要更多地使用媒体查询和一些 JavaScript 或 jQuery 来显示和隐藏导航点击。准备好挑战了吗?好的,让我们做到!
我们已经使用媒体查询根据屏幕宽度更改了我们的 CSS。这非常有用,因为它修复了我们在较窄宽度下的大部分问题。我们仍然需要在较小宽度下修复导航。在下一节中,我们将使用媒体查询大幅改变我们的导航,使其隐藏,除了一个可以点击以显示带有 jQuery 的导航的菜单图标。
移动菜单
到目前为止,在本章中,您已经了解了 Ethan Marcotte 关于响应式网页设计的三个基本原则-流体网格,可伸缩的图像和媒体查询。在某种程度上,这在某种程度上是容易的部分。困难的部分是弄清楚多种棘手的设计挑战;例如,在移动设备上该怎么处理我们的菜单,特别是当我们决定添加更多菜单时。幸运的是,这是一个体面的设计模式已经出现的领域。我们将放弃水平菜单,而是显示导航栏上的每个按钮的隐藏菜单,而是通过点击或触摸来激活的隐藏菜单。被点击或触摸后,隐藏菜单将垂直滑下并显示所有菜单选项。我们将通过为移动导航设置样式来实现这一点。然后,我们将隐藏导航并添加触发移动菜单打开和关闭的菜单图标。最后,我们将将我们的 HTML 链接到 jQuery CDN 和我们的脚本文件,并编写一些基本的 jQuery 来实现这一点。
在打开状态下为移动导航设置样式
在样式表的最底部,我将添加一个针对900px或更小的新媒体查询:
@media screen and (max-width: 1023px){
...
}/* end of media query */
@media screen and (max-width: 900px) {
}
请注意,我还在第一个媒体查询的结束大括号处添加了一个注释。这很有用,这样我们就不会迷失我们的第一个媒体查询的结束位置。新的移动导航只会在新的断点900px宽度时触发。那时它开始看起来很奇怪并且开始破损。首先,我不想导航固定在顶部,所以让我们摆脱固定定位并将其替换为默认的静态位置:
@media screen and (max-width: 900px) {
nav {
position: static;
}
}
静态,你可能还记得之前的部分,是position属性的默认值,因此它基本上关闭了fixed定位,并将其返回为根据正常流行为的元素。
接下来,让我们告诉primary-nav的所有直接列表项向右浮动,而不是向左。我们还将给它一个完整的宽度,因为这些浮动元素只会占用所需的宽度,类似于内联元素,因此我们将告诉它们通过将宽度设置为100%来占用整个可用宽度:
@media screen and (max-width: 900px) {
nav {
position: static;
}
.primary-nav > li {
float: right;
width: 100%;
}
}/* close media query */
我还在这个媒体查询的结束大括号处添加了一个注释,以便不会迷失。
现在让我们专注于锚点。让我们将文本对齐到右侧,标准化填充,移除左侧的边框并在底部添加边框,并将字体大小调整为 13px,并将宽度设置为 100%:
@media screen and (max-width: 900px) {
nav {
position: static;
}
.primary-nav > li {
float: right;
width: 100%;
}
.primary-nav li a {
text-align: right;
padding: 15px 25px 15px 0;
width: 100%;
border-bottom: 1px solid #e7e7e7;
border-left: none;
font-size: 13px;
}
}/* close media query */
好的,这是在应用此 CSS 之前导航的样子:
这是刷新后的样子。现在它开始看起来像移动导航了:
问题在于 100%的宽度实际上并没有起作用。嗯,实际上它起作用了;锚点是其容器的 100%,但它们的容器也需要是 100%。因此,让我们将primary-nav的宽度设置为 100%:
@media screen and (max-width: 900px) {
nav {
position: static;
}
.primary-nav > li {
float: right;
width: 100%;
}
.primary-nav li a {
text-align: right;
padding: 15px 25px 15px 0;
width: 100%;
border-bottom: 1px solid #e7e7e7;
border-left: none;
font-size: 13px;
} .primary-nav {
width: 100%;
}
}/* close media query */
我们可以在待办清单上划掉这一项:
这确实像是一个菜单。房间里的大象是鲨鱼只是占用了太多的空间。让我们来修复一下。
让我们在针对鲨鱼的媒体查询的底部添加一个新的选择器。我们将使它变得更小,向上移动,并使用顶部和左侧位置的偏移属性将其紧贴到左侧,因为这已经是一个绝对定位的元素:
@media screen and (max-width: 900px) {
nav {
position: static;
}
.primary-nav > li {
float: right;
width: 100%;
}
.primary-nav li a {
text-align: right;
padding: 15px 25px 15px 0;
width: 100%;
border-bottom: 1px solid #e7e7e7;
border-left: none;
font-size: 13px;
} .primary-nav {
width: 100%;
}
nav figure {
width: 100px;
top: 0;
left: 20px;
}
}/* close media query */
看起来不错:
接下来要修复的是下拉菜单。这样不行:
我们现在需要做一个设计决定。我们应该隐藏下拉菜单,不让任何移动用户访问它吗?我们可以。但这对移动用户不公平。我们显然可以保持它如上所示,但我认为把它融入到primary-nav中会更好地为移动用户服务。我想让它看起来像其他主要菜单。所以我们要针对.has-submenu ul选择器。我们将在媒体查询的底部添加这个规则集。我们将把position属性从absolute改为static,把display属性改为block,移除border和border-radius属性,并让width横跨整个宽度:
@media screen and (max-width: 900px) {
nav {
position: static;
}
.primary-nav > li {
float: right;
width: 100%;
}
.primary-nav li a {
text-align: right;
padding: 15px 25px 15px 0;
width: 100%;
border-bottom: 1px solid #e7e7e7;
border-left: none;
font-size: 13px;
} .primary-nav {
width: 100%;
}
nav figure {
width: 100px;
top: 0;
left: 20px;
} .has-submenu ul {
position: static;
display: block;
border: none;
border-radius: 0;
width: 100%;
}
}/* close media query */
现在我们有这个:
哇!看起来很不错。我们也可以取消动画,因为它不再需要。我们将在媒体查询内部添加一个新的选择器,并将-webkit-animation和animation设置为none;这个关键字将取消动画:
@media screen and (max-width: 900px) {
nav {
position: static;
}
.primary-nav > li {
float: right;
width: 100%;
}
.primary-nav li a {
text-align: right;
padding: 15px 25px 15px 0;
width: 100%;
border-bottom: 1px solid #e7e7e7;
border-left: none;
font-size: 13px;
} .primary-nav {
width: 100%;
}
nav figure {
width: 100px;
top: 0;
left: 20px;
} .has-submenu ul {
position: static;
display: block;
border: none;
border-radius: 0;
width: 100%;
} .has-submenu:hover ul {
-webkit-animation: none;
animation: none;
}
}/* close media query */
我们不再有动画效果。"电影"菜单的悬停状态以一种奇怪的方式覆盖了鲨鱼,但很快在我们添加汉堡菜单图标时就会得到修复:
移动导航在打开状态下已经完成;现在我们需要隐藏它,并添加汉堡图标来触发它的打开和关闭。
添加汉堡菜单图标
让我们在index.html文件和shark-movies.html文件中的primary-nav正上方添加一个div标签。我们给它一个类名mobile-menu-icon;这很重要:
<!--
===============
Nav
===============
-->
<nav class="grouping">
<figure>
<img src="img/sharky.png" alt="Shark">
</figure>
<div class="mobile-menu-icon"></div>
<ul class="primary-nav grouping">
<li><a href="#">Home</a></li>
<li class="has-submenu"><a href="shark-movies.html">Movies</a>
<ul>
<li class=""><a href="">Jaws</a></li>
<li class=""><a href="">Sharknado</a></li>
<li class=""><a href="">Open Water</a></li>
</ul>
</li>
<li><a href="#">Species</a></li>
<li><a href="#">Chum</a></li>
</ul>
</nav>
当我们应用这个时,浏览器中什么都没有显示出来,因为这只是一个空的div标签。让我们使用背景图片添加图标。我们不会把这个放在媒体查询中;实际上,我们会移动到 CSS 中原始的 nav 所在的位置,并添加这个规则集:
/****************
nav
****************/
nav {
background-color: #fff;
position: fixed;
left: 0;
right: 0;
top: 0;
z-index:1;
box-shadow: 0 8px 15px rgba(0, 0, 0, 0.1);
}
nav figure {
width: 160px;
position: absolute;
top: -20px;
left: 50px;
-webkit-animation: crazyShark 1s ease;
animation: crazyShark 1s ease;
}
.mobile-menu-icon {
background: url('../images/mobile-menu-icon.png') 0 0 no-repeat;
}
我们已经在我们的图片文件夹中有这张图片。我们使用零和零作为我们的背景位置和 no-repeat,以确保这张图片不会自动重复。除非我们添加宽度和高度,否则在浏览器中什么都不会显示。我们知道这张图片宽 30 像素,高 26 像素,所以我们将使用这些确切的尺寸:
.mobile-menu-icon {
background: url('../images/mobile-menu-icon.png') 0 0 no-repeat;
width: 30px;
height: 26px;
}
现在当我们保存并刷新时,我们可以看到三条杠图标位于浏览器窗口的顶部:
我们还想把它移到右边,并给它一些距离,使用一些上、右和下的 margin。我们还可以改变光标,这样它看起来就有了不同的外观。让我们把这些属性添加到mobile-menu-icon中:
.mobile-menu-icon {
background: url('../images/mobile-menu-icon.png') 0 0 no-repeat;
width: 30px;
height: 26px;
float: right;
margin: 10px 15px 10px 0;
cursor: pointer;
}
在刷新浏览器之前,我们可以看到我们只是在图标上方普通的光标悬停:
刷新后,它移到右边,现在有一个指针类型的光标,表示它是可点击的:
显然,我们在移动设备上看不到这个,但在桌面设备或桌面浏览器上,这是一个小小的好东西。
隐藏菜单
现在让我们默认隐藏菜单。我们不再使用display: none,因为如我之前提到的,这对无障碍原因来说并不好。让我们探索另一种更有创意地隐藏内容的技术,以便屏幕阅读器仍然可以找到并宣布它。我们将回到媒体查询内部,.primary-nav内部。我们将说这个元素的高度是零:
@media screen and (max-width: 900px) {
.intro-content {
margin-top: 50px;
}
nav {
position: static;
}
.primary-nav {
width: 100%;
max-height: 0;
overflow: hidden;
-webkit-transition: all ease-out .35s;
-moz-transition: all ease-out .35s;
-o-transition: all ease-out .35s;
transition: all ease-out .35s;
}
...
以下是前面代码的输出:
就是这样。现在我们要做的是在点击时激活菜单。
使用 jQuery 在点击时触发菜单
在这个阶段,我们需要链接到 jQuery 和我们自己的 JavaScript 文件。我们将在 HTML 的底部,在闭合的</body>和</html>标签之前做这个。复制一个链接到托管在谷歌网站上的 jQuery CDN。在此之下,添加一个链接到我们自己的 JS 文件。我们将把这个文件放在js文件夹中,并将文件命名为scripts.js:
<script src="img/jquery.min.js"></script>
<script src="img/scripts.js"></script>
</body>
</html>
另外,让我们将这个复制到shark-movies.html的相同位置。我们也要创建一个新的 JavaScript 文件。
在 Sublime Text 中创建新文件的简单方法是使用Cmd + N(在 Mac 上)或Ctrl + N(在 Windows 上)。Cmd + S(在 Mac 上)或Ctrl + S(在 Windows 上)将让您保存名称并保存文件。
我们将其保存在js文件夹中:
我们将文件命名为scripts.js。
好的,现在让我们写一些 jQuery。如果这里的一切对你来说都不太有意义,不要担心。我不会详细讨论这个话题,因为这超出了本书的范围,但这对我们的响应式设计是必需的。我们要粘贴到我们的新scripts.js文件中的是一个在 DOM 准备就绪时触发的函数:
$(document).ready(function() {
});//end doc ready
我们想把我们写的代码放在这个函数里。这只是告诉脚本在网页准备就绪之前等待,然后再执行其中的代码。这对于 jQuery 来说是标准的。因此,让我们将其粘贴到我们的函数中:
$(document).ready(function() {
$(".mobile-menu-icon").on("click", function(){
$(".primary-nav").toggleClass("active");
$(this).toggleClass("open");
});
});//end doc ready
首先,我们在这里有一个 jQuery 函数,它专门针对mobile-menu-icon类,当你点击该元素时:
$(".mobile-menu-icon").on("click", function(){
一旦点击了该元素,就会执行两行代码。我们首先要关注的是primary-nav,并切换一个名为active的类:
$(".primary-nav").toggleClass("active");
$(this).toggleClass("open");
因此,如果您点击汉堡菜单,它将向primary-nav添加一个active类。如果您再次点击它,它将删除它,并为我们保持这样做,这很好。下一行是针对$(this)。在这里,$(this)指的是我们点击的任何东西。在这种情况下,我们点击的是mobile-menu-icon,并在其上切换一个名为open的类。查看 DevTools 中的移动菜单图标和primary-nav:
DevTools 中的代码行在以下截图中突出显示:
这两者都应该添加类。当我们点击汉堡菜单图标时,我们会看到mobile-menu-icon获得open类,primary-nav获得active类:
当我们再次点击它时,它们两者都消失了。所以现在我们可以继续以我们需要的方式定位这些类。让我们回到我们的 CSS。我们想要定位mobile-menu-icon在打开状态下。因此,我们将该选择器添加到 CSS 的 nav 部分中:
@media screen and (max-width: 900px) {
...
.mobile-menu-icon {
background: url('../images/mobile-menu-icon.png') 0 0 no-repeat;
width: 30px;
height: 26px;
float: right;
margin: 10px 15px 10px 0;
cursor: pointer;
}
.mobile-menu-icon.open { }
}/* end of media query */
我们要做的就是改变背景图片:
.mobile-menu-icon.open {
background-image: url('../images/mobile-menu-close-icon.png');
}
现在当我们点击汉堡图标时,我们得到 x 图标,当我们再次点击它时,我们得到菜单图标。所以很好:
现在我们想要定位primary-nav.active选择器,因此让我们将其添加到我们的 CSS 中,并给它一些高度:
@media screen and (max-width: 900px) {
.intro-content {
margin-top: 50px;
}
nav {
position: static;
}
.primary-nav {
width: 100%;
max-height: 0;
overflow: hidden;
}
.primary-nav.active {
max-height: 350px;
}
...
}
现在当我们点击图标时,我们得到我们的菜单:
当我们再次点击它时,它消失了:
目前,图像出现并立即消失,所以我们想为其添加一个过渡效果。让我们去.primary-nav并添加一个过渡效果:
@media screen and (max-width: 900px) {
.intro-content {
margin-top: 50px;
}
nav {
position: static;
}
.primary-nav {
width: 100%;
max-height: 0;
overflow: hidden;
-webkit-transition: all ease-out .35s;
transition: all ease-out .35s;
}
.primary-nav.active {
max-height: 350px;
}
...
}
现在我们应该有一个丝般顺滑的过渡效果,当我们点击菜单图标时,隐藏的导航会向下滑动和向上滑动。
我们的移动导航已经完成,有相当多的 CSS 和一点 JavaScript,并且我们的网站现在具有广泛的响应性。我们只需要做一件事 - 我们需要在移动设备上测试我们的网站。我们会注意到,结果与我们将浏览器调整为手机或平板电脑宽度时非常不同。幸运的是,解决方案非常简单 - viewport meta 标签。
视口 meta 标签
我们的响应式网站几乎完成了。除了我们还没有在移动设备上测试过它。在本节中,让我们使用 Chrome 的移动设备模拟器来测试我们的设计,然后看看并尝试理解viewport meta 标签。
在移动设备上测试我们的响应式设计
在手机上测试的一种方法是这样的-让您的网站上线并在实际手机或平板上测试。一个更简单的方法是在手机上进行简单测试(但可能略微不太准确)是使用 Chrome 的设备模拟器。在 DevTools 中有一个设备图标:
一旦你点击了那个,你就可以选择一个手机。我们可以看到我们的网站,但它看起来并不像我们刚刚将浏览器窗口最小化到手机大小时那样:
发生的情况是,大多数移动设备会尝试缩小您的网站以适应手机屏幕,然后如果您的网站不具有响应性,它将看起来像桌面版本,只是小得多。所以一个显而易见的问题是我没有看到移动导航。有一个非常简单的解决方案-viewport元素。我将把它复制粘贴到index和shark-movies页面中:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<!-- description -->
<title>Section 6-Becoming Responsive - Mastering CSS</title>
<!-- stylesheets -->
<link rel="stylesheet" href="css/style.css">
<!-- mobile -->
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
<!-- stylesheets for older browsers -->
<!-- ie6/7 micro clearfix -->
<!--[if lte IE 7]>
<style>
.grouping {
*zoom: 1;
}
</style>
<![endif]-->
<!--[if IE]>
<script src="img/html5.js"></script>
<![endif]-->
</head>
这只是一个带有viewport名称的meta元素;我们稍后会回到这个问题。现在,让我们看看当我们刷新浏览器时会发生什么:
实际上我们得到了移动版本,所以这看起来好多了,解决了我们遇到的问题。
viewport 元标签的解剖
让我们来看看这个元标签:
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
在这里,我提供了一个带有值viewport的名称属性。然后,我有一个content属性,里面提供了一些不同的东西。第一件事是width=device-width。这基本上意味着“请不要在移动设备上缩小我的页面,因为我正在使用媒体查询来处理这个问题。谢谢!”第二件事是initial-scale=1.0,基本上是说这个-将其大小调整到设备的宽度,而不多。最后,我们有minimum-scale=1.0。这在您旋转手机时会有所帮助,因此网站在设备的宽度从纵向模式更改为横向模式以及反之后仍然保持在设备的宽度。viewport元标签还有更多内容。我们可以添加user-scalable=no:
user-scalable=no这个术语不允许用户在手机上放大或缩小。拥有这个属性的网站可能会非常恼人,这就是为什么我们不会在我们的网站上包含它的原因。
总之,我建议将viewport元标签添加到您的网站的模板中,以便在每个网站上使用。实际设备测试没有替代品,因为实际手机和模拟器永远不会完全相同。
总结
在这一章中,我们涵盖了响应式网页设计的核心概念,这将使您的网站在任何设备上都能看起来很好。您学到了流体网格和灵活图片是使网站适应所有屏幕尺寸的第一步。我们现在了解了媒体查询如何确保网站在较窄的宽度下看起来很好。我们还使用 jQuery 创建了一个移动菜单,以便在点击时触发菜单。最后,我们在 Chrome 的移动设备模拟器上测试了我们的设计,并学会了如何使用viewport元标签来确保我们的网站在移动设备上具有响应性。我强烈建议您在自己使用这些技术时,从设计过程的一开始就考虑移动体验。在下一章中,我们将讨论网络字体。