分类目录归档:UI

Inline Form Labels(2)

很多网站在填写表单的时候,都可以看到这样一种UI,input[type=text]里面有提示的文字,当鼠标点进去之后提示文字就消失了。关于这种UI,之前有写过文章介绍过http://www.zhoumingzhi.com/?p=182。在本文中主要是介绍以HTML 5的实现方式以及兼容方案。

一,HTML 5的实现方式

HTML 5的input元素引入了placeholder的属性,就是为了实现本文开头所说的效果,代码也很简单。

<input type="text" placeholder="请输入一些文字" />

二,对不支持HTML 5浏览器的兼容方案

对于不支持HTML 5的浏览器,上面的方法就不可行了。但是placeholder这个属性可以利用起来,结合JS实现想达到的效果。下面结合代码说明实现方法:

首先要用JS检测一下浏览器支不支持placeholder这个属性。

(function() {
	var tmp = document.createElement("input");
	window.supports = {
		// 检测input是否支持placeholder特性
		placeholder: "placeholder" in tmp
	}
})();

如果支持的话就什么也不做,浏览器会帮你实现这个效果。

如果不支持则要写段JS。为了方便说明用到了jQuery,代码如下:

// 绑定input的placeholder
function bindPlaceholder(input) {
	if(!window.supports.placeholder) {
		var jqInput = $(input),
			placeholder = jqInput.attr("placeholder");
		
		if(placeholder) {
			if(input.value == "") {
				input.value = placeholder; 
				$(input).addClass("placeholder");
			}
			jqInput.focus(function() {
				if(this.value == placeholder) {
					$(this).removeClass("placeholder");
					this.value = "";
				}
			}).blur(function() {
				if(this.value == "") {
					$(this).addClass("placeholder");
					this.value = placeholder;
				}
			});
		}
	}
}

上述代码中有个叫placeholder的CSS类,这个主要是让占位符的字体颜色变灰。CSS如下:

.placeholder {
	color: #a9a9a9;
}

实现上面的函数之后,就可以在页面初始化的时候调用了。

$("input[placeholder]").each(function() {
	bindPlaceholder(this);
});

三,结论

HTML 5中有很多很方便的特性,但是目前不是所有浏览器都支持,所以在用HTML 5的时候要考虑到兼容方案。

底部浮动条的一种兼容方案

Demo

原理:在标准浏览器下用position: fixed的方式就可以了。IE6下面用overlay.className = overlay.className迫使浏览器重新布局,以达到position: fixed的效果。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
	<meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
	<title>出师表</title>
	<style type='text/css'>
	html, body {
		margin: 0;
		padding: 0;
	}
	body {
		/* 这里不能加position: relative */
	}
	#content {
		font-family: 微软雅黑;
		font-size: 36px;
		line-height: 60px;
		width: 960px;
		margin: 0 auto;
	}
	#content p {
		text-indent: 2em;
	}
	#overlay {
		opacity: .5;
		filter: alpha(opacity=50);
		background: #ccc;
		width: 100%;
		height: 100px;
		position: fixed;
		_position: absolute;
		bottom: 0;
	}
	</style>
</head>
<body>
	<div id='content'><p>先帝创业未半,而中道崩殂;今天下三分,益州疲敝,此诚危急存亡之秋也。然侍卫之臣,不懈于内;忠志之士,忘身于外者:盖追先帝之殊遇,欲报之于陛下也。诚宜开张圣听,以光先帝遗德,恢弘志士之气;不宜妄自菲薄,引喻失义,以塞忠谏之路也。宫中府中,俱为一体;陟罚臧否,不宜异同:若有作奸犯科,及为忠善者,宜付有司,论其刑赏,以昭陛下平明之治;不宜偏私,使内外异法也。侍中、侍郎郭攸之、费依、董允等,此皆良实,志虑忠纯,是以先帝简拔以遗陛下:愚以为宫中之事,事无大小,悉以咨之,然后施行,必得裨补阙漏,有所广益。将军向宠,性行淑均,晓畅军事,试用之于昔日,先帝称之曰"能",是以众议举宠为督:愚以为营中之事,事无大小,悉以咨之,必能使行阵和穆,优劣得所也。亲贤臣,远小人,此先汉所以兴隆也;亲小人,远贤臣,此后汉所以倾颓也。先帝在时,每与臣论此事,未尝不叹息痛恨于桓、灵也!侍中、尚书、长史、参军,此悉贞亮死节之臣也,愿陛下亲之、信之,则汉室之隆,可计日而待也。</p><p>臣本布衣,躬耕南阳,苟全性命于乱世,不求闻达于诸侯。先帝不以臣卑鄙,猥自枉屈,三顾臣于草庐之中,谘臣以当世之事,由是感激,遂许先帝以驱驰。后值倾覆,受任于败军之际,奉命于危难之间:尔来二十有一年矣。先帝知臣谨慎,故临崩寄臣以大事也。授命以来,夙夜忧虑,恐付托不效,以伤先帝之明;故五月渡泸,深入不毛。今南方已定,甲兵已足,当奖帅三军,北定中原,庶竭驽钝,攘除奸凶,兴复汉室,还于旧都:此臣所以报先帝而忠陛下之职分也。至于斟酌损益,进尽忠言,则攸之、依、允等之任也。愿陛下托臣以讨贼兴复之效,不效则治臣之罪,以告先帝之灵;若无兴复之言,则责攸之、依、允等之咎,以彰其慢。陛下亦宜自谋,以谘诹善道,察纳雅言,深追先帝遗诏。臣不胜受恩感激!今当远离,临表涕泣,不知所云。</p>
	</div>
	<div id='overlay'></div>
