Open TanXinNiao opened 3 years ago
菜单由一组链接组成。用 HTML中的列表元素( ul 或 ol )来分组链接不仅符合逻辑, 而且即使没有额外的 CSS也能适当显示链接的层次。默认情况下,由于列表项( li ) 是块级元素,因此它们会上下堆叠。
<nav class="list1">
<ul>
<li><a href="#">Alternative</a></li>
<li><a href="#">Country</a></li>
<li><a href="#">Jazz</a></li>
<li><a href="#">Rock</a></li>
</ul>
</nav>
以上标记给每个列表项的文本都加了链接标签,让它们都变得可以点击。(每个链接 中的 # 是 URL 占位符,将来可以替换成真正的 URL。)
/*去掉默认的内边距和外边距*/
* {
margin: 0;
padding: 0;
}
/*设定本例中菜单的大小和位置*/
nav {
margin: 50px;
width: 150px;
}
/*给菜单加上边框*/
.list1 ul {
border: 1px solid #f00;
border-radius: 3px;
padding: 5px 10px 3px;
}
/*去掉项目符号并为链接添加间距*/
.list1 li {
list-style-type: none;
padding: 3px 10px;
}
/*“非首位子元素”选择符*/
.list1 li+li {
border-top: 1px solid #f00;
}
/*为链接添加样式*/
.list1 a {
text-decoration: none;
font: 20px Exo, helvetica,
arial, sans-serif;
font-weight: 400;
color: #000;
background: #ffed53;
}
/*悬停高亮*/
.list1 a:hover {
color: #069;
}
前面的 CSS代码中有两点特别值得一说。首先,加粗的 li + li 选择符的意思是“任 何跟在 li 之后的 li ”。
当然,也有 实现相同效果的其他方法,比如:
/*给所有 li 上方添加一条边框*/
li {border-top:1px solid #f00;}
/*去掉第一个 li 上方的边框*/
li:first-child {border-top:none;}
我为每个链接都加上了背景色,以便显示相应链接可点击区域的大小。 目前只有文本是可以点的,因为链接( a )是行内元素,它会收缩并包住 其中的文本。然而,更好的用户体验是让列表项所在的整行都能点击。
此外,在每个 li 元素的上方和下方,各有 3像素的内边距,导致链接文本与列表项 边框之间有了距离。正因为有了这段距离,当用户鼠标移动到链接间的间隙时,光 标会从“悬停于链接之上”时的小手形状,变成常规的箭头形状。
* {
margin: 0;
padding: 0;
}
nav {
margin: 50px;
width: 150px;
}
.list1 ul {
border: 1px solid #f00;
border-radius: 3px;
padding: 5px 10px 3px;
}
.list1 li {
list-style-type: none;
padding: 3px 10px;
}
.list1 li+li a a {
border-top: 1px solid #f00;
}
.list1 a {
display: block;
padding: 3px 10px;
textdecoration:
none;
font: 20px Exo, helvetica, arial, sansserif;
font-weight: 400;
color: #000;
background: #ffed53;
}
.list1 a:hover {
color: #069;
}
把选择符 li + li 变成 li + li a ,就可以把上边框添加到列表项后面的列表项所包 含的链接元素上
而且,我还把内边距从列表项转移到了链接内部。这样,链接上下就互相接触了,中间没了间隙,鼠标 经过时光标状态也不会变了。最后,还要把每个链接的 display 属性设定为 block , 这样链接元素的盒子就会由“收缩包围内容”变成“扩展填充父元素”。换句话说, 链接的可点击区域将扩大到整个列表行。
<nav class="list1">
<ul>
<li><a href="#">Shirts</a></li>
<li><a href="#">Pants</a></li>
<li><a href="#">Dresses</a></li>
<li><a href="#">Shoes</a></li>
<li><a href="#">Accessories</a></li>
</ul>
</nav>
.list1 ul {
/*强制 ul 包围浮动的 li 元素*/
overflow: hidden;
}
.list1 li {
/*让 li 元素水平排列*/
float: left;
/*去掉项目符号*/
list-style-type: none;
}
.list1 a {
/*让链接填满 li 元素*/
display: block;
padding: 0 16px;
/*去掉链接的下划线*/
text-decoration: none;
color: #999;
}
.list1 li+li a {
border-left: 1px solid #aaa;
}
.list1 a:hover {
color: #555;
}
为方便设定菜单样式,我为列表容器 nav 添加了 multi_drop_menu 类。这个菜单的每一条CSS规则都以 .multi_drop_menu 开头,以确保它们只会应用给带有这个类的容器。 下拉菜单的标记是在前面菜单例子基础上扩充而来的。下面就是所有标记,虽然看 起来挺吓人,但实际上不过是一个简单的三层嵌套列表而已。
<nav class="multi_drop_menu">
<!-- 一级开始 -->
<ul>
<li><a href="#">Power</a></li>
<li><a href="#">Money</a></li>
<li><a href="#">Love</a></li>
<li><a href="#">Fame</a>
<!-- 二级开始 -->
<ul>
<li><a href="#">Sports Star</a></li>
<li><a href="#">Movie Star</a></li>
<li><a href="#">Rock Star</a>
<!-- 三级开始 -->
<ul>
<li><a href="#">Bruce Springsteen</a></li>
<li><a href="#">Bono</a></li>
<li><a href="#">Mick Jagger</a></li>
<li><a href="#">Bob Dylan</a></li>
</ul>
<!-- 三级结束 -->
</li>
<li><a href="#">Web Designer</a></li>
</ul>
<!-- 二级结束 -->
</li>
</ul>
<!-- 一级结束 -->
</nav>
想嵌套更多子菜单也非常简单,方法是在标 记中找到相应链接,把包含子菜单项的无序列表加在链接之后、包含它的 li 元素结 束标签之前。一会儿你就知道了,这样做无须更改 CSS。下面就从创建水平的顶级 菜单开始吧。
顶级菜单 以下是顶级菜单的 CSS(请配合注释理解它们的作用):
/*添加视觉样式*/
.multi_drop_menu {
font: 1em helvetica, arial, sans-serif;
}
.multi_drop_menu a {
/*让链接充满列表项*/
display: block;
/*文本颜色*/
color: #555;
/*背景颜色*/
background-color: #eee;
/*链接的内边距*/
padding: .2em 1em;
/*分隔线宽度*/
border-width: 3px;
/*可以有颜色,也可以透明*/
border-color: transparent;
}
.multi_drop_menu a:hover {
/*悬停时文本颜色*/
color: #fff;
/*悬停时背景色*/
background-color: #aaa;
}
.multi_drop_menu a:active {
/*点击时背景变色*/
background: #fff;
/*点击时文本变色*/
color: #ccc;
}
/*添加功能样式*/
.multi_drop_menu * {
margin: 0;
padding: 0;
}
/*强制 ul 包围 li*/
.multi_drop_menu ul {
float: left;
}
.multi_drop_menu li {
/*水平排列菜单项*/
float: left;
/*去掉默认的项目符号*/
list-style-type: none;
/*为子菜单提供定位上下文*/
position: relative;
}
.multi_drop_menu li a {
/*让链接填充列表项*/
display: block;
/*给每个链接添加一个右边框*/
border-right-style: solid;
/*背景只出现在内边距区域后面*/
background-clip: padding-box;
/*去掉链接的下划线*/
text-decoration: none;
}
.multi_drop_menu li:last-child a {
border-right-style: none;
}
/*临时隐藏低级菜单*/
.multi_drop_menu li ul {
display: none;
}
对于图 6-6 的 CSS 样式,首先要注意的是,我把菜单的视觉样式与功能样式分开来 写了。视觉样式控制字体大小、边框和文本的颜色,功能样式控制菜单的布局和行 为。两者的区别通过代码注释是很容易分辨的。对于下拉菜单这么复杂的组件,分 开来写视觉和功能代码是非常值得提倡的。这样,将来如果有人要修改菜单外观, 只要修改它的视觉样式就好了,而功能样式可以原封不动。
如果你在代码中像这样把视觉样式与功能样式分开来写,那一定要用注释说明为 什么
目前,最明显的变化还是 li 通过浮动由垂直堆叠变成了水平排列。至于为了让 ul 包围列表项,没有使用 overflow:hidden ,而是使用了 float:left ,是因为前者会导 致后来添加到下拉菜单中的子菜单无法显示——它们最终会显示在父元素 ul 的外 面,结果会因为“溢出”(overflow)而被隐藏(hidden)。
为了保证用户体验,所有视觉样式——内边距、背景、边框,等等,都要应用给链 接 a ,而不要应用给 ul 或 li ,以便热区(可点击区域)最大化,让用户鼠标经过时 不会产生前面例子中看到的状态切换。为达到这个目的,同时还要从视觉上分隔链 接,我们使用了 background-clip:padding-box 声明,这样就可以阻止链接的背景(像 常规状态下一样)延伸到边框后面。然后,让边框透明(也可显示为其他实色),在 链接之间产生间隙,让后面的页面能够透过边框被看到。如此一来,不用外边距也 能分隔链接,而且鼠标从一个菜单项移动到另一个菜单项时,也不会出现光标切换。
菜单项之间从视觉上是分开,但实际上却是紧挨在一块的。
或许你注意到了,我给 li 元素应用了 position:relative ,这是给添加子菜单作准备 的,在当前这一步里实际上它没有什么效果。另外,最后一行 CSS 隐藏了子菜单, 以防显示它们影响我们创建顶级菜单。在后面两步中,我会告诉你怎么在菜单项处 于悬停状态时显示它们。
第一步为顶级菜单添加的样式都会被次级菜单继承。换句话说,需要应用给子菜单 的样式大部分都已经写完啦!
菜单的下拉部分 虽然子菜单继承顶级菜单的样式为我们提供了方便,但其实有些样式在子菜单中并 不是必要的。比如,(列表的第二级)下拉菜单是垂直堆叠的,所以我们不希望其中 的列表项继承浮动而变成水平排列。另外,顶级菜单使用右边框作为分隔线,而下 拉菜单需要使用上边框来分隔垂直堆叠的菜单项。这一步,我们还会绝对定位下拉 菜单,以便它能精确地对齐父元素(包含子菜单的 li ),而这个父元素已经在上一步 被设定成相对定位了。
与此同时,为了看到这些设定的效果,还要显示二级下拉菜单,并继续隐藏三级下 拉菜单(下一步再考虑它)。
/* 添加的视觉样式 */
/*二级菜单宽度*/
.multi_drop_menu li ul {
width: 9em;
}
.multi_drop_menu li li a {
/*去掉继承的右边框*/
border-right-style: none;
/*添加上边框*/
border-top-style: solid;
}
/* 添加的功能样式 */
.multi_drop_menu li ul {
/*临时显示二级下拉菜单*/
display: block;
/*相对于父菜单项定位*/
position: absolute;
/*左边与父菜单项对齐*/
left: 0;
/*顶边与父菜单项底边对齐*/
top: 100%;
}
.multi_drop_menu li li {
/*停止浮动,恢复堆叠*/
float: none;
}
.multi_drop_menu li li ul {
/*继续隐藏三级下拉菜单*/
display: none;
}
这一步成功的关键是下拉菜单的绝对/相对定位。通过将其顶边位置( top )设定为 100% (相对于其相对定位的父元素 li ),其顶边会与父元素底边恰好对齐。它与父元 素之间的间隙,实际上是下拉菜单中第一个链接的边框
让下拉菜单响应鼠标事件 接下来是好玩的部分了,即要让下拉菜单拥有自己的功能。换句话说,它一开始是 隐藏的,只有在其父元素处于鼠标悬停状态时,才需要显示
.multi_drop_menu li ul {
/*隐藏二级下拉菜单*/
display: none;
/*相对于父菜单项定位*/
position: absolute;
/*左边与父菜单项对齐*/
left: 0;
/*顶边与父菜单项底边对齐*/
top: 100%;
}
.multi_drop_menu li:hover>ul {
/*父元素悬停时显示*/
display: block;
}
让菜单起作用的关键在于先把它藏起来:
/隐藏二级菜单/
li ul {display:none;}
然后,再在父元素悬停时把它显示出来:
/显示二级菜单/
li:hover > ul {display:block;}
最后一行 CSS 的意思是:当鼠标移动到列表项上时,就显示它的子列表。注意,这
里的 :hover 触发器是设定在 li 元素而非链接自身上的。这样做是因为我们想要显示
li 的子元素 ul ,而它不是想链接的子元素。此外,为了只显示其子元素,悬停列表
项与子列表之间还有一个子选择符 >。如果没有这个子选择符,
当顶级菜单项处于悬停状态时,会同时显示二级和三级菜单。
添加三级菜单 由于前面包含 :hover 的 CSS规则会像应用给二级菜单一样,应用给三级菜单,所以 在父元素处于鼠标之下时,三级菜单自然也会显示出来。可是,图 6-9中的三级菜单 被其父元素挡住了。这时候三级菜单与其二级父元素的位置是什么关系?没错,就 是二级菜单与顶级菜单的关系。所以,三级菜单跑到了鼠标下的二级菜单后面去了, 而且其第一项顶边与悬停的父菜单项底边是对齐的。从图中可以看到,三级菜单第 一项“Bruce Springsteen”的上半部分被二级菜单最后一项“Web Designer”给盖 住了。
我们在这一步要做的,就是把三级菜单放到二级菜单右侧,让它的顶边与鼠标所在 菜单项的底边对齐。
.multi_drop_menu li li ul {
/*相对于父菜单定位*/
position: absolute;
/*与父菜单右侧对齐*/
left: 100%;
/*与父菜单项顶边对齐*/
top: 0;
}
你可以在顶级添加更多列表项,在标记中添加更 多子列表,然后无须多写一行 CSS,它们就能构成新的下拉菜单。此时,我还想再 作两个调整。首先,为了真正让这些代码有用,而且可以重用,需要再写一些样式, 让顶级菜单能够垂直显示,以便能将其用在导航侧边栏里。为此,我得先给 nav 容 器添加第二个类 vertical 。
<!-- HTML 类名之间要有空格 -->
<nav class="multi_drop_menu vertical">
这样,我就可以写一些样式,让它们只在导航容器有 vertical 类的情况下才起作用。 所以,这些新增规则的选择符开头都是
/*CSS 类名之间没有空格*/
.multi_drop_menu.vertical
这意味着选择一个带有两个类的元素——注意,CSS的类名间没有空格。
/*顶级垂直菜单宽度*/
.multi_drop_menu.vertical {
width: 8em;
}
.multi_drop_menu.vertical li a {
border-right-style: none;
border-top-style: solid;
}
.multi_drop_menu.vertical li li a {
border-left-style: solid;
}
.multi_drop_menu.vertical ul,
.multi_drop_menu.vertical li {
/* 让顶级菜单垂直显示*/
float: none;
}
.multi_drop_menu.vertical li ul {
/*子菜单左边与上一级菜单右边对齐*/
left: 100%;
/*子菜单顶边与上一级菜单项顶边对齐*/
top: 0;
}
为了让菜单恢复默认的堆叠状态,这里重置了顶级 li 元素及其父元素 ul 的浮动属 性,后者原来浮动是为了包围浮动的 li 元素。
这里还为 nav 容器设定了宽度。如果不设定导航容器宽度,那么 nav 及其包含的顶级 菜单项都会尽可能伸展。最后,我们把二级菜单与顶级菜单的位置关系,设定成了 前面三级菜单和二级菜单之间的关系,即让子菜单顶边与父菜单项顶边对齐。当然, 还去掉了顶级菜单项的右边框,代之以上边框 增加了垂直菜单样式后,顶级菜单项上下堆叠起来了
突出显示选择路径 只有位于鼠标下方的元素才会突出显示。为了让用户明确地知道自己 是怎么一路选择下来的,还需要让每一级菜单中被选择的元素突出显示。实际上, 很简单,只要把 .multi_drop_menu a:hover 替换成以下 CSS即可。
.multi_drop_menu li:hover>a {
/*悬停时的文本颜色*/
color: #fff;
/*悬停时的背景颜色*/
background-color: #aaa
}
这个小小的替换能够起作用,是因为 :hover 事件会沿着元素的结构层次“向上冒泡”。 所以,把 :hover 设定在 li 元素,就相当于也把它设定给了所有祖先 li 元素。然后, 只要再给其子元素(链接)设定样式即可。这个改进能够极大地提高菜单的易用性, 而我们的下拉菜单到此也讲完了
有了以上 CSS 文件,只要把它链接到页面中,并给一个包含无序列表的容器加上 multi_drop_menu 类,该列表就会摇身一变,成为一个全功能的菜单,这个魔术我会 在下一章变给你看。
表单与其他页面元素的作用不同。其他元素是把服务器发过来的内容显示给用户, 而表单则是把用户的信息发送给服务器。
HTML5 与表单 HTML5 为 input 元素新增了 13 种新类型(type 属性),也对表单进行了大幅增强,包括特别 特别棒的 placeholder 属性,可以在文本框中显示说明文字(例如“在这里填写用户名”), 只要用户一开始输入它就会消失。
我们先把这个表单的所有标记展示一下,然后再讨论其中个别的 HTML 元素。
<!-- 必要的 form 标签 -->
<form class="stylin_form1" action="process_form.php" method="post">
<h3>A Stylin' Form</h3>
<!-- 控件组,即各种控件的容器 -->
<fieldset>
<!-- 控件组的文字说明,或标题 -->
<legend><span>Part 1 • Basic Controls</span></legend>
<!-- 开始单行文本输入控件 -->
<section>
<p class="note">* indicates required field</p>
<!-- for 属性把标注与控件关联起来,它的值必须与控件 ID 值相同 -->
<label for="user_name">User Name<span> *</span></label>
<!-- text 属性让这个控件可以输入文本 -->
<input type="text" id="user_name" name="user_name" />
<p>Please select a user name</p>
</section>
<!-- 开始密码控件 -->
<section>
<label for="password">Password<span> *</span></label>
<!-- 密码文本显示为掩码 -->
<input type="password" id="password" name="password" maxlength="20" />
<p>Password must be 8 or more characters</p>
</section>
<!-- 开始多行文本输入控件 -->
<section>
<label for="description">Description</label>
<textarea id="description" name="description" placeholder="Enter the description here."></textarea>
</section>
<!-- 开始 HTML5 日期控件 -->
<section>
<label for="description">Date</label>
<input type="date" id="special_date" name="event_date" min="2012-09-05" />
</section>
</fieldset>
<fieldset>
<legend><span>Part 2 • Multiple-Choice Controls</span></legend>
<!-- 开始复选框 -->
<section>
<h4>Select Any Number</h4>
<section>
<input type="checkbox" id="check1" name="checkset" value="1" tabindex="4" />
<label for="check1">Choice 1</label>
</section>
<section>
<input type="checkbox" checked="checked" id="check2" name="checkset" value="2" />
<label for="check2">Choice 2 is pre-checked</label>
</section>
<section>
<input type="checkbox" id="check3" name="checkset" value="3" />
<label for="check3">Choice 3—add as many as you need!
</label>
</section>
<p>You must choose one or more</p>
</section>
<!-- 开始单选按钮 -->
<section>
<h4>Select Only One</h4>
<section>
<input checked="checked" id="radio1" name="radioset" type="radio" value="Choice_1" />
<label for="radio1">Choice 1 is pre-selected and shows the text
wraps nicely if it goes to multiple lines.</label>
</section>
<section>
<input id="radio2" name="radioset" type="radio" value="Choice_2" />
<label for="radio2">Choice 2</label>
</section>
<section>
<input id="radio3" name="radioset" type="radio" value="Choice_3" />
<label for="radio3">Choice 3</label>
</section>
</section>
<!-- 开始选项列表(下拉列表) -->
<section>
<label for="select_choice">Select Your Choice</label>
<select id="select_choice" name="select_choice">
<option value="0">None</option>
<option value="1">Choice 1</option>
<option value="2">Choice 2</option>
<option value="3">Choice 3</option>
<option value="4">Choice 4</option>
</select>
</section>
</fieldset>
<!-- 开始提交按钮 -->
<section>
<input type="submit" value="Submit This Form" />
</section>
</form>
form 元素 所有表单的标记都包含在一个 form 元素中。
<form class="stylin_form1" action="process_form.php" method="post">
<!-- 这里是表单标记 -->
</form>
form 元素有两个必要的属性: action 和 method 。 action 属性用于指定服务器上用来 处理表单数据的文件的 URL。 method (值要么是 post ,要么是 get )用于指定怎么 把数据发送到服务器。
提交表单 用户提交表单后,他们在表单控件中填写的表单数据或者做出的选择,都会被发送 到服务器。所谓控件,是对表单中用来收集数据的各种表单组件的通称,包括文本 框、复选框、单选按钮,等等。表单中的数据是以“名=值”的形式发送给服务器的, 比如“username=chrisconsumer”,每个控件都是这么一个名/值对形式。这里的“名” 就是你在控件 name 属性中设定的名字。“值”可能是用户在文本控件中输入的信息, 也可能是表示可选控件(如复选框)中某一项是否被选中的布尔状态值(比如某个 复选框中用 1 表示选中,用 0表示未选中)。
控件组 可以把一组相关的表单控件组织到一个控件组元素 fieldset 中。比如,在一个电商 网站上,用于收集用户姓名和地址的控件组,可能会加上一个标题叫“收货人信息”。 随后可能就是另一个用于填写信用卡信息的控件组。
前面表单标记使用了两个 fieldset 元素,把所有控件分成了两组。第一组包含基本 的标注和控件,第二组包含更复杂的控件,比如复选框( type="checkbox" )、单选按 钮( type="radio" )和选项列表( select )。 fieldset 的第一个子元素一定得是 legend 文本元素,其中包含这个控件组的标题。
<fieldset>
<legend>Mailing Address</legend>
<!--- 标注和控件 -->
</fieldset>
我给两个控件组的 legend 元素都加了灰色条,文本则反白显 示,从而明确表示它们是两个独立的表单区块。
控件与标注 表单包含一或多个控件。如前所述,控件就是让用户勾选、点选、输入的组件。对 于那些可以输入的控件,大家又俗称其为字段。每个表单控件( submit 按钮除外), 都有一个对应的 label 文本元素,用于描述控件代表的数据
<label for="user_name">User Name</label>
<!-- 创建一个文本字段 -->
<input type="text" id="user_name" name="user_name" />
<p>Please select a user name</p>
label 元素可以包含控件,也可以放在控件前头或后头。如果像前面那样没有用 label
包含控件,那么 label 的 for 属性与控件的 id 属性必须匹配,以便把两者关联起来。
不过,控件与标注之间的关系是隐式存在的,而且如果 label 像下面这样包含控件,
其 for 属性也不是必需的:
for 属性与 id 属性通过相同的值关联起来后,用户点击标注文本也可以选择单选按
钮和复选框。
<label>User Name <input type="text" name="user_name" /></label>
无论采用哪种方式关联标注与控件都没问题,重要的是一定要有这种关联。这种关 联对于屏幕阅读器等辅助技术解读表单非常关键。我个人习惯于把 label 放在控件 前面(当然对于复选框和单选按钮,还是要放在后面),因为这样更方便设定样式。
除了标记和控件,有时候还需要告诉用户如何填写或选择,特别是在需要填写特定 格式数据(如日期)的时候。HTML 中没有为表单控件说明规定什么标签,因此我 们就用 p 这个段落元素吧。另外,说明的文字最好方便在用户输入无效数据的情况 下突出显示(比如变成红色),这样就不用单独准备和控制错误消息了。本例后面会 介绍怎么做到这一点。
当然,如果你希望在用户输入无效数据时显示不同于说明的错误消息,可以再添加 一个 p 元素,并赋予它 .error 类。
控件类型 最常用的 HTML 表单元素是什么?你一定会说是 input 。一点都没错儿!这个元素 之所以常用,是因为它可以在屏幕上显示为多种不同的外观,并具有不同的行为。 文本框、复选框、单选按钮等的背后都是 input 元素,区别在于它们的 type 属性。 以下列出了 type 属性部分可能的值。
text :基本的单行文本框。 password :文本显示为掩码。 checkbox :复选框。 radio :单选按钮。 submit :提交表单的按钮。 time 、 date 、 search :HTML5文本框的变体。
有一个文本控件不是 input 元素的变体,那就是多行文本区 textarea 元素。另外, 在用户输入之前一直显示的占位符文本,是通过 placeholder 属性设定的。
复选框、单选按钮和选项列表 这三种表单元素比一般的控件/标注组合要复杂一些。 复选框可以让用户从多个备选项中选择一或多个项。每个备选项都是非排他型的, 即勾选同一组中的其他项,不会影响已经选择的项。
单选按钮限制用户只能从多个备选项中选择一项。每个备选项都是排他型的,即 勾选同一组中的另一项,就会取消对前一项的选择。 要特别注意一下,复选框和单选按钮都是成组出现的,分组的方法是为它们设定 相同的 name 属性。对于本例中的单选按钮,它们共同的名字叫 radioset 。这个组 中的每个单选按钮由其 value 属性唯一标识。在本例中,分别是 Choice_1 、 Choice_2 ,等等。假设用户选择了第一个按钮,那么发送到服务器的名/值对,就 是 radioset=Choice_1 。
选项列表( select 元素)会创建一个下拉列表,用户可以在其中点选。在 select 元素中,每个备选项都用 option 文本元素来生成。
因为 HTML 标准没有规定专门包含控件及其标注的元素,所以我就用块级 section 元素来充当这个角色。这样既方便组织控件,又方便为行内表单控件及其标注设定 样式。
基本的标注和控件 在前面的标记中,我把每个标注和控件都放到了一个 section 元素中,让 label 位于 控件之前。
<section>
<label>…</label>
<input />
<p>…</p> <!-- 控件使用说明 -->
</section>
通过把每一对标注和控件都放到块级 section 元素中,它们自然就在页面上垂直堆叠 起来了。而且,这样也有了一个可以定位它们的包含元素。
复选框和单选按钮 单选按钮和复选框是由很多标注/控件组成的,所以我把它们都放到了一个 section 元素中(便于为标注/控件设定基本的 CSS 样式)。然后,在这个元素里面,再把每 个复选框或单选按钮的标注/控件分别放到各自的 section 元素中。
<section>
<h4>Set Heading</h4>
<!-- 第一个单选按钮/复选框 -->
<section>
<input />
<label>…</label>
</section>
<!-- 第二个单选按钮/复选框 -->
<section>
<input />
<label>…</label>
</section>
<!-- 控件使用说明 -->
<p>…</p>
</section>
我把标注放到了控件后面,这样它才能显示在控件右侧。虽然每个控件(复 选框和单选按钮)都有一个对应的标注,但不能将它们用作整个控件组的标题。每 个 label 元素只能用于标注一个控件。为此,我就用一个 h4 元素来作为它们的标题。 如前所述,我还添加了一个 p 元素,用于显示控件使用说明。
首先,从表单的整体布局开始。换句话说,就是先设定 form 和两个 fieldset 元素的 样式。
form.stylin_form1 {
width: 14em;
/* 表单整体宽度 */
margin: 20px auto;
/* 在容器内居中 */
border: 1px solid #bbb7ae;
padding: .5em .5em .15em;
}
.stylin_form1 h3 {
/* 表单主标题 */
margin: 0;
padding: 0 0 .2em .2em;
font-weight: 600;
color: #bbb7ae;
}
.stylin_form1 fieldset {
/*包含控件与标注*/
margin: 0;
padding: 0 0 .2em 0;
width: 100%;
border: 0;
}
/* legend 元素的位置不同寻常,所以我把它的文本包含在一个块级 span 中,转而为 span
设定样式 */
.stylin_form1 legend {
width: 100%;
padding: .3em 0;
background: #bbb7ae;
/*灰色条*/
}
.stylin_form1 legend span {
/* 设定标题文本的样式 */
display: block;
font-size: 1em;
line-height: 1.1em;
padding: 0 0 0 .4em;
font-weight: 700;
color: #fff;
/*灰色条上的反白文本*/
}
通过为 form 设定内边距,它所包含的内容都与表单边界空开了距离。 很明显,我们给 legend 添加了灰色条,相应地把文本设定成了白色。 legend 元素默 认的位置是由浏览器内部的一种未加说明的机制确定的,并不是由浏览器样式表设 定的。因此,不可能通过 CSS 来精确控制它的位置。解决这个问题的方法,就是把 它的文本包含在一个 span 元素中,将该元素设定为 display:block ,然后再设定该元 素的位置。接下来,我们从宏观到微观,再看看怎么给控件组( fieldset )中的控件 和标注设定样式。
.stylin_form1 section {
overflow: hidden;
/* 强制 section 包含表单控件及标注 */
padding: .2em 0 .4em 0;
border-bottom: 8px solid #e7e5df;
/* 根据需要在每个 section 间增加间距 */
}
.stylin_form1 section:last-child {
/* 每组最后一个 section 没有边框 */
border-bottom: 0px;
}
.stylin_form1 section label,
/* 表单控件的标注 */
.stylin_form1 section h4 {
/* h4 是复选框和单选按钮组的标题 */
display: block;
clear: both;
margin: .3em .3em 0 0;
/* 右外边距确保标注文本在碰到 input 之前会换行 */
padding-bottom: .1em;
font-size: .8em;
font-family: 'Droid Sans';
font-weight: 400;
line-height: 1.1;
}
.stylin_form1 section label span,
/* 星号表示必填字段 */
.stylin_form1 section h4 span {
font-size: .75em;
vertical-align: text-top;
color: #f00;
}
.stylin_form1 section p.note {
/*说明星号是必填字段的文本*/
font-size: .7em;
color: #f00;
margin: 0;
padding: 0 0 .3em 0;
}
.stylin_form1 section input,
.stylin_form1 section textarea,
.stylin_form1 section select {
margin: .2em .5em .2em 0;
padding: .2em .4em;
/* 给 input 中的文本添加间距 */
color: #000;
box-shadow: 1px 1px 3px #ccc;
font-size: .8em;
/* 针对 Firefox - 没有这条声明会在 textarea 上使用 Courier */
font-family: inherit;
outline: none;
/* 去掉默认蓝色聚焦轮廓线 */
}
/* 设定文本字段(文本、密码、日期、文本区等)的样式,并加圆角 */
.stylin_form1 section input,
.stylin_form1 section textarea {
width: 12em;
/*设定字段宽度*/
border: 1px solid #bbb7ae;
border-radius: 3px;
/*圆角边框*/
}
.stylin_form1 section textarea {
height: 5em;
/* textarea 的高度 */
margin-top: .3em;
/* 与上面 label 的间距 */
line-height: 1.1;
}
.stylin_form1 section p {
/*控件使用说明*/
margin: .3em .75em 0;
clear: both;
font-size: .7em;
line-height: 1.1;
color: #000;
}
.stylin_form1 section p.error {
color: #f00;
/* 添加 error 类,把说明文字设定为红色 */
}
我把所有控件的可见盒子(类型为 text 、 date 、 textarea 和 select ) 都设定为相同宽度,为说明文字应用了样式,并给每个 section 元素添加了较宽的边 框,让它们从视觉上保持适当距离。
某些标注后面的星号表示相应字段是必填的,也就是用户不能不填。这个(用 Shift-8) 输入的星号被包含在 span 元素中,以便调整它与标注文本的相对位置,并应用不同 颜色。注意,我们还为控件使用说明段落设定了一条 p.error 规则。如果控件中数据 无效,这个类就会被添加到说明段落上。然后,说明文字会变成红色,提醒用户必 须输入有效数据才能提交表单。
好,下面该轮到复选框、单选按钮和选项列表了。这些控件全都位于第二个控件组 中。
.stylin_form1 section section {
/* 控件/标注的内包装 */
overflow: hidden;
/* 强制元素包围浮动标注 */
margin: .2em 0 .3em .4em;
padding: 0 0 .1em 0;
border-bottom: none;
}
.stylin_form1 section section input {
/*单选按钮或复选框*/
float: left;
clear: both;
width: auto;
/* 重设继承的宽度 */
margin: .1em 0 0em .3em;
/* 顶部与标注对齐,左侧防止 intput 溢出 */
}
.stylin_form1 section section label {
float: left;
clear: none;
/* 重设继承的值 */
width: 15em;
margin: .15em 0 0 .6em;
/* 在相邻的复选框之间、复选框与标注之间增加间距 */
font-weight: normal;
/* 重设继承的值 */
font-size: .7em;
line-height: 1.2;
}
.stylin_form1 section select {
margin-left: .4em;
font-size: .85em;
}
.stylin_form1 section input[type="submit"] {
/*提交按钮*/
width: auto;
/* 覆盖为其他字段设定的宽度 */
margin: .4em .3em 0 0;
font-size: 1em;
font-weight: 800;
color: #fff;
background-color: #bbb7ae;
cursor: pointer;
/*在鼠标位于按钮之上时,把光标变成小手形状*/
}
.stylin_form1>section:last-child {
/*居中提交按钮*/
text-align: center;
}
每一组单选按钮和复选框的标题,看起来很像表单第一个控件组里的标注。但实际 上,它们是 h4 元素——我们只能用 label 来关联控件。正因为如此,要把同样的样 式设定给 h4 很简单,把为表单第一个控件组中的 label 设定样式的选择符中的 label 替换成 h4 即可。然后,给这里每个复选框和单选按钮的小标注另外写一种样式。为 了让选项列表与其他元素对齐,我们也为它设定了相应的样式,通过减小其文本大 小也把它缩小了一点。至于提交按钮,它的宽度不再继承前面设定给其他 input 的 规则,并且在表单底部居中。明确告诉用户,这是表单的最后一个步骤
在为 name 属性设定同一个名字的情况下,复选框和单选按钮的行为就像一个组一样。 提交表单时,它们的 name 值与 value 值,将分别作为“名=值”对中的“名”和“值”。
接下来,我会再向大家展示一些样 式,它们能让表单的标注显示在控件的左侧。使用这个“标注在左”版的样式也很 简单,只要给 form 元素加上 labels_left 类即可。
form.stylin_form1.labels_left {
width: 22em;
/*加宽表单,为标注腾出地方*/
}
form.stylin_form1.labels_left label,
form.stylin_form1.labels_left h4 {
float: left;
/*把标注浮动到控件左侧*/
width: 8em;
}
form.stylin_form1.labels_left p {
margin: 0 0 0 9.35em;
/*缩进控件说明,以便它位于控件正下方*/
padding: .3em 0 0 0;
clear: both;
/*确保说明不会跟着浮动的标注和控件走*/
}
form.stylin_form1.labels_left p.note {
/*必填字段文本下方的间距*/
margin: 0 0 .2em 0;
}
/*每个单选按钮或复选框及其标注的内包装*/
form.stylin_form1.labels_left section section {
width: 10em;
margin-left: 6.5em;
padding-top: 0;
}
form.stylin_form1.labels_left section section input {
width: 1.25em;
/*单选按钮或复选框的宽度*/
margin-left: 0;
}
.stylin_form1.labels_left section input,
.stylin_form1.labels_left section textarea,
.stylin_form1.labels_left section select {
float: left;
/*让控件成为第二栏*/
width: 12em;
}
.stylin_form1.labels_left section select {
/*缩进选项列表*/
margin-left: .2em;
}
/*防止提交表单按钮继承浮动的行为*/
.stylin_form1.labels_left>section input[type=submit] {
float: none;
}
几乎每个站点都会提供一种搜索机制。我猜,恐怕你很难把一个搜索框当成一个表 单,但它的确是——一个字段的表单。搜索表单提供了简单的搜索功能,它随时恭 候在标题栏的右侧,那儿几乎是它的专属位置。苹果网站上那个小巧、低调的搜索 框,在用户点击它的时候会扩展开来,为输入关键词提供更多空间。它旁边甚至连 一个按钮都没有——用户都知道点击、输入,然后再按 Return 或回车键。这种搜索 表单的 form 元素中只需要包含一个 label 和一个 input 。在下面的例子中,我会把 这个表单放在一个 header 标签中,以便得到一个包含搜索功能的页眉。
借这个例子,我们正好可以讲一讲 CSS3 的过渡功能。过渡可以基于某个 CSS 属性 实现动画效果。在这个例子中,我们要利用它实现文本框动态变宽的效果。对应的 CSS如下:
* {
margin: 0;
padding: 0;
}
header {
/*在这个例子中代表页眉*/
font-family: helvetica, arial, sans-serif;
display: block;
overflow: hidden;
width: 500px;
margin: 30px;
border-radius: 6px;
background-color: #ddd;
}
form.stylin_form_search1 {
/*包含 label 和 input 的容器*/
float: right;
width: 200px;
margin: 5px;
padding: 5px;
}
form.stylin_form_search1 input {
float: right;
width: 70px;
padding: 2px 0 3px 5px;
outline: none;
/*去掉默认的突显轮廓线*/
font-size: .8em;
border-color: #eee #ccc #ccc #eee;
/*针对其他浏览器的厂商前缀*/
border-radius: 10px;
-webkit-transition: 2s width;
}
form.stylin_form_search1 input:focus {
width: 200px;
}
form.stylin_form_search1 label {
display: none;
}
在前面的 CSS中, input 规则将该字段宽度设定为 70像素, input:focus 规则将该字 段宽度修改成了 200 像素。这意味着,在用户单击搜索框让它获得键盘焦点之后, 字段宽度会改变。不过,由于现在有了 transition:2s width; 声明,字段不会突然扩 展到新宽度,而是会用两秒钟时间平滑地伸展到 200像素。必须注意,CSS3 的过渡 声明要放在设定初始状态的规则中。而且, transition 属性需要使用带厂商前缀的 形式——这里只示范了带有 WebKit(Safari/Chrome)前缀的属性。
CSS3 过渡 CSS3 过渡可以让 CSS 属性产生动画效果。平常被某些事件触发时变化很突然的样式,比如鼠 标悬停时改变链接颜色,使用过渡后会在指定的时间段内逐渐变化。第一条 CSS 规则设定属性 的初始状态和过渡参数。第二条 CSS 规则设定事件发生时属性的目标状态。 在下面这个例子中,用户单击表单输入字段后,输入框的边框颜色会从黑色变化为绿色,过渡 周期为两秒钟。 input {border-color:black; transition:border-color 2s;} input:focus {border-color:green;} 请注意,使用 transition 属性时要针对所有浏览器添加厂商前缀。 通常,过渡动画是由用户鼠标悬停时的:hover 伪类规则和表单元素获得焦点时的:focus 伪类 规则触发的。除此之外,还可以在一个带类名选择符的规则中设定新状态,然后通过 JavaScript (或 jQuery、MooTools 等 JavaScript 库)为元素添加这个类名来触发过渡,添加类名的时机可 以是鼠标点击或其他事件发生时。 有五个过渡属性: transition-property,过渡的 CSS 属性名,比如 color、width; transition-duration,过渡的持续时间,以秒或毫秒设定,比如 2s 、 500ms ; transition-timing-function,过渡的调速函数,决定动画效果是否平滑,是先慢后快还 是先快后慢,比如 ease-in、ease-out、ease-in-out 或 linear(默认值); transition-delay,过渡开始前的延迟时间,以秒或毫秒设定,比如 1s、200ms; transition,过渡的简写属性,例如 transition:color 2s ease-in 1ms;。 很多(并非全部)CSS 属性都可以通过 transition 属性来实现动画效果。至于哪些属性可以 实现动画效果,可以参考链接 http://www.w3.org/TR/css3-transitions/#animatable-properties, 这个页面是 W3C 对 CSS3 Transitions Module 的官方陈述。另外,下面这篇介绍 CSS3 过渡的文 章也非常值得一看:http://www.css3.info/preview/css3-transitions。
弹出层(也叫提示条)指的是在鼠标悬停于某个元素之上时显示的一个界面组件。 在页面空间有限的情况下,弹出层是为用户提供更多信息的一种有效手段。
围绕创建弹出层,我会跟大家讲两个 CSS 中非常强大,但又没多少 人真正理解的特性: z-index 属性和动态生成 HTML 元素。
<figure>
<img src="images/pink_heels.jpg" alt="pink heels" />
<figcaption>
<h3>Pink Platforms</h3>
<a href="#">More info</a>
</figcaption>
</figure>
<figure class="click_me">
<img src="images/leopard_heels.jpg" alt="leopard heels" />
<figcaption>
<h3>Leopard Platforms</h3>
<a href="#">More info</a>
</figcaption>
</figure>
<figure class="click_me">
<img src="images/red_heels.jpg" alt="red heels" />
<figcaption>
<h3>Red Platforms</h3>
<a href="#">More info</a>
</figcaption>
</figure>
大家注意,如果要使用 figcaption ,必须确保它在 figure 中是唯一的。而且,它要 么是第一个子元素,要么是最后一个子元素。首先,我们设定 figure 元素的样式, 让它在视觉上成为一个包围图片的盒子。
figure {
width: 144px;
height: 153px;
/* sizing of image box */
margin: 20px 20px;
/* space between boxes */
border: 1px solid #666;
/* image border */
position: relative;
/* positioning context for popups */
float: left;
/* makes images sit side by side */
}
img {
display: block;
}
figure 元素的边框之内恰好能容纳图片,而且浮动也让它们并排显 示在一行。 figcaption 元素目前就显示在它默认的位置,但下一步它就会变成我们 的弹出层。为什么把图片设定为 display:block 呢?因为图片默认是行内元素,行内 元素的定位原则是与文本基线对齐,而不是与它们容器的底边对齐。在把它们放在 块级元素内部时(像这里一样),把图片转换为块级元素可以解决这个问题。另外, 这里还把 figure 元素设定为相对定位,这样随后就可以把它作为定位上下文来定位 figcaption 了,下面就是相应的样式。
figcaption {
display: none;
/*隐藏弹出层*/
position: absolute;
/*相对于容器(图片)定位*/
left: 74%;
top: 14px;
/*把弹出层放到图片右侧*/
width: 130px;
/*弹出层宽度*/
padding: 10px;
/*弹出层内边距*/
background: #f2eaea;
border: 3px solid red;
border-radius: 6px;
}
figure:hover figcaption {
display: block;
}
/*鼠标悬停在图片上时显示弹出层*/
figcaption h3 {
/*弹出层的内容*/
font-size: 14px;
color: #666;
margin-bottom: 6px;
}
figcaption a {
/*弹出层的内容*/
display: block;
text-decoration: none;
font-size: 12px;
color: #000;
}
鼠标移动图片上时,会在弹出层中显示图题。可是,前两个截图的弹出层被右边的图片 给挡住了
在一个包含多个同辈元素的容器内,就像 这里 body 元素的三个 figure 子元素一样,这些同辈元素都会构造一个堆叠上下文。
在这个上下文中,它们的子元素会上下堆叠起来。假如通过定位让前两个 figure 元 素重叠,那么你会发现第一个 figure 元素,以及它的堆叠上下文中的所有子元素, 都将位于第二个 figure 元素的后面。第一个插图的弹出层属于第一个 figure 元素的 堆叠上下文,因此它默认会在第二个 figure 元素的堆叠上下文中所有元素的后面。
CSS中有一个 z-index 属性,用于控制元素的在堆叠上下文中的次序。换句话说,通 过它可以改变元素堆叠时的默认次序。 z-index 值较大的元素,在堆叠层次中位于 z-index 值较小的元素上方。 z-index 属性的值可以是 0 到任意大的数值;负值也可 以,但在某些浏览器中并不可靠。默认情况下,所有堆叠元素的 z-index 的值为 auto , 相当于 0。
不过, z-index 只对那些 position 值为 static 之外的元素有效。换句话说,涉及的 两个元素必须是 absolute 、 relative 或 fixed 定位才行。在我们的例子中,弹出层 已经是绝对定位了,其位置相对于相对定位的 figure 元素。所以,只要给弹出层应 用大于 0的 z-index 值,就可以让它们在堆叠中处于最高层。这里,我们把 z-index 设定为 2。
figure:hover figcaption {display:block; z-index:2;} /*把弹出层放到最前面*/
这个例子告诉我们,应该只给悬停状态设定 z-index 属性,而不要试图通过设定个别
元素的 z-index 来确保元素不会重叠。只要坚持“悬停时设定 z-index ”,你就能放
心地摆放图片,而且知道在触发弹出层时,它一定会显示在页面的最前面
使用宽边框创建三角形
div {
border: 12px solid;
border-color: transparent red transparent transparent;
height: 0px;
width: 0px;
}
通过加宽盒子的边框,将盒子的宽和高都设定为 0,同时将其他三 个边框设定为 transparent ,就可以用 CSS 造出一个三角形。现在,我把这个技术 与 ::before 伪元素结合起来。大家知道, ::before 和 ::after 这两个伪元素是用于添 加文本或图标等少量内容的。不过,完全可以为它们生成的内容设定任何样式,就 像给标记中其他元素设定样式一样。在这个例子中,就是要把伪元素生成内容的盒 子,通过 CSS制作成一个三角形,并把它放到弹出层的左边。
figcaption::after {
/*红色三角形的盒子*/
content: "";
/*需要有内容,这里是一个空字符串*/
position: absolute;
/*相对于弹出层定位*/
border: 12px solid;
border-color: transparent red transparent transparent;
right: 100%;
top: 17px;
/*相对于盒子边框定位三角形*/
height: 0px;
width: 0px;
/*收缩边框创造三角形*/
}
要注意的是, ::before 伪元素的 content 属性中必须有点 内容,否则就不能显示生成的元素。因为我们并不真需要什么内容,所以就用一对 双引号指定了一个空字符串给它。
界面组件是我对 HTML 提供的常见用户界面(UI,User Interface)元素的称呼,包 括列表和表单等。实际上,下拉菜单和弹出层也是很常用的界面组件,只不过 HTML 没有提供对应的原生标签而已,本章也将介绍如何通过标记和样式实现它们。 除了介绍怎么实现这些界面组件,我还会告诉你怎么写代码才能更方便地重用这些 组件,省得每次都从头写。另外,我还会告诉你怎么把它们放到重用样式表中,以 备将来在不同的项目中派上用场。