7.2 页眉
先从页眉(header
)开始吧。
<header>
<section id="title">
<h1>Stylin’ with CSS</h1>
<h2>The Blog and Books of Charles Wyke-Smith</h2>
</section>
<nav class="menu">
<ul>
<li class="choice1"><a href="#">Articles</a></li>
<li class="choice2"><a href="#">Books</a></li>
<li class="choice3" ><a href="#">Resources</a></li>
<li class="choice4"><a href="#">Bookshelf</a></li>
<li class="choice5"><a href="#">Contact Me</a></li>
</ul>
</nav>
<form class="search" action="#" method="post">
<label for="user_name">search</label> <!-- 标注的for属性与文本框ID相同 -->
<input type="text" id="user_name" name="user_name" placeholder="search" />
</form>
</header>
页眉的标记分三部分:页面标题(section#title
)、搜索表单(form.search
)和导航菜单(nav.menu
)。
7.2.1 页面标题
我们把h1
和h2
元素定位在了header
元素的左上角。以下是CSS。
- header {
- position:relative; /*为页面标题和搜索表单提供定位上下文*/
- height:70px; /*固定高度,包围绝对定位元素*/
- margin:10px 0;
- background:#fff;
- border-radius:20px 0px 20px 0px; /*顺序:左上、右上、右下、左下*/
- box-shadow:0 12px 8px -9px #555; /*负扩展值把阴影定位到盒子内部*/
- padding:1px; /*防止子元素外边距叠加*/
- }
- header section#title {
- position:absolute;
- width:300px; /*宽到足以不让文本折行*/
- height:65px; /*高到足以容纳两行文本*/
- left:0px; /*左上角定位*/
- top:0;
- }
- header h1 {
- padding:9px 12px 0;
- font-family:'Lato', helvetica, sans-serif;
- font-weight:900;
- font-size:2.2em;
- line-height:1;
- letter-spacing:-.025em;
- color:#4eb8ea;
- }
- header h2 {
- padding:0px 12px;
- font-family:"Source Sans Pro", helvetica, sans-serif;
- font-weight:400; /*设定下载字体的粗细*/
- font-size:.9em; line-height:1;
- letter-spacing:-.025em;
- color:#333;
- }
图7-3 为h1
和h2
设定了样式,临时添加的边框显示了它们的位置
这里要注意的第一件事,就是明确地为header
设定了固定高度。在前面的例子中,我们一直提倡让内容本身决定结构化元素的高度,让高度随内容增多而扩展。可是在这里,由于header
包含的是绝对定位的元素,该元素不会影响父元素高度,所以必须明确设定这个高度。header
的内容是极少会改动的,因此未来也不大可能出现内容溢出header
的情况。
其次,就是我把“两个圆角,两个方角”的样式,应用给了header
及页面中的很多其他元素。这种不张扬但又与众不同的效果,为页面赋予了独特的外观。要了解如何为HTML元素盒子应用圆角,请参考附注栏“弧形角”。
接着,给section#title
设定了绝对定位,在确定其文本内容的大小和内边距之后,又给它设定了足以包含这些文本的固定宽度和高度。
对于这里标题的文本,我们使用了Google Web字体Lato。而在设定Google Web字体粗细的时候, font-weight
属性的值使用是数字值。这跟使用系统安装的字体不一样,对于系统安装的字体,浏览器通常只能显示font-weight
的normal
和bold
两个值。要了解有关Google Web字体的更多信息,请回顾第4章。
另外一处别出心裁的设计,就是盒子的阴影。我们的CSS会控制这些阴影只显示在盒子底部,而且比盒子自身窄一些。这样,盒子就好像在页面上悬空一样。前面代码中加粗了相应声明,有关盒阴影的更多信息请参考附注栏“盒阴影”。
页面标题就位之后,接下来我们再采取类似的方法把搜索框定位到页眉区域右侧。
弧形角
圆角效果可是几年前Web 2.0网页的招牌式设计。当时,实现简单的圆角要用复杂的JavaScript,或者得用嵌套的DIV和丝毫不差的图片定位。而现在,则只要一行CSS就能搞定了。
最简单语法形式如下。
- border-radius:10px;
这样,盒子的四角都会变成半径为10像素的圆角。
如果要单独设定每个角的半径,也可以在上面的简写属性中按顺序依次指定。只不过,现在指定的是角而不是边,所以“上、右、下、左”的顺序就不适用了,而是要改用“左上、右上、右下、左下”。
另外,也可以像下面这样分别设定水平和垂直半径:
- border-radius:10px / 20px;
图7-4 前面两个CSS声明的效果示例图7-4也说明了为什么管这种效果叫弧形角了,即我们设定的值表示的是位于角内部的圆形或椭圆形的半径。
如果你想给每个角都设定不同的水平和垂直半径,写法如下:
- border-radius:10px 6px 4px 12px / 20px 12px 8px 24px; / 4个水平值,4个垂直值 /
最后提醒大家,弧形角不一定要通过边框才能显示出来。大家看到图7-1中的菜单了吗,菜单的圆角就是通过元素的背景色而非边框显现出来的。
盒阴影
HTML元素的阴影,同样是CSS3被广泛实现之前很难做出来的一个效果。当时,要给元素添加阴影效果,必须用图片和DIV配合,还要耐心地调整,而现在则只要一行CSS声明即可。
最简单语法形式如下。
- box-shadow:4px 4px 5px 8px #aaa inset;
box-shadow
属性的这几值分别代表:水平偏移量、垂直偏移量、模糊量、扩展量、颜色、阴影位于边框内部(默认位于边框外部,即outset
)。最低限度,必须设定水平偏移量、垂直偏移量和颜色,这样会得到一个与元素宽度和高度大小一致且为指定颜色的硬边阴影。如果水平和垂直偏移量是负值,阴影就会出现在元素左上方。
inset
关键字会把阴影放到盒子内部。另外,box-shadow
还支持多个阴影声明。图7-5展示了一些阴影示例。
图7-5 使用正、负偏移量,实现了各种不同的阴影效果
7.2.2 搜索表单
还是先温习一下标记吧。
<form class="search" action="#" method="post">
<label for="user_name">search</label>
<input type="text" id="user_name" name="user_name" placeholder="search" />
</form>
以下其实就是上一章搜索表单示例的CSS。唯一明显不同的,就是表单在页眉中定位的方式。
- form.search {
- position:absolute; width:150px; /*宽到足以容纳扩展后的搜索框*/
- top:23px; right:20px; /*相对于页眉右上角定位*/
- }
- .search input {
- float:right; width:70px;
- padding:2px 0 3px 5px;
- border-radius:10px 0px 10px 0px;
- font-family:"Source Sans Pro", helvetica, sans-serif;
- font-weight: 400; font-size:1em;
- color:#888;
- outline:none; /*去掉默认的轮廓线*/
- -webkit-transition:2s width; /*搜索框过渡动画,别忘了带其他厂商前缀的属性*/
- }
- .search input:focus { width:140px;}/*在获得焦点时扩展到这么宽*/
- .search label{display:none;}
- form.search input {background-color:#fff;}
- form.search input::-webkit-input-placeholder {color:#ccc;}
图7-6 搜索框已经扩展到最大宽度,其边框是临时添加的
上面加粗的代码表明,我们为表单元素设定了宽度,并将它的右边相对于页眉的右边定位。如图7-6中临时添加的边框所示,我们为表单设定了足够的宽度,让它能够容纳扩展后的输入框。关于搜索表单我不想在这儿说太多,因为上一章已经详细地讲过了。但有一点我想强调,就是——至少在WebKit浏览器中——可以给占位符文本设定不同样式,让它有别于用户输入的文本。如何设定请参见前面代码最后一行。
7.2.3 菜单
现在,页眉两端有了两个绝对定位的元素,这两个元素已经离开了常规文档流。接下来,我必须让菜单在它们之间居中。这里的菜单使用的也是第6章的菜单,只是少量修改了几个地方而已。
为节省版面,这里没有列出生成下拉菜单的所有嵌套的标记。如果你想回顾如何用CSS制作下拉菜单,请参考第6章。
<nav class="menu">
<ul>
<li class="choice1"><a href="#">Articles</a></li>
<li class="choice2"><a href="#">Books</a>
<!-- 更多菜单项 -->
</nav>
包含在nav
元素中的仍然是普通的链接列表,但现在每个列表项都有了不同的类,方便我们设定颜色。
以下是CSS。
- nav.menu {
- margin:19px auto;
- padding:0;
- text-align:center; /*在容器内居中菜单*/
- font-size:.8em;
- }
- nav.menu > ul { display:inline-block;} /*收缩包紧列表项*/
- nav.menu li {
- float:left; /* 让菜单项水平排列*/
- list-style-type:none; /*去掉默认的项目符号*/
- position:relative; /*为子列表提供定位上下文*/
- }
- nav.menu li a {
- display:block; /*让链接填满列表项*/
- padding:.25em .8em;
- font-family:"Source Sans Pro", helvetica, sans-serif;
- font-style: normal;
- font-weight:600;
- font-size:1.2em;
- text-align:left;
- color:#fff;
- text-decoration:none; /*去掉链接的下划线*/
- -webkit-font-smoothing: antialiased; /*在WebKit浏览器中平滑字体*/
- }
- nav.menu li.choice1 a {background:#f58c21;}
- nav.menu li.choice2 a {background:#4eb8ea;}
- nav.menu li.choice3 a {background:#d6e636;}
- nav.menu li.choice4 a {background:#ee4c98;}
- nav.menu li.choice5 a {background:#f58c21;}
- nav.menu li:hover > a {
- color:#555;
- border-color:#fff;
- border:0;
- }
- nav.menu li:last-child a {border-bottom-right-radius:10px;}
- nav.menu li:first-child a {border-top-left-radius:10px;}
图7-7 菜单在页眉内居中了。由于左右两端的页面标题和搜索表单都因绝对定位而脱离了文档流,所以nav
元素是与布局同宽的(如临时添加的边框所示)
页面标题和搜索表单是绝对定位,因而离开了常规文档流。换句话说,nav
元素就会当它们不存在,而在水平方向上扩展填充父元素header
(图7-7)。这样我就可以让菜单在页面上居中。下面就以此为例,再讲一些使用CSS居中元素的技巧。
居中没有宽度的元素
在一个元素内居中另一个元素有时候会很困难。对于常规、静态定位的元素,可以让它向左或向右浮动,或者使用text-align
属性让它在父元素内居左、居右或居中。还可以利用自动外边距(margin:0 auto
)来居中元素。这些方法的问题在于,要居中的元素必须是有宽度的。像这里用于构成菜单的HTML列表,它可能是根据数据库信息动态生成的,或者说将来有可能手工编辑,总之你不可能提前设定它的宽度。我收到很多邮件,都是询问怎么在容器内部居中菜单的。下面也算是统一回答吧,我们来看看怎么居中一个没有设定宽度的元素。
在display
属性的值中,inline-block
具有一些特殊的混合行为。正如它的名字所暗示的,它既有块级元素的特点,也有行内元素的行为。从块级元素角度说,可以为它设定外边距和内边距,也可以通过它简便而有效地包围其他块级元素。从行内元素角度看,它会收缩包裹自己的内容,而不是扩展填充父元素。换句话说,inline-block
元素的宽度始终等于其内容宽度。这种元素还有一个特点,就是可以包围浮动元素。不过,这种元素也有一个问题,即不能给它的外边距设定auto
值——而这恰恰又是在更大的容器内居中元素的最简单方法。
解决方案就是为要居中元素的父元素(这里的nav
)应用text-align:center
,为要居中的元素(这里的ul
)设定display:inline-block
,让它包含列表项。这样设定就可以得到我们想要的结果:没有固定宽度的元素也能在其父元素内居中。如前面代码开头加粗的CSS声明所示,我们就是这么做的。现在菜单完美居中了,因为其父元素nav
忽略了两端绝对定位的元素,扩展到了与header
同宽。
为了演示这个技巧有多好,我们来去掉一个菜单项。
图7-8 即使去掉菜单项,菜单整体上仍然居中
如图7-8所示,去掉最后一个菜单项之后,整个菜单照样居中。对于不同用户会看到不同菜单(比如,区别是否为注册会员)的动态站点来说,这个技巧的确太有用了。还有一个地方要特别留意,就是我没有直接为第一和第五个菜单项添加圆角效果,否则将来如果增减菜单项就会出问题。如前面代码末尾加粗的声明所示,为了让CSS适应菜单项的动态变化,我们把圆角效果应用给了:first-child
和last-child
元素。这样一来,第五个菜单项被删掉后,第四个菜单项就会成为最后一个子元素,从而取得应用给:last-child
的圆角效果。
添加下拉菜单
下面,利用添加下拉菜单的机会,我来给大家介绍另一种CSS过渡效果。
- nav.menu li ul {
- opacity:0; visibility:hidden; /* 隐藏下拉菜单*/
- position:absolute; /*相对于父菜单定位*/
- width:12em; /*下拉菜单宽度*/
- left:0; /*左边与父菜单项左边对齐*/
- top:100%; /*顶边与父菜单项底边对齐*/
- -webkit-transition: 1s all; /*设定过渡效果*/
- -moz-transition: 1s all;
- transition: 1s all;
- }
- nav.menu li:hover > ul {
- opacity:1; visibility:visible; /*两个属性都会产生过渡动画*/
- }
- nav.menu li li {
- float:none; /*去掉继承的浮动,让菜单项上下堆叠*/
- }
- nav.menu li li:first-child a {border-radius:0;}
- nav.menu li li:last-child a {border-bottom-left-radius:10px;}
- .no-csstransitions nav.menu li ul { /*针对不支持过渡的浏览器*/
- visibility:visible; /*覆盖过渡声明*/
- opacity:1; /*覆盖过渡声明*/
- display:none; /*如果不支持过渡,就直接隐藏菜单*/
- }
- .no-csstransitions nav.menu li:hover > ul {
- display:block; /*在父菜单项悬停时显示菜单*/
- }
图7-9 添加了下拉菜单
这里的代码也是第6章相应例子中的代码,所以如果有什么问题,请大家参考第6章以及前面CSS中的注释。不过,关于这里的菜单,我还想跟大家解释三点。
首先,下拉菜单会继承圆角样式。但对于下拉菜单来说,我们不想像顶级菜单一样把该样式应用到对角上,而只想应用给最后一个菜单项下面的两个角。所以,我们覆盖了继承的左上角的圆角边框,又给左下角添加了圆角边框样式。当然,右下角的圆角边框仍然是继承来的(图7-9)。
其次,代码注释里也提到了,我们为下拉菜单应用了不透明度过渡,让它产生淡入效果。我一开始只使用了opacity
属性,把它的起始值设定为0(透明,不可见),终止值设定为1(不透明,完全可见)。这样确实可以在鼠标悬停时让下拉菜单淡入淡出,但即使不可见的时候,它也会在那里。换句话说,鼠标移动到顶级菜单项下方,甚至还没有到那一项时,其下拉菜单就会淡入。后来,我又尝试添加display:none
和display:block
,即在非悬停状态完全去掉下拉菜单。这倒是解决了下拉菜单“占位”的问题,但过渡效果就没有了,下拉菜单会突然出现、消失。然后,我又放弃display
,改为在悬停状态关掉visibility
属性,同时过渡opacity
。结果是下拉菜单淡入,但会突然消失。最后,还是同时过渡opacity
和visibility
属性解决了问题——菜单在透明时消失无形,淡入和淡出都正常了。说了这么多,主要是想告诉大家,有时候要得到某个结果,总免不了要多试验几次。不过我还是很欣慰的,因为我的试验能让大家节省好几个小时。大家就不用跟我客气啦!
第三,我还利用这个机会向大家展示了Modernizr在实际中的应用,通过它为那些不支持CSS3过渡的浏览器提供了后备CSS声明。具体来说,在不支持过渡的浏览器中,Modernizr会在页面加载后为顶级HTML标签添加no-csstransitions
类。我们可以把这个类名放到选择符里,写几条只有不理解CSS过渡的浏览器才会用的规则。在这些规则里,去掉在支持过渡的浏览器中控制菜单的visibility
和opacity
声明,为能力欠缺的浏览器提供基本的display:none
和display:block
声明,实现第6章那种简单的显示和隐藏菜单的效果。
到现在为止,header
部分已经完事了。下一节,我们学习怎么为功能区设定样式,这个区域包括博客文章、登录表单和博文链接。
垂直居中
用CSS实现垂直居中也不简单。如果你想在一个固定高度的元素内垂直居中一行文本,可以把这一行文本的
line-height
设定为该元素的高度。假设元素高度为300像素,可以这样写:
text-align:center; / 水平居中/
line-height:300px; /垂直居中:行高=容器高度/
如果想垂直居中其他元素,比如图片,可以将容器的
display
属性设定为table-row
,再设定(只对单元格有效的)vertical-align
属性为middle
,比如:
display:table-cell; /借用表格的行为/
vertical-align:middle; /垂直居中/
text-align:center; /水平居中/
尽管这些方案都不够自然,但CSS没提供什么垂直定位元素的属性,也就只能这么将就了。