<!--[if IE 6]>
<script type="text/javascript">
(function(){
	var overlay = document.getElementById('overlay'), t;
	window.onscroll = function() {
		t && clearTimeout(t);
		t = setTimeout(function() {
			// reflow
			overlay.className = overlay.className;
		}, 13);
	};
})();
</script>
<![endif]-->
</body>
</html>

制作Google Reader风格的按钮

用了很久的Google Reader,发现它的按钮很好看(如下图),而且圆角和渐变效果的处理方式也值得学习一下。

一,实现圆角效果

这里的圆角是用border来模拟出来的,把图片放大之后就可以看到其中的奥秘。

分析一下,要实现这种效果,就得用两个div嵌套实现。

<div class="btn-outer">
	<div class="btn-inner">
		HELLO WORLD
	</div>
</div>

里面的div负责上下的边框,外面的div负责左右的边框,并且左右的边框分别往外扩张一个象素,用负边距可以实现这样的效果。为了实现自适应内容的宽度,这两个div必须浮动,CSS如下:

.btn-outer, .btn-inner {
	float: left;
	border-color: #ccc;
}
.btn-outer {
	border-width: 1px 0;
	border-style: solid none;
}
.btn-inner {
	margin: 0 -1px;
	border-width: 0 1px;
	border-style: none solid;
	position: relative;
}

.btn-inner里面的position: relative是为了修复IE6的BUG,如果不加这个,在IE6下面左右两个边框会消失掉。

二,实现渐变效果

圆角做好之后,接下来制作button的背景,这里的背景是个渐变的效果。Google的做法是加多一个div,并用这个div的背景色和下边框色来模拟渐变的效果。改动一下上面的HTML:

<div class="btn-outer">
	<div class="btn-inner">
		<div class="btn-top-shadow"></div>
		<div class="btn-content">HELLO WORLD</div>
	</div>
</div>

接下来改动CSS为:

.btn-outer, .btn-inner {
	float: left;
	border-color: #ccc;
}
.btn-outer {
	border-width: 1px 0;
	border-style: solid none;
}
.btn-inner {
	font-size: 24px;
	line-height: 1.4em;
	color: #333333;
	margin: 0 -1px;
	border-width: 0 1px;
	border-style: none solid;
	position: relative;			
	background: #E3E3E3;
	overflow: hidden;
}
.btn-top-shadow {				
	border-bottom: 0.1em solid #EEEEEE;
	height: 0.7em;
	width: 100%;
	background: #F9F9F9;
	position: absolute;
	overflow: hidden;
}
.btn-content {
	position: relative;
	padding: 0 4px;
}

其中.btn-inner和.btn-top-shadow的overflow: hidden是为了修复IE6的BUG。可以看出用.btn-inner和.btn-top-shadow的背景色,以及.btn-top-shadow的下边框色就可以实现出渐变。

DEMO

终极攻略——元素垂直居中

转载须写明出处,附带本文链接。文中提到的居中,如没有特别说明,均指垂直居中。

有的同学看到题目就会想:我把父元素设成display: table-cell; vertical-align: middle不就行了吗?很不幸,IE6下没有display: table-cell这个玩意,所以本攻略中一概不讨论上述方式实现的居中。本攻略中,要垂直居中的元素分以下三种情况:

  1. 块级元素
  2. 不可替换(non-replaced)的行元素
  3. 可替换(replaced)的行元素

如果上面说的概念不是很清楚,请参考一下w3的文档。

一,块级元素垂直居中。

因为是未知高度的块元素,通常来说没有办法用绝对定位的方式使它居中。最好的办法是把它变成行内块元素(display: inline-block),这样的话就变成了类似第三种情况:可替换的行元素垂直居中

二,不可替换的行元素垂直居中。

这种情况恐怕是最简单的了,比如:

<div style="height: 300px;line-height: 300px;">
  <span>我要垂直居中啊。</span>
</div>

只要把div的行高(line-height)设成高度(height)一样就可以了,span会自动补白(leading),占满div的高度,以达到垂直居中对齐的效果。这个方法对一行里有多个non-replaced inline elements的情况也是适用的。这里要注意,如果内容太长导致换行的话,这个方法就不适用了,碰到这种情况可以先把元素变为display: inline-block,然后再参考第三种情况:可替换的行元素垂直居中

三,可替换的行元素垂直居中。

如果出现这种情况:

<div style="height: 300px;line-height: 300px;">
  <span>我要垂直居中啊。</span>
  <span>我也要垂直居中。</span>
  <input type="text" />
</div>

上面的代码中,前两个span是不可替换元素,后面的input是可替换元素。在标准浏览器下看,一切正常,可是换到IE6下,情况就不同了,你会发现,这三个元素全都跑到上面去了。这是一个IE6的BUG,简单的说,如果一行中有某些可替换元素,那么IE6会把这一行上面的补白(half-leading)给去掉。如果你打算深入研究,请看http://www.positioniseverything.net/explorer/lineheightbug.html

方法一:

在上述情况下,如果你要兼容IE6,就不得不使用HACK,IE的CSS下面有个特殊的属性writing-mode,它可以改变流的书写方向,我们就用它来做这种垂直居中的事情。拿上面的例子来说,先给input加个容器(用行内元素,这样不会改变FF下的布局):

<div style="height: 300px;line-height: 300px;">
  <span>我要垂直居中啊。</span>
  <span>我也要垂直居中。</span>
  <span class="tmp"><input type="text" /></span>
</div>

然后针对IE6写HACK:

<!--[if IE 6]>
<style type="text/css">
  div .tmp {
    /* 这里的height要和外层容器的行高一致,
       如果内容只有一行的话,可以直接用height: 100% */
    height: 100%;
    writing-mode: tb-rl;
    text-align: center;
    vertical-align: middle;
  }
</style>
<![endif]-->

方法二:

这种居中方案,可以说是万能的。如果方法一可以解决问题,请尽量不要用此方法,毕竟增加了一个没有意义的空标签是很不爽的。

请看如下代码:

<div style="height: 400px;">
  <img src="http://www.xxxxxx.com/摄影作品1.jpg" alt="" />
</div>

上面的img元素是可替换元素,它有固有的高度和宽度(由图片大小决定)。就目前的布局来说,vertical-align对它还不能起到居中对齐的作用,因为它没有占满父级div所有的高度。有的同学就说了:好办,我给img来个line-height。这样是不行的,因为它是可替换元素。难道没有办法了吗?有的,我们知道一行在屏幕上实际占有的高度,是由这行里最高的行级元素决定的(还有多种决定因素),而其它比较矮的行元素通过设置vertical-align: middle就会在这行里垂直居中对齐。通过上一点分析,我们就在img的后面加上一个空的行级元素,让它占满div的高度。这个任务交给inline-block级别的元素完成,因为它可以设高度,而且又是行布局。完整代码如下:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
	<title>垂直居中</title>
	<style type="text/css">
	#container {		
		border: 1px solid black;
		text-align: center;
		height: 500px;
	}
	#container b {
		display: inline-block;
		height: 100%;
		vertical-align: middle;
	}
	</style>
</head>
<body>
	<div id="container">
	  <img src="http://img1.cache.netease.com/img09/logo/logo.gif" alt="logo" />
	  <b></b>
	</div>
</body>
</html>

这种方法也适合一行中同时出现non-replaced elements和replaced elements的情况。

星级评价

闲来无事,想做一个星级评价系统的UI,又不想用JavaScript。分析一下,只能从:hover这个伪类入手,但是IE6除了a标签,别的标签都不支持:hover伪类。所以问题就限定在怎么样用a标签来实现星级评价。

假设星级评价有5个等级,每个星星有20象素的高度和宽度。

最初的想法是用嵌套的方式去实现,但a标签是不能嵌套使用的。所以又想到了重叠的方式,比如等级5是100象素的宽度,等级4是80个象素,并且覆盖到等级5的上面。等级3是60个象素,覆盖到等级4的上面。以此类推,并且把这5个a标签左对齐,设置一下hover时的背景图片就搞定了。

CSS代码:

#rating {
  position: relative;
}
#rating a {
  display: block;
  height: 20px;
  position: absolute;
  left: 0;
  top: 0;
}
#rating a:hover {
  background: red;
}
#rank5 {
  width: 100px;
}
/* 每个rank都要设置宽度,很麻烦。省略不写了。 */

HTML代码:

<div id="rating">
  <a id="rank5" href="#"></a>
  <a id="rank4" href="#"></a>
  <a id="rank3" href="#"></a>
  <a id="rank2" href="#"></a>
  <a id="rank1" href="#"></a>
</div>

但是这样很麻烦,每个等级的a标签都要设置宽度。那么就结合一下嵌套和重叠的方法,每个等级再加入div标签做嵌套,实现自动适应宽度。

CSS代码:

#rating {
  width: 100px;
  height: 20px;
  background: transparent url(star_rating.gif) repeat;
}
#rating div {
  margin-right: 20px;       
}
#rating, #rating div
{
  zoom: 1; /* 如果不加这个,在IE6,7下你会很纠结 */       
  position: relative; 
}
#rating a {
  position: absolute;
  height: 20px;
  width: 100%;
}
#rating a:hover {
  background: transparent url(star_rating.gif) repeat;
  background-position: 0 -20px;
}     

HTML代码:

<div id="rating"> 
  <a href="#"></a>
  <div> 
    <a href="#"></a>
    <div> 
      <a href="#"></a>
      <div> 
        <a href="#"></a>
        <div> 
          <a href="#"></a>
        </div>
      </div>
    </div>
  </div>
</div>

解释一下,#rating这个div是等级5的容器,设定它的宽度为100象素,这时候它里面的div也都是宽100象素。然后它里面的div分别向右间隔20个象素,这样就变成了宽100px,80px,60px,40px和20px的五个div。再让a标签绝对定位重叠起来,并且让它们和容器一样的大小。设置好背景图片之后,一个星级评价的UI就完成了。

点我查看完整的演示

Inline elements的一些事

Inline elements的定义

Inline-level elements are those elements of the source document that do not form new blocks of content; the content is distributed in lines (e.g., emphasized pieces of text within a paragraph, inline images, etc.). Several values of the ‘display’ property make an element inline: ‘inline’, ‘inline-table’, ‘inline-block’ and ‘run-in’ (part of the time; see run-in boxes). Inline-level elements generate inline boxes.

replaced elements and non-replaced elements

An element whose content is outside the scope of the CSS formatting model, such as an image, embedded document, or applet. For example, the content of the HTML IMG element is often replaced by the image that its “src” attribute designates. Replaced elements often have intrinsic dimensions: an intrinsic width, an intrinsic height, and an intrinsic ratio. For example, a bitmap image has an intrinsic width and an intrinsic height specified in absolute units (from which the intrinsic ratio can obviously be determined). On the other hand, other documents may not have any intrinsic dimensions (for example, a blank HTML document).

User agents may consider a replaced element to not have any intrinsic dimensions if it is believed that those dimensions could leak sensitive information to a third party. For example, if an HTML document changed intrinsic size depending on the user’s bank balance, then the UA might want to act as if that resource had no intrinsic dimensions.

replaced elements的大概意思是那些有自己尺寸和比例的元素,比如img有自己的宽和高。img,input,textarea,select,object这些都是replaced elements,除了它们别的元素就是non-replaced elements。

Inline elements的宽和高

对于non-replaced的inline元素,设置width和height是没有作用的,不过此类元素的高度可以由line-height来指定。

Inline elements的padding和margin

对于non-replaced的inline元素,padding和margin作用方式是一样的,margin和padding只会在左右两个方向起到实际作用。如下面的例子,将a元素的padding设置了50px,只在左右留出了空白。

Lorem ipsum dolor sit amet consect etuer adipi scing elit sed diam nonummy nibh euismod tinunt ut laoreet dolore magna aliquam erat volut. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat.

Inline Form Labels

很多网站在填写表单的时候,都可以看到这样一种UI,input[type=text]里面有提示的文字,当鼠标点进去之后提示文字就消失了。以前做这个效果的时候都是用很笨的方法,就是把提示文字写进input的value里,然后在focus的时候去掉,在blur的时候再判断一下input的value是不是空,如果为空再把提示加上去。这样做很笨,不仅代码难看,也不符合“规范”(提示就应该是提示,不应该是input的值)。

有一天在网上发现的一种很容易的做法,下面就介绍一下。先是普通的HTML:

<div class="row">
  <label class="hint" for="textbox_name">
    <span>User name</span>
  </label>
  <input class="textbox" type="text" id="textbox_name" />
</div>

注意这里的label有一个for的属性,这个是必需的,它可以确保点击label的时候,让input获得焦点,因为之后我们要让label定位到input的上方(label与input重叠)。接下来添加样式:

.row {
  position: relative;
  padding: 1px;
}
.row .hint{
  opacity: 0.4;
  filter:alpha(opacity=40);
  padding: 4px 0 0 6px;     
  position: absolute;
}
.row .textbox {      
  height: 18px;
  outline: none;
  border: 1px solid Gray;
  padding: 4px 3px 2px;     
  -webkit-border-radius: 2px
  -moz-border-radius: 2px;
}
.focus {
  padding: 0;
}
.focus .textbox {
  border: 2px solid #7B8AAC;
}

这样label就和input重叠起来了。

然后处理用户的操作,当input获得焦点的时候,让label隐藏,失去焦点的时候需要判断一下input的value是不是空值,如果是空值就让label显示出来,下面是JavaScript:

$(function() {
  $(".textbox").focus(function() {
    var self = $(this);
    self.closest(".row").addClass("focus");
    self.prev().css("display", "none");
  }).blur(function() {
    var self = $(this);
    self.closest(".row").removeClass("focus");
    if(self.val() == "") {
      self.prev().css("display", "inline");
    }
  });
});

为了方便,这里用到了jQuery。

多列等高方案

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
	<head>
		<title>三列等高</title>
		<style>
			#header
			{
				background-color: gray;
				height: 100px;
				width: 960px;
			}
			#container-1, #container-2, #container-3
			{
				position: relative;
				float: left;
			}
			#container-1
			{		
				overflow: hidden;
				width: 960px;
				margin-top: 10px;
				background-color: red;	
			}
			#container-2
			{
				background-color: green;
				left: 240px;
			}
			#container-3
			{
				float: left;
				background-color: blue;
				left: 480px;
			}
			#main, #primary, #secondary
			{
				display: inline;
				margin: 0 10px;
				position: relative;
				float: left;
			}
			#main
			{
				width: 460px;
				right: 50%;
			}
			#primary
			{				
				width: 220px;
				right: 125%;
			}
			#secondary
			{
				right: 75%;
				width: 220px;		
			}
		</style>		
	</head>
	<body>
		<div id="header"></div>			
		<div id="container-1">
			<div id="container-2">
				<div id="container-3">
					<div id="main">Main<br />Main</div>
					<div id="primary">Primary</div>
					<div id="secondary">Secondary</div>
				</div>
			</div>
		</div>		
		<div style="clear: left">
		</div>
	</body>
</html>

让一个div固定在页面底端

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>div居底</title>
	<script type="text/javascript" 
		src="http://www.google.com/jsapi"></script>
	<script type="text/javascript">
		google.load("jquery", "1.3.2");
		google.setOnLoadCallback(function() {		    
			var s = "";
			for (var i = 0; i < 100; i++) {
				s += "<div>這里是內容,很高</div>";					
			}		 
			s += "<div>END</div>"
			$("#container").html(s);   
			function my_resize() {
				$("#container").height($("#right").height() - $("#copyright").outerHeight(true));
			};
			my_resize();
			$(window).resize(function() {
				my_resize();
			})
		});
	</script>
	<style type="text/css">
		html, body {
			height: 100%;
			margin: 0;
			overflow: hidden;
		}
		#right {
			height: 100%;
			position: relative;
			background-color: Yellow;
		}
		#copyright {
			height: 20px;
			width: 100%;
			background-color: Gray;	
		}
		#container {
		    height: 2000px;
			overflow-x: hidden;
			overflow-y: auto;
		}
	</style>
</head>
<body>
    <div id="left"></div>
    <div id="right">
        <div id="container">
        </div>
        <div id="copyright">Copyright &copy; 1999-2009, Michael Zhou, All Rights Reserved</div>
    </div>
</body>
</html>

display: inline-block在IE6、IE7下bug的解决方法

display: inline-block在IE6、IE7下应用到块元素时是不起作用的,可以用下面的方法解决:

.selector { display: inline-block }
.selector { *display: inline }

注意:一定要分开写,如果写在同一个选择器里是不起作用的,这是IE的一个BUG。

在IE下,display: inline-block只是触发了元素的layout。比如将display: inline-block设置到div上,只能保证这个div拥有块元素的特征(可以设置宽度,高度等),但还是会产生换行。接下来要设置display: inline,使其不产生换行。