<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>阿当</title>
	<atom:link href="http://adam.ofeva.com/feed" rel="self" type="application/rss+xml" />
	<link>http://adam.ofeva.com</link>
	<description>阿当的笔记</description>
	<lastBuildDate>Wed, 09 Mar 2011 01:23:52 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3</generator>
		<item>
		<title>各浏览器对页面外部资源加载的策略</title>
		<link>http://adam.ofeva.com/blog/69.html</link>
		<comments>http://adam.ofeva.com/blog/69.html#comments</comments>
		<pubDate>Wed, 09 Mar 2011 01:23:39 +0000</pubDate>
		<dc:creator>阿当</dc:creator>
				<category><![CDATA[Web]]></category>

		<guid isPermaLink="false">http://adam.ofeva.com/?p=69</guid>
		<description><![CDATA[这个总结来源于一次优化的请求，最初某个页面的加载十分缓慢，load事件迟迟无法触发，因此希望可以通过对静态文件分域名等方式对页面的外部资源进行优化，拿得load事件尽可能早地触发。 于是我查看了页面的源码，并对外部资源进行了整理，基于下面2个理念画出了一个推测的瀑布图： 浏览器对同一个域只能并发2个HTTP请求 &#8211; 网上盛传已久。 javascript文件的加载会阻塞浏览器其他资源的加载 &#8211; 同样网上盛传已久。 然而，当我看到各浏览器中实际的瀑布图时，我知道自己又犯了一个简单的错误：太过相信所谓的权威和大众的声音，而没有更早地进行实践来检验理论的正确性…… 本篇文章就使用几种流行的浏览器，针对同一个页面的外部资源加载过程进行分析，推测各浏览器加载外部资源的策略、特征，并最后给予一定的比较和总结。 测试样例 测试的页面结构如下： head 1.css + 1.js body 1.jpg + 2.jpg + 2.js + 2.css + 3.jpg + 4.jpg + 3.css + 3.js + 5.jpg + 6.jpg 共12个外部资源，加上页面本身，一次完整的加载一共有13次HTTP GET请求。 针对每一个外部资源，服务器首先会休眠5秒的时间，随后再返回相应的内容，以方便查看整个外部资源的加载过程。 测试的浏览器如下： IE6 IE8 Firefox3.6 Firefox4.0 beta12 Chrome 8 Opera 11 IE6 IE6的瀑布图非常传统，其特征有： 各资源按照在HTML中出现的顺序进行加载。 javascript文件会阻塞其后所有资源的加载。 最大并发HTTP连接数为2个。 可见网上盛传的2个“误区”都来自IE6统治浏览器市场的时代，针对IE6的优化太多太多，大家也就习惯性地将这些结论作为公理来使用了。 [...]]]></description>
			<content:encoded><![CDATA[<p>这个总结来源于一次优化的请求，最初某个页面的加载十分缓慢，load事件迟迟无法触发，因此希望可以通过对静态文件分域名等方式对页面的外部资源进行优化，拿得load事件尽可能早地触发。</p>
<p>于是我查看了页面的源码，并对外部资源进行了整理，基于下面2个理念画出了一个推测的瀑布图：</p>
<ul>
<li>浏览器对同一个域只能并发2个HTTP请求 &ndash; 网上盛传已久。 </li>
<li>javascript文件的加载会阻塞浏览器其他资源的加载 &ndash; 同样网上盛传已久。 </li>
</ul>
<p>然而，当我看到各浏览器中实际的瀑布图时，我知道自己又犯了一个简单的错误：太过相信所谓的权威和大众的声音，而没有更早地进行实践来检验理论的正确性……</p>
<p>本篇文章就使用几种流行的浏览器，针对同一个页面的外部资源加载过程进行分析，推测各浏览器加载外部资源的策略、特征，并最后给予一定的比较和总结。</p>
<h3>测试样例</h3>
<p>测试的页面结构如下：</p>
<ul>
<li>head
<ul>
<li>1.css + 1.js </li>
</ul>
</li>
<li>body
<ul>
<li>1.jpg + 2.jpg + 2.js + 2.css + 3.jpg + 4.jpg + 3.css + 3.js + 5.jpg + 6.jpg </li>
</ul>
</li>
</ul>
<p>共12个外部资源，加上页面本身，一次完整的加载一共有13次HTTP GET请求。</p>
<p>针对每一个外部资源，服务器首先会休眠5秒的时间，随后再返回相应的内容，以方便查看整个外部资源的加载过程。</p>
<p>测试的浏览器如下：</p>
<ul>
<li>IE6 </li>
<li>IE8 </li>
<li>Firefox3.6 </li>
<li>Firefox4.0 beta12 </li>
<li>Chrome 8 </li>
<li>Opera 11 </li>
</ul>
<h3>IE6</h3>
<p><img src="http://www.otakustay.com/wp-content/uploads/2011/03/timeline-ie6.png"></p>
<p>IE6的瀑布图非常传统，其特征有：</p>
<ul>
<li>各资源按照在HTML中出现的顺序进行加载。 </li>
<li>javascript文件会阻塞其后所有资源的加载。 </li>
<li>最大并发HTTP连接数为2个。 </li>
</ul>
<p>可见网上盛传的2个“误区”都来自IE6统治浏览器市场的时代，针对IE6的优化太多太多，大家也就习惯性地将这些结论作为公理来使用了。</p>
<h3>IE8</h3>
<p><img src="http://www.otakustay.com/wp-content/uploads/2011/03/timeline-ie8.png"></p>
<p>和IE6完全不同的瀑布图，其特点有：</p>
<ul>
<li>最大并发HTTP连接数为6个。 </li>
<li>javascript文件已经不会阻塞其他资源的加载，甚至多个javascript文件可以一起加载，并且会保证执行的顺序。 </li>
<li>会分析HTML结构，优先下载script和link标签定义的外部资源。 </li>
</ul>
<h3>Firefox3.6</h3>
<p><img src="http://www.otakustay.com/wp-content/uploads/2011/03/timeline-firefox3.png"></p>
<p>和IE8的几乎完全一样：</p>
<ul>
<li>最大并发HTTP连接数为6个（可在about:config中修改）。 </li>
<li>javascript文件不会阻塞其他资源的加载，多个javascript文件可以一起加载。 </li>
<li>会分析HTML结构，优先下载script和link标签定义的外部资源。 </li>
</ul>
<h3>Firefox4 beta12</h3>
<p><img src="http://www.otakustay.com/wp-content/uploads/2011/03/timeline-firefox4.png"></p>
<p>不知是因为设计理念上的不同，还是因为beta版未照顾到这一块，Firefox4反而退化了，和Firefox3.6的区别主要体现在对资源类型的处理上，Firefox4不再严格地优先下载script和link标签定义的外部资源，而是按照HTML结构中出现的顺序来进行加载。</p>
<h3>Chrome8</h3>
<p><img src="http://www.otakustay.com/wp-content/uploads/2011/03/timeline-chrome.png"></p>
<p>Chrome自带的工具不能很清楚地表示各请求的开始时间，所以使用了Fiddler的瀑布图，从图上可以看出，Chrome也是比较特立独行的一位，其特点有：</p>
<ul>
<li>最大并发HTTP连接数为6。 </li>
<li>head部分的资源会单独下载，且阻塞body中的其他资源的加载。 </li>
<li>会优先加载script和link标签定义的资源。 </li>
</ul>
<h3>Opera11</h3>
<p><img src="http://www.otakustay.com/wp-content/uploads/2011/03/timeline-opera.png"></p>
<p>先报怨一下，Dragonfly不怎么好用来着……Opera的资源加载也比较有特色，而且很难看出规律，只能大致总结一下：</p>
<ul>
<li><del>最大并发HTTP连接数为5（网上有说原先版本是4）。</del><ins>经过网友的指正，Opera的最大并发HTTP连接数默认为16，可在<code>opera:config - Performance - Max Connections Server</code>查看和修改。</ins> </li>
<li>javascript文件的加载会阻塞其他script和link标签定义的外部资源的加载，如图中的2.js。但不会阻塞图片等其他资源的加载，如图中的3.js。 </li>
<li>会一定程度上对资源的优先级进行优化，但由于javascript文件要阻止后续部分资源的加载，又为了充分利用最大HTTP连接数，因此不能严格先加载所有的script和link标签定义的资源，导致瀑布图上各类型资源有相互穿插，难寻规律。 </li>
</ul>
<h3>总结</h3>
<ul>
<li>抛开IE6不论的话，除非是在线相册之类外部资源非常多的页面，不然没必要去追求静态资源的分域名优化。 </li>
<li>针对IE6进行静态资源分域名优化时，要严格注意javascript文件对后续资源的阻塞，进行精确计算和设计后保证资源最完美地分域名存储，以提供最大并行度。 </li>
<li>鉴于Chrome对head部分的资源会独立加载，当head部分用不满6个HTTP并发数时，是否可以将资源移到body中呢？在body中的资源又会引起其他的问题，需要谨慎考虑。 </li>
<li>Opera的行为比较怪异，似乎主动设计了一个很麻烦的算法，不过考虑到其占有率，就先放在一边吧……而且号称最快的浏览器的Opera，在加载javascript文件时竟然如此笨拙…… </li>
<li>Firefox4 beta12的行为让人无法理解，看来要追踪RC版是否还存在这个问题，如果存在的话可以考虑找Mozilla报个问题了。 </li>
</ul>
<p>对各浏览器加载外部资源的策略的掌握，是<abbr title="Web Performance Optimize">WPO</abbr>的基本元素，虽然一直想当一个WPO的专家，却在这方面迟迟不愿实践，实在有愧于自己的理想……</p>
<p>最后，如果有哪位朋友了解Opera对资源加载的具体策略的，还请提供一下，以便有更清晰地认知，谢谢~！</p>
<p>来源：http://www.otakustay.com/browser-strategy-loading-external-resource/</p>
]]></content:encoded>
			<wfw:commentRss>http://adam.ofeva.com/blog/69.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>追求神乎其技的程式设计之道（番外篇）</title>
		<link>http://adam.ofeva.com/blog/65.html</link>
		<comments>http://adam.ofeva.com/blog/65.html#comments</comments>
		<pubDate>Wed, 23 Feb 2011 09:42:21 +0000</pubDate>
		<dc:creator>阿当</dc:creator>
				<category><![CDATA[个人]]></category>
		<category><![CDATA[Programing]]></category>

		<guid isPermaLink="false">http://adam.ofeva.com/?p=65</guid>
		<description><![CDATA[我的学习过程 还记得小时候我和我弟上过一个奇特的数学补习班，叫做「功文数学」。他们的「教学」方法非常特别，不像一般的教室会有一个老师在讲台上教课，而是每个人会拿到一叠数学题目，每一面都有数个计算问题，像是「10 x 2 = ?」这样的问题。每次去功文的任务就是把那一叠题目写完，写得越快的人就可以越早回家。那些题目说来没什么意思，从头到尾全都是计算问题，难度从最基本的加减乘除，一直到微积分都有。每次写完题目后，会有个类似老师的角色，拿出解答本帮你对答案，打上成绩。如果在那老师能应付的范围内，他可能还会跟你说说哪里犯错了，下次多加油之类的，但如果到了国高中程度的问题时，老师的作用就只剩对答案而已了。 我之所以说这个补习班很奇妙，原因就在于这个教学过程中，讲求的是大量的计算和自我学习的能力。我从加减乘除开始，一路这样写到了微积分，写到后来那边的老师也没办法批改了，干脆把解答本拿给我们看，让我们自己看解答学。起初我还觉得这个补习班挺不错的，只要我做得越快就能越早回家，对小孩子来说是个很好的诱因。而且因为大量的训练，让我的计算能力变得非常好，国小国中时的数学考试我都不用准备也能很快写完交卷。但后来我开始觉得不太对劲，很多题目我虽然知道怎么算，但我其实不懂为什么要这么算，或是这么算的意义是什么。可是台湾的考试也不管这个，反正你只要能得到答案，根本没人在意过程是怎样。 功文数学的这套方法对训练计算能力而言很有帮助，因为计算就是需要大量的练习才能变快变好。但问题是，计算能力再好，也是只能算已经定义清楚的问题（也就是考卷上的问题），而无法发现新问题并定义问题。 在我高中开始参加程式比赛后，我用了我熟悉的这套方法训练自己的程式能力，自己一个人大量的找题目练习。每天从早练到晚，即使没有人教我也觉得很正常，因为我从小就是这样自学起来的。这样练了一年后，我能飞快的写程式和解题，看到熟悉的题型就能马上开始敲键盘，但同时我也开始觉得写程式变成一种机械化的过程：看题，解题，写程式，看题，解题，写程式…。跟我从小做数学似乎没两样。 我开始觉得无趣。即使我程式写得再快再好，也只是解出一个别人设计好的问题而已。 心中的声音不断的说：「为什么我要做别人早就知道答案的问题？」 我想要做没有人做过的事，看到没有人看到的问题，再亲手解决这些问题。 当我开始这么想的时候，我正在一边学着做科展。科展全名是科学展览，意味着要做点跟科学有关的事来展览。以前的文章提过，我高三时梦想着要让电脑自己写程式，我觉得这是一件没人做过的事，非常酷，所以我就一直专注在这件事上（其实当时就有很多人在做这种研究了，只是我不知道而已）。但从一开始到我被选上去美国参加国际科展时，我都一直不觉得这件事科学在哪，顶多说是一个工程（或工艺）作品。但选上代表后是有些好处的，台大的欧阳明教授给了我一些指引，让我知道「科学方法」得测量和量化这个作品的结果，才有客观的数据可以知道它的作用，进而跟其他相似作品比较好坏，也因此我才开始对「做研究」这件事有点概念。（到这时我才想起来，小时候虽然有做一些物理化学实验，但从没人跟我说过这些实验是有一套标准的方法和流程的，当然也没人跟我说这件事的价值所在。） 虽然做科展挺有趣的，但同时我也对科学感到失望 – 因为我意识到科学方法没办法帮助我们发明新东西，只能用来评估和检验已知事物的好坏。至于所谓的创新科学研究，也是得先提出一个假设 (hypothesis)，然后再用这套标准方法去验证它是否成立。但到底要假设什么事情呢？科学可没办法告诉你。 上大学后，我看到当时处在黄金时代的MIT Media Lab所产出的许多创新研究成果，也开始接触到设计(design)这个领域。我发现Media Lab的人大多有跨领域的背景，像是设计师+电机工程师、或是音乐家+软体工程师，他们常看到别人看不到的问题，并提出简单又优雅的解决方法。我以前一直以为做设计的人讲求的是美术天份，但后来深入了解后才发现这是个大误会。设计的目的是解决问题，跟程式设计师其实没两样，只是用的工具是纸笔或模型罢了。（但一个好的设计通常也都很「美」就是了） 虽然如此，设计师和工程师也有很大的不同。设计师对四周环境和日常生活很敏感，常常得在生活中注意各种细节的不完美之处。但工程师成天泡在电脑中，而且很善于使用其实很难用的介面和程式（像Linux、vi、command line、还有各种程式语言），甚至引以为傲，日子久了也就不觉得这些东西有什么问题。泡在程式码中的工程师也一样，如果习惯了和前人或其他人的大便码相处，久了也就不觉得臭了。 发现这些现象后，我开始学着跳脱出原本习惯的一切，开始注意生活周遭的各种细节，思考为什么这个东西当初要这样设计、这样做有什么好处和坏处、有没有更好的方式之类的问题。之后，当我习惯观察细节后，慢慢察觉到我习惯用的软体、工具、环境、程式语言，处处都是设计后的结果，而且充满可以改进和创新的空间。这些东西都不是没来由的产物，而是经过某些人思考过后的结果，甚至是经过好几轮的演化结果。可惜平常在学习或教学的时候，很少人会提到这些歷史渊源和演化过程，以至于这些设计都变成理所当然的存在。但如果我们能仔细观察平常的事物，进一步思考就会发现很多设计都是为了因应当初时空环境的限制，而这些限制现在不一定存在了，所以我们就会有发挥的空间。 眼光拉远后，能看到的问题更多了。到了这个阶段，能力强的人会觉得能解决的问题也很多。但上天给每个人的时间是一样多的，这时重要的事情反而又变成：「找出最重要、最根本的问题来解决，而不要被众多的小问题和小机会所分心，才能产生最大的影响力」。 回顾我的学习过程，我会觉得每一个阶段都是一块基石，一块块往上叠以后才会具备该有的能力和经验做下一阶段的事情。举例来说，要是我一开始没投入程式比赛的练习累积足够的实作能力，之后我就没办法随心所欲的写出我想写的程式，也没办法参加科展体会做没人做过的事有多么有趣。之后我可能就会一昧沉浸在钻研各种流行技术中，或是眼高手低说得一嘴好主意但却做不出什么来。 虽然学习是一步一步往上走的，但过程中每件事都有反面的效应，让我不知道是不是做别的选择会更好。像是我觉得功文数学浪费了我太多时间在数学计算上，而限制了我在其他方面的发展，但同时它也让我养成靠自己学习的习惯；参加程式竞赛也有类似的效应，虽然增强了我的程式能力，但也让我错过正常的高中生活和课程（虽然说到目前为止没有觉得有什么负面影响）。 无论如何，我相信在成长的过程中适当的大量练习是必要的。异数(Outliers: The Story of Success)一书的作者Gladwell说要精通一件事情至少需要一万小时的练习，我相信这是真的。我在高中为了比赛所做的练习起码就超过五千小时，上大学后轻易就超过一万小时，但其实我也不觉得我真的精通了什么。经过大量的练习，本来很难的技巧或技术都会变成一种本能，可以很自然的使用它来从事更高阶的应用或是建构更复杂的技术。没有这些基础，也就很难站在更高的地方看得更远想得更多，我也不会走在现在的道路上。 来源：http://blog.vgod.tw/2011/02/23/divine-code-extra/]]></description>
			<content:encoded><![CDATA[<p>我的学习过程</p>
<p>还记得小时候我和我弟上过一个奇特的数学补习班，叫做「功文数学」。他们的「教学」方法非常特别，不像一般的教室会有一个老师在讲台上教课，而是每个人会拿到一叠数学题目，每一面都有数个计算问题，像是「10 x 2 = ?」这样的问题。每次去功文的任务就是把那一叠题目写完，写得越快的人就可以越早回家。那些题目说来没什么意思，从头到尾全都是计算问题，难度从最基本的加减乘除，一直到微积分都有。每次写完题目后，会有个类似老师的角色，拿出解答本帮你对答案，打上成绩。如果在那老师能应付的范围内，他可能还会跟你说说哪里犯错了，下次多加油之类的，但如果到了国高中程度的问题时，老师的作用就只剩对答案而已了。</p>
<p>我之所以说这个补习班很奇妙，原因就在于这个教学过程中，讲求的是大量的计算和自我学习的能力。我从加减乘除开始，一路这样写到了微积分，写到后来那边的老师也没办法批改了，干脆把解答本拿给我们看，让我们自己看解答学。起初我还觉得这个补习班挺不错的，只要我做得越快就能越早回家，对小孩子来说是个很好的诱因。而且因为大量的训练，让我的计算能力变得非常好，国小国中时的数学考试我都不用准备也能很快写完交卷。但后来我开始觉得不太对劲，很多题目我虽然知道怎么算，但我其实不懂为什么要这么算，或是这么算的意义是什么。可是台湾的考试也不管这个，反正你只要能得到答案，根本没人在意过程是怎样。</p>
<p>功文数学的这套方法对训练计算能力而言很有帮助，因为计算就是需要大量的练习才能变快变好。但问题是，计算能力再好，也是只能算已经定义清楚的问题（也就是考卷上的问题），而无法发现新问题并定义问题。</p>
<p>在我高中开始参加程式比赛后，我用了我熟悉的这套方法训练自己的程式能力，自己一个人大量的找题目练习。每天从早练到晚，即使没有人教我也觉得很正常，因为我从小就是这样自学起来的。这样练了一年后，我能飞快的写程式和解题，看到熟悉的题型就能马上开始敲键盘，但同时我也开始觉得写程式变成一种机械化的过程：看题，解题，写程式，看题，解题，写程式…。跟我从小做数学似乎没两样。</p>
<p>我开始觉得无趣。即使我程式写得再快再好，也只是解出一个别人设计好的问题而已。</p>
<p>心中的声音不断的说：「为什么我要做别人早就知道答案的问题？」</p>
<p>我想要做没有人做过的事，看到没有人看到的问题，再亲手解决这些问题。</p>
<p>当我开始这么想的时候，我正在一边学着做科展。科展全名是科学展览，意味着要做点跟科学有关的事来展览。以前的文章提过，我高三时梦想着要让电脑自己写程式，我觉得这是一件没人做过的事，非常酷，所以我就一直专注在这件事上（其实当时就有很多人在做这种研究了，只是我不知道而已）。但从一开始到我被选上去美国参加国际科展时，我都一直不觉得这件事科学在哪，顶多说是一个工程（或工艺）作品。但选上代表后是有些好处的，台大的欧阳明教授给了我一些指引，让我知道「科学方法」得测量和量化这个作品的结果，才有客观的数据可以知道它的作用，进而跟其他相似作品比较好坏，也因此我才开始对「做研究」这件事有点概念。（到这时我才想起来，小时候虽然有做一些物理化学实验，但从没人跟我说过这些实验是有一套标准的方法和流程的，当然也没人跟我说这件事的价值所在。）</p>
<p>虽然做科展挺有趣的，但同时我也对科学感到失望 – 因为我意识到科学方法没办法帮助我们发明新东西，只能用来评估和检验已知事物的好坏。至于所谓的创新科学研究，也是得先提出一个假设 (hypothesis)，然后再用这套标准方法去验证它是否成立。但到底要假设什么事情呢？科学可没办法告诉你。</p>
<p>上大学后，我看到当时处在黄金时代的MIT Media Lab所产出的许多创新研究成果，也开始接触到设计(design)这个领域。我发现Media Lab的人大多有跨领域的背景，像是设计师+电机工程师、或是音乐家+软体工程师，他们常看到别人看不到的问题，并提出简单又优雅的解决方法。我以前一直以为做设计的人讲求的是美术天份，但后来深入了解后才发现这是个大误会。设计的目的是解决问题，跟程式设计师其实没两样，只是用的工具是纸笔或模型罢了。（但一个好的设计通常也都很「美」就是了）</p>
<p>虽然如此，设计师和工程师也有很大的不同。设计师对四周环境和日常生活很敏感，常常得在生活中注意各种细节的不完美之处。但工程师成天泡在电脑中，而且很善于使用其实很难用的介面和程式（像Linux、vi、command line、还有各种程式语言），甚至引以为傲，日子久了也就不觉得这些东西有什么问题。泡在程式码中的工程师也一样，如果习惯了和前人或其他人的大便码相处，久了也就不觉得臭了。</p>
<p>发现这些现象后，我开始学着跳脱出原本习惯的一切，开始注意生活周遭的各种细节，思考为什么这个东西当初要这样设计、这样做有什么好处和坏处、有没有更好的方式之类的问题。之后，当我习惯观察细节后，慢慢察觉到我习惯用的软体、工具、环境、程式语言，处处都是设计后的结果，而且充满可以改进和创新的空间。这些东西都不是没来由的产物，而是经过某些人思考过后的结果，甚至是经过好几轮的演化结果。可惜平常在学习或教学的时候，很少人会提到这些歷史渊源和演化过程，以至于这些设计都变成理所当然的存在。但如果我们能仔细观察平常的事物，进一步思考就会发现很多设计都是为了因应当初时空环境的限制，而这些限制现在不一定存在了，所以我们就会有发挥的空间。</p>
<p>眼光拉远后，能看到的问题更多了。到了这个阶段，能力强的人会觉得能解决的问题也很多。但上天给每个人的时间是一样多的，这时重要的事情反而又变成：「找出最重要、最根本的问题来解决，而不要被众多的小问题和小机会所分心，才能产生最大的影响力」。</p>
<p>回顾我的学习过程，我会觉得每一个阶段都是一块基石，一块块往上叠以后才会具备该有的能力和经验做下一阶段的事情。举例来说，要是我一开始没投入程式比赛的练习累积足够的实作能力，之后我就没办法随心所欲的写出我想写的程式，也没办法参加科展体会做没人做过的事有多么有趣。之后我可能就会一昧沉浸在钻研各种流行技术中，或是眼高手低说得一嘴好主意但却做不出什么来。</p>
<p>虽然学习是一步一步往上走的，但过程中每件事都有反面的效应，让我不知道是不是做别的选择会更好。像是我觉得功文数学浪费了我太多时间在数学计算上，而限制了我在其他方面的发展，但同时它也让我养成靠自己学习的习惯；参加程式竞赛也有类似的效应，虽然增强了我的程式能力，但也让我错过正常的高中生活和课程（虽然说到目前为止没有觉得有什么负面影响）。</p>
<p>无论如何，我相信在成长的过程中适当的大量练习是必要的。异数(Outliers: The Story of Success)一书的作者Gladwell说要精通一件事情至少需要一万小时的练习，我相信这是真的。我在高中为了比赛所做的练习起码就超过五千小时，上大学后轻易就超过一万小时，但其实我也不觉得我真的精通了什么。经过大量的练习，本来很难的技巧或技术都会变成一种本能，可以很自然的使用它来从事更高阶的应用或是建构更复杂的技术。没有这些基础，也就很难站在更高的地方看得更远想得更多，我也不会走在现在的道路上。</p>
<p>来源：http://blog.vgod.tw/2011/02/23/divine-code-extra/</p>
]]></content:encoded>
			<wfw:commentRss>http://adam.ofeva.com/blog/65.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>用户体验的十个基本法则</title>
		<link>http://adam.ofeva.com/blog/61.html</link>
		<comments>http://adam.ofeva.com/blog/61.html#comments</comments>
		<pubDate>Wed, 27 Aug 2008 13:58:51 +0000</pubDate>
		<dc:creator>阿当</dc:creator>
				<category><![CDATA[UI/UE]]></category>

		<guid isPermaLink="false">http://adam.ofeva.com/?p=61</guid>
		<description><![CDATA[Andreas Pfeiffer写的WHY FEATURES DON’T MATTER ANYMORE: THE NEW LAWS OF DIGITAL TECHNOLOGY，从用户体验角度强调了功能并不在重要，重要的在于用户对产品或服务的体验，并列出十大法则，10 fundamental rules for the age of user experience technology。 在这个时代里10个基本法则是： 1、更多的功能并不好 More features isn’t better, it’s worse； 2、增加功能不会让事情更容易 You can’t make things easier by adding to them； 3、让用户迷惑是毁掉业务的终级手段 Confusion is the ultimate deal-breaker； 4、风格很关键 Style matters； 5、只有在一项功能可以提升用户体验时才加上它 Only features that provide a good [...]]]></description>
			<content:encoded><![CDATA[<p>Andreas Pfeiffer写的WHY FEATURES DON’T MATTER ANYMORE: THE NEW LAWS OF DIGITAL TECHNOLOGY，从用户体验角度强调了功能并不在重要，重要的在于用户对产品或服务的体验，并列出十大法则，10 fundamental rules for the age of user experience technology。 </p>
<p>在这个时代里10个基本法则是：</p>
<p><strong>1、更多的功能并不好</strong><br />
More features isn’t better, it’s worse；</p>
<p><strong>2、增加功能不会让事情更容易</strong><br />
You can’t make things easier by adding to them； </p>
<p><strong>3、让用户迷惑是毁掉业务的终级手段</strong><br />
Confusion is the ultimate deal-breaker；</p>
<p><strong>4、风格很关键</strong><br />
Style matters；</p>
<p><strong>5、只有在一项功能可以提升用户体验时才加上它</strong><br />
Only features that provide a good user experience will be used； </p>
<p><strong>6、任何需要学习的功能都只会吸引一小部分用户</strong><br />
Any feature that requires learning will only be adopted by a small fraction of users； </p>
<p>7、无用的功能不止是无用，它会破坏易用性<br />
Unused features are not only useless, they can slow you down and diminish ease of use；</p>
<p><strong>8、用户不会关心技术，他们只想知道产品能做什么</strong><br />
Users do not want to think about technology: what really counts is what it does for them； </p>
<p><strong>9、忘掉关键功能，关注最重要的用户体验</strong><br />
Forget about the killer feature. Welcome to the age of the killer user-experience； </p>
<p><strong>10、简洁很难，因此少就是多</strong><br />
Less is difficult, that’s why less is more。</p>
]]></content:encoded>
			<wfw:commentRss>http://adam.ofeva.com/blog/61.html/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>MySQL 服务器调优</title>
		<link>http://adam.ofeva.com/blog/60.html</link>
		<comments>http://adam.ofeva.com/blog/60.html#comments</comments>
		<pubDate>Tue, 26 Aug 2008 04:48:53 +0000</pubDate>
		<dc:creator>阿当</dc:creator>
				<category><![CDATA[database]]></category>
		<category><![CDATA[mysql]]></category>

		<guid isPermaLink="false">http://adam.ofeva.com/?p=60</guid>
		<description><![CDATA[利用服务器的几个调优技巧，让 MySQL 服务器飞速运行 如今，开发人员不断地开发和部署使用 LAMP（Linux®、Apache、MySQL 和 PHP/Perl）架构的应用程序。但是，服务器管理员常常对应用程序本身没有什么控制能力，因为应用程序是别人编写的。这份 共三部分的系列文章 将讨论许多服务器配置问题，这些配置会影响应用程序的性能。本文是本系列文章的第三部分，也是最后一部分，将重点讨论为实现最高效率而对数据库层进行的调 优。 关于 MySQL 调优 有 3 种方法可以加快 MySQL 服务器的运行速度，效率从低到高依次为： 替换有问题的硬件。 对 MySQL 进程的设置进行调优。 对查询进行优化。 替换有问题的硬件通常是我们的第一考虑，主要原因是数据库会占用大量资源。不过这种解决方案也就仅限于此了。实际上，您通常可以让中央处理器（CPU）或磁盘速度加倍，也可以让内存增大 4 到 8 倍。 第二种方法是对 MySQL 服务器（也称为 mysqld）进行调优。对这个进程进行调优意味着适当地分配内存，并让 mysqld 了解将会承受何种类型的负载。加快磁盘运行速度不如减少所需的磁盘访问次数。类似地，确保 MySQL 进程正确操作就意味着它花费在服务查询上的时间要多于花费在处理后台任务（如处理临时磁盘表或打开和关闭文件）上的时间。对 mysqld 进行调优是本文的重点。 最好的方法是确保查询已经进行了优化。这意味着对表应用了适当的索引，查询是按照可以充分利用 MySQL 功能的方式来编写的。尽管本文并没有包含查询调优方面的内容（很多著作中已经针对这个主题进行了探讨），不过它会配置 mysqld 来报告可能需要进行调优的查询。 虽然已经为这些任务指派了次序，但是仍然要注意硬件和 mysqld 的设置以利于适当地调优查询。机器速度慢也就罢了，我曾经见过速度很快的机器在运行设计良好的查询时由于负载过重而失败，因为 mysqld 被大量繁忙的工作所占用而不能服务查询。 记录慢速查询 在一个 SQL 服务器中，数据表都是保存在磁盘上的。索引为服务器提供了一种在表中查找特定数据行的方法，而不用搜索整个表。当必须要搜索整个表时，就称为表扫描。通常 来说，您可能只希望获得表中数据的一个子集，因此全表扫描会浪费大量的磁盘 I/O，因此也就会浪费大量时间。当必须对数据进行连接时，这个问题就更加复杂了，因为必须要对连接两端的多行数据进行比较。 当然，表扫描并不总是会带来问题；有时读取整个表反而会比从中挑选出一部分数据更加有效（服务器进程中查询规划器用来作出这些决定）。如果索引 [...]]]></description>
			<content:encoded><![CDATA[<div class="cnt">
<p>利用服务器的几个调优技巧，让 MySQL 服务器飞速运行<br />
如今，开发人员不断地开发和部署使用 LAMP（Linux®、Apache、MySQL 和 PHP/Perl）架构的应用程序。但是，服务器管理员常常对应用程序本身没有什么控制能力，因为应用程序是别人编写的。这份 共三部分的系列文章 将讨论许多服务器配置问题，这些配置会影响应用程序的性能。本文是本系列文章的第三部分，也是最后一部分，将重点讨论为实现最高效率而对数据库层进行的调 优。</p>
<p>关于 MySQL 调优</p>
<p>有 3 种方法可以加快 MySQL 服务器的运行速度，效率从低到高依次为：<br />
替换有问题的硬件。<br />
对 MySQL 进程的设置进行调优。<br />
对查询进行优化。</p>
<p>替换有问题的硬件通常是我们的第一考虑，主要原因是数据库会占用大量资源。不过这种解决方案也就仅限于此了。实际上，您通常可以让中央处理器（CPU）或磁盘速度加倍，也可以让内存增大 4 到 8 倍。</p>
<p>第二种方法是对 MySQL 服务器（也称为 mysqld）进行调优。对这个进程进行调优意味着适当地分配内存，并让 mysqld 了解将会承受何种类型的负载。加快磁盘运行速度不如减少所需的磁盘访问次数。类似地，确保 MySQL 进程正确操作就意味着它花费在服务查询上的时间要多于花费在处理后台任务（如处理临时磁盘表或打开和关闭文件）上的时间。对 mysqld 进行调优是本文的重点。</p>
<p>最好的方法是确保查询已经进行了优化。这意味着对表应用了适当的索引，查询是按照可以充分利用 MySQL 功能的方式来编写的。尽管本文并没有包含查询调优方面的内容（很多著作中已经针对这个主题进行了探讨），不过它会配置 mysqld 来报告可能需要进行调优的查询。</p>
<p>虽然已经为这些任务指派了次序，但是仍然要注意硬件和 mysqld 的设置以利于适当地调优查询。机器速度慢也就罢了，我曾经见过速度很快的机器在运行设计良好的查询时由于负载过重而失败，因为 mysqld 被大量繁忙的工作所占用而不能服务查询。</p>
<p><strong>记录慢速查询</strong></p>
<p>在一个 SQL 服务器中，数据表都是保存在磁盘上的。索引为服务器提供了一种在表中查找特定数据行的方法，而不用搜索整个表。当必须要搜索整个表时，就称为表扫描。通常 来说，您可能只希望获得表中数据的一个子集，因此全表扫描会浪费大量的磁盘 I/O，因此也就会浪费大量时间。当必须对数据进行连接时，这个问题就更加复杂了，因为必须要对连接两端的多行数据进行比较。</p>
<p>当然，表扫描并不总是会带来问题；有时读取整个表反而会比从中挑选出一部分数据更加有效（服务器进程中查询规划器用来作出这些决定）。如果索引 的使用效率很低，或者根本就不能使用索引，则会减慢查询速度，而且随着服务器上的负载和表大小的增加，这个问题会变得更加显著。执行时间超过给定时间范围 的查询就称为慢速查询。</p>
<p>您可以配置 mysqld 将这些慢速查询记录到适当命名的慢速查询日志中。管理员然后会查看这个日志来帮助他们确定应用程序中有哪些部分需要进一步调查。清单 1 给出了要启用慢速查询日志需要在 my.cnf 中所做的配置。</p>
<p><strong>清单 1. 启用 MySQL 慢速查询日志</strong></p>
<table style="border: 1px solid rgb(153, 153, 153);font-size: 12px;width: 80%" align="center">
<tbody>
<tr>
<td><span style="color: rgb(0, 0, 0)">[mysqld] ; enable the slow query log, default 10 seconds log-slow-queries ; log queries taking longer than 5 seconds long_query_time = 5 ; log queries that don’t use indexes even if they take less than long_query_time ; MySQL 4.1 and newer only log-queries-not-using-indexes</span></td>
</tr>
</tbody>
</table>
<p>这三个设置一起使用，可以记录执行时间超过 5 秒和没有使用索引的查询。请注意有关 log-queries-not-using-indexes 的警告：您必须使用 MySQL 4.1 或更高版本。慢速查询日志都保存在 MySQL 数据目录中，名为 hostname-slow.log。如果希望使用一个不同的名字或路径，可以在 my.cnf 中使用 log-slow-queries = /new/path/to/file 实现此目的。</p>
<p>阅读慢速查询日志最好是通过 mysqldumpslow 命令进行。指定日志文件的路径，就可以看到一个慢速查询的排序后的列表，并且还显示了它们在日志文件中出现的次数。一个非常有用的特性是 mysqldumpslow 在比较结果之前，会删除任何用户指定的数据，因此对同一个查询的不同调用被计为一次；这可以帮助找出需要工作量最多的查询。</p>
<p><strong>对查询进行缓存</strong></p>
<p>很多 LAMP 应用程序都严重依赖于数据库，但却会反复执行相同的查询。每次执行查询时，数据库都必须要执行相同的工作 —— 对查询进行分析，确定如何执行查询，从磁盘中加载信息，然后将结果返回给客户机。MySQL 有一个特性称为查询缓存，它将（后面会用到的）查询结果保存在内存中。在很多情况下，这会极大地提高性能。不过，问题是查询缓存在默认情况下是禁用的。</p>
<p>将 query_cache_size = 32M 添加到 /etc/my.conf 中可以启用 32MB 的查询缓存。</p>
<p>监视查询缓存</p>
<p>在启用查询缓存之后，重要的是要理解它是否得到了有效的使用。MySQL 有几个可以查看的变量，可以用来了解缓存中的情况。清单 2 给出了缓存的状态。</p>
<p><strong>清单 2. 显示查询缓存的统计信息</strong><br />
mysql&gt; SHOW STATUS LIKE ‘qcache%’;<br />
<span>+————————-+————+<br />
| Variable_name&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;   | Value&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;   |<br />
+————————-+————+<br />
| Qcache_free_blocks&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;   | 5216&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;   |<br />
| Qcache_free_memory&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;   | 14640664&nbsp;&nbsp;   |<br />
| Qcache_hits&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;   | 2581646882 |<br />
| Qcache_inserts&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;   | 360210964  |<br />
| Qcache_lowmem_prunes&nbsp;&nbsp;&nbsp;   | 281680433  |<br />
| Qcache_not_cached&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;   | 79740667&nbsp;&nbsp;   |<br />
| Qcache_queries_in_cache | 16927&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;   |<br />
| Qcache_total_blocks&nbsp;&nbsp;&nbsp;&nbsp;   | 47042&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;   |<br />
+————————-+————+<br />
8 rows in set (0.00 sec)</span></p>
<p>这些项的解释如表 1 所示。</p>
<p>表 1. MySQL 查询缓存变量变量名 说明<br />
Qcache_free_blocks  缓存中相邻内存块的个数。数目大说明可能有碎片。FLUSH QUERY CACHE 会对缓存中的碎片进行整理，从而得到一个空闲块。<br />
Qcache_free_memory  缓存中的空闲内存。<br />
Qcache_hits  每次查询在缓存中命中时就增大。<br />
Qcache_inserts  每次插入一个查询时就增大。命中次数除以插入次数就是不中比率；用 1 减去这个值就是命中率。在上面这个例子中，大约有 87% 的查询都在缓存中命中。<br />
Qcache_lowmem_prunes 缓存出现内存不足并且必须要进行清理以便为更多查询提供空间的次数。这个数字最好长时间来看；如果这个数字在不断增长，就表示可能碎片非常严重，或者内 存很少。（上面的 free_blocks 和 free_memory 可以告诉您属于哪种情况）。<br />
Qcache_not_cached  不适合进行缓存的查询的数量，通常是由于这些查询不是 SELECT 语句。<br />
Qcache_queries_in_cache  当前缓存的查询（和响应）的数量。<br />
Qcache_total_blocks  缓存中块的数量。<br />
通常，间隔几秒显示这些变量就可以看出区别，这可以帮助确定缓存是否正在有效地使用。运行 FLUSH STATUS 可以重置一些计数器，如果服务器已经运行了一段时间，这会非常有帮助。</p>
<p>使用非常大的查询缓存，期望可以缓存所有东西，这种想法非常诱人。由于 mysqld 必须要对缓存进行维护，例如当内存变得很低时执行剪除，因此服务器可能会在试图管理缓存时而陷入困境。作为一条规则，如果 FLUSH QUERY CACHE 占用了很长时间，那就说明缓存太大了。</p>
<p>强制限制</p>
<p>您可以在 mysqld 中强制一些限制来确保系统负载不会导致资源耗尽的情况出现。清单 3 给出了 my.cnf 中与资源有关的一些重要设置。</p>
<p><strong>清单 3. MySQL 资源设置</strong><br />
<span>set-variable=max_connections=500<br />
set-variable=wait_timeout=10<br />
max_connect_errors = 100</span></p>
<p>连接最大个数是在第一行中进行管理的。与 Apache 中的 MaxClients 类似，其想法是确保只建立服务允许数目的连接。要确定服务器上目前建立过的最大连接数，请执行 SHOW STATUS LIKE ‘max_used_connections’。</p>
<p>第 2 行告诉 mysqld 终止所有空闲时间超过 10 秒的连接。在 LAMP 应用程序中，连接数据库的时间通常就是 Web 服务器处理请求所花费的时间。有时候，如果负载过重，连接会挂起，并且会占用连接表空间。如果有多个交互用户或使用了到数据库的持久连接，那么将这个值设 低一点并不可取！</p>
<p>最后一行是一个安全的方法。如果一个主机在连接到服务器时有问题，并重试很多次后放弃，那么这个主机就会被锁定，直到 FLUSH HOSTS 之后才能运行。默认情况下，10 次失败就足以导致锁定了。将这个值修改为 100 会给服务器足够的时间来从问题中恢复。如果重试 100 次都无法建立连接，那么使用再高的值也不会有太多帮助，可能它根本就无法连接。</p>
<p><strong>缓冲区和缓存</strong></p>
<p>MySQL 支持超过 100 个的可调节设置；但是幸运的是，掌握少数几个就可以满足大部分需要。查找这些设置的正确值可以通过 SHOW STATUS 命令查看状态变量，从中可以确定 mysqld 的运作情况是否符合我们的预期。给缓冲区和缓存分配的内存不能超过系统中的现有内存，因此调优通常都需要进行一些妥协。</p>
<p>MySQL 可调节设置可以应用于整个 mysqld 进程，也可以应用于单个客户机会话。</p>
<p>服务器端的设置</p>
<p>每个表都可以表示为磁盘上的一个文件，必须先打开，后读取。为了加快从文件中读取数据的过程，mysqld 对这些打开文件进行了缓存，其最大数目由 /etc/mysqld.conf 中的 table_cache 指定。清单 4 给出了显示与打开表有关的活动的方式。</p>
<p><strong>清单 4. 显示打开表的活动</strong><br />
mysql&gt; SHOW STATUS LIKE ‘open%tables’;<br />
<span>+—————+——-+<br />
| Variable_name | Value |<br />
+—————+——-+<br />
| Open_tables&nbsp;&nbsp;   | 5000  |<br />
| Opened_tables | 195&nbsp;&nbsp;   |<br />
+—————+——-+<br />
2 rows in set (0.00 sec)</span></p>
<p>清单 4 说明目前有 5,000 个表是打开的，有 195 个表需要打开，因为现在缓存中已经没有可用文件描述符了（由于统计信息在前面已经清除了，因此可能会存在 5,000 个打开表中只有 195 个打开记录的情况）。如果 Opened_tables 随着重新运行 SHOW STATUS 命令快速增加，就说明缓存命中率不够。如果 Open_tables 比 table_cache 设置小很多，就说明该值太大了（不过有空间可以增长总不是什么坏事）。例如，使用 table_cache = 5000 可以调整表的缓存。</p>
<p>与表的缓存类似，对于线程来说也有一个缓存。 mysqld 在接收连接时会根据需要生成线程。在一个连接变化很快的繁忙服务器上，对线程进行缓存便于以后使用可以加快最初的连接。</p>
<p>清单 5 显示如何确定是否缓存了足够的线程。</p>
<p><strong>清单 5. 显示线程使用统计信息</strong><br />
mysql&gt; SHOW STATUS LIKE ‘threads%’;<br />
<span>+——————-+——–+<br />
| Variable_name&nbsp;&nbsp;&nbsp;&nbsp;   | Value  |<br />
+——————-+——–+<br />
| Threads_cached&nbsp;&nbsp;&nbsp;   | 27&nbsp;&nbsp;&nbsp;&nbsp;   |<br />
| Threads_connected | 15&nbsp;&nbsp;&nbsp;&nbsp;   |<br />
| Threads_created&nbsp;&nbsp;   | 838610 |<br />
| Threads_running&nbsp;&nbsp;   | 3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;   |<br />
+——————-+——–+<br />
4 rows in set (0.00 sec)</span></p>
<p>此处重要的值是 Threads_created，每次 mysqld 需要创建一个新线程时，这个值都会增加。如果这个数字在连续执行 SHOW STATUS 命令时快速增加，就应该尝试增大线程缓存。例如，可以在 my.cnf 中使用 thread_cache = 40 来实现此目的。</p>
<p>关键字缓冲区保存了 MyISAM 表的索引块。理想情况下，对于这些块的请求应该来自于内存，而不是来自于磁盘。清单 6 显示了如何确定有多少块是从磁盘中读取的，以及有多少块是从内存中读取的。</p>
<p><strong>清单 6. 确定关键字效率</strong><br />
mysql&gt; show status like ‘%key_read%’;<br />
<span>+——————-+———–+<br />
| Variable_name&nbsp;&nbsp;&nbsp;&nbsp;   | Value&nbsp;&nbsp;&nbsp;&nbsp;   |<br />
+——————-+———–+<br />
| Key_read_requests | 163554268 |<br />
| Key_reads&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;   | 98247&nbsp;&nbsp;&nbsp;&nbsp;   |<br />
+——————-+———–+<br />
2 rows in set (0.00 sec)</span></p>
<p>Key_reads 代表命中磁盘的请求个数， Key_read_requests 是总数。命中磁盘的读请求数除以读请求总数就是不中比率 —— 在本例中每 1,000 个请求，大约有 0.6 个没有命中内存。如果每 1,000 个请求中命中磁盘的数目超过 1 个，就应该考虑增大关键字缓冲区了。例如，key_buffer = 384M 会将缓冲区设置为 384MB。</p>
<p>临时表可以在更高级的查询中使用，其中数据在进一步进行处理（例如 GROUP BY 字句）之前，都必须先保存到临时表中；理想情况下，在内存中创建临时表。但是如果临时表变得太大，就需要写入磁盘中。清单 7 给出了与临时表创建有关的统计信息。</p>
<p><strong>清单 7. 确定临时表的使用</strong><br />
mysql&gt; SHOW STATUS LIKE ‘created_tmp%’;<br />
<span>+————————-+——-+<br />
| Variable_name&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;   | Value |<br />
+————————-+——-+<br />
| Created_tmp_disk_tables | 30660 |<br />
| Created_tmp_files&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;   | 2&nbsp;&nbsp;&nbsp;&nbsp;   |<br />
| Created_tmp_tables&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;   | 32912 |<br />
+————————-+——-+<br />
3 rows in set (0.00 sec)</span></p>
<p>每次使用临时表都会增大 Created_tmp_tables；基于磁盘的表也会增大 Created_tmp_disk_tables。对于这个比率，并没有什么严格的规则，因为这依赖于所涉及的查询。长时间观察 Created_tmp_disk_tables 会显示所创建的磁盘表的比率，您可以确定设置的效率。 tmp_table_size 和 max_heap_table_size 都可以控制临时表的最大大小，因此请确保在 my.cnf 中对这两个值都进行了设置。</p>
<p>每个会话的设置</p>
<p>下面这些设置针对于每个会话。在设置这些数字时要十分谨慎，因为它们在乘以可能存在的连接数时候，这些选项表示大量的内存！您可以通过代码修改会话中的这些数字，或者在 my.cnf 中为所有会话修改这些设置。</p>
<p>当 MySQL 必须要进行排序时，就会在从磁盘上读取数据时分配一个排序缓冲区来存放这些数据行。如果要排序的数据太大，那么数据就必须保存到磁盘上的临时文件中，并再 次进行排序。如果 sort_merge_passes 状态变量很大，这就指示了磁盘的活动情况。清单 8 给出了一些与排序相关的状态计数器信息。</p>
<p><strong>清单 8. 显示排序统计信息</strong><br />
mysql&gt; SHOW STATUS LIKE “sort%”;<br />
<span>+——————-+———+<br />
| Variable_name&nbsp;&nbsp;&nbsp;&nbsp;   | Value&nbsp;&nbsp;   |<br />
+——————-+———+<br />
| Sort_merge_passes | 1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;   |<br />
| Sort_range&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;   | 79192&nbsp;&nbsp;   |<br />
| Sort_rows&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;   | 2066532 |<br />
| Sort_scan&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;   | 44006&nbsp;&nbsp;   |<br />
+——————-+———+<br />
4 rows in set (0.00 sec)</span></p>
<p>如果 sort_merge_passes 很大，就表示需要注意 sort_buffer_size。例如， sort_buffer_size = 4M 将排序缓冲区设置为 4MB。</p>
<p>MySQL 也会分配一些内存来读取表。理想情况下，索引提供了足够多的信息，可以只读入所需要的行，但是有时候查询（设计不佳或数据本性使然）需要读取表中大量数 据。要理解这种行为，需要知道运行了多少个 SELECT 语句，以及需要读取表中的下一行数据的次数（而不是通过索引直接访问）。实现这种功能的命令如清单 9 所示。</p>
<p><strong>清单 9. 确定表扫描比率</strong><br />
mysql&gt; SHOW STATUS LIKE “com_select”;<br />
<span>+—————+——–+<br />
| Variable_name | Value  |<br />
+—————+——–+<br />
| Com_select&nbsp;&nbsp;&nbsp;   | 318243 |<br />
+—————+——–+<br />
1 row in set (0.00 sec)</span></p>
<p>mysql&gt; SHOW STATUS LIKE “handler_read_rnd_next”;<br />
<span>+———————–+———–+<br />
| Variable_name&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;   | Value&nbsp;&nbsp;&nbsp;&nbsp;   |<br />
+———————–+———–+<br />
| Handler_read_rnd_next | 165959471 |<br />
+———————–+———–+<br />
1 row in set (0.00 sec)</span></p>
<p>Handler_read_rnd_next / Com_select 得出了表扫描比率 —— 在本例中是 521:1。如果该值超过 4000，就应该查看 read_buffer_size，例如 read_buffer_size = 4M。如果这个数字超过了 8M，就应该与开发人员讨论一下对这些查询进行调优了！</p>
<p><strong>3 个必不可少的工具</strong></p>
<p>尽管在了解具体设置时，SHOW STATUS 命令会非常有用，但是您还需要一些工具来解释 mysqld 所提供的大量数据。我发现有 3 个工具是必不可少的；在 参考资料 一节中您可以找到相应的链接。</p>
<p>大部分系统管理员都非常熟悉 top 命令，它为任务所消耗的 CPU 和内存提供了一个不断更新的视图。 mytop 对 top 进行了仿真；它为所有连接上的客户机以及它们正在运行的查询提供了一个视图。mytop 还提供了一个有关关键字缓冲区和查询缓存效率的实时数据和历史数据，以及有关正在运行的查询的统计信息。这是一个很有用的工具，可以查看系统中（比如 10 秒钟之内）的状况，您可以获得有关服务器健康信息的视图，并显示导致问题的任何连接。</p>
<p>mysqlard 是一个连接到 MySQL 服务器上的守护程序，负责每 5 分钟搜集一次数据，并将它们存储到后台的一个 Round Robin Database 中。有一个 Web 页面会显示这些数据，例如表缓存的使用情况、关键字效率、连接上的客户机以及临时表的使用情况。尽管 mytop 提供了服务器健康信息的快照，但是 mysqlard 则提供了长期的健康信息。作为奖励，mysqlard 使用自己搜集到的一些信息针对如何对服务器进行调优给出一些建议。</p>
<p>搜集 SHOW STATUS 信息的另外一个工具是 mysqlreport。其报告要远比 mysqlard 更加复杂，因为需要对服务器的每个方面都进行分析。这是对服务器进行调优的一个非常好的工具，因为它对状态变量进行适当计算来帮助确定需要修正哪些问题。</p>
</div>
]]></content:encoded>
			<wfw:commentRss>http://adam.ofeva.com/blog/60.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>JavaScript 的 new， 好久不见 :P</title>
		<link>http://adam.ofeva.com/blog/59.html</link>
		<comments>http://adam.ofeva.com/blog/59.html#comments</comments>
		<pubDate>Fri, 22 Aug 2008 07:32:18 +0000</pubDate>
		<dc:creator>阿当</dc:creator>
				<category><![CDATA[javascript]]></category>

		<guid isPermaLink="false">http://adam.ofeva.com/?p=59</guid>
		<description><![CDATA[avaScript是一门基于原型的语言，但它却拥有一个 new 操作符使得其看起来象一门经典的面对对象语言。那样也迷惑了程序员们，导致一些有问题的编程模式。 其实你永远不需要在JavaScript使用 new Object()。用字面量的形式{}去取代吧。 同理，不要使用 new Array() ，而代之以字面量[]。JavaScript中的数组并不象Java中的数组那样工作的，使用类似Java的语法只会让你糊涂。 同理不用使用 new Number, new String, 或者 new Boolean。这些的用法只会产生无用的类型封装对象。就直接使用简单的字面量吧。 不要使用 new Function 去创建函数对象。用函数表达式更好。比如： frames[0].onfocus = new Function(”document.bgColor=’antiquewhite’”) 更好的写法是： frames[0].onfocus = function () {document.bgColor = ‘antiquewhite’;}; 第二种形式让脚本编译器更快的看到函数主体，于是其中的语法错误也会更快被检测出来。有时候程序员使用 new Function 是因为他们没有理解内部函数是如何工作的。 selObj.onchange = new Function(”dynamicOptionListObjects[”+dol.index+”].change(this)”); 如果我们让用字符串做函数体，编译器不能看到它们。如果我们用字符串表达式做函数体，我们同样也看不到它们。更好的方式就是不要盲目编程。通过制造一个返回值为函数的函数调用，我们可以明确的按值传递我们想要绑定的值。这允许我们在循环中初始化一系列 selObj 对象。 selObj.onchange = function (i) { return function () { dynamicOptionListObjects[i].change(this); [...]]]></description>
			<content:encoded><![CDATA[<p>avaScript是一门基于原型的语言，但它却拥有一个 new 操作符使得其看起来象一门经典的面对对象语言。那样也迷惑了程序员们，导致一些有问题的编程模式。</p>
<p>其实你永远不需要在JavaScript使用 new Object()。用字面量的形式{}去取代吧。</p>
<p>同理，不要使用 new Array() ，而代之以字面量[]。JavaScript中的数组并不象Java中的数组那样工作的，使用类似Java的语法只会让你糊涂。</p>
<p>同理不用使用 new Number, new String, 或者 new Boolean。这些的用法只会产生无用的类型封装对象。就直接使用简单的字面量吧。</p>
<p>不要使用 new Function 去创建函数对象。用函数表达式更好。比如：</p>
<p><code>frames[0].onfocus = new Function(”document.bgColor=’antiquewhite’”)</code></p>
<p>更好的写法是：</p>
<p><code>frames[0].onfocus = function () {document.bgColor = ‘antiquewhite’;};</code></p>
<p>第二种形式让脚本编译器更快的看到函数主体，于是其中的语法错误也会更快被检测出来。有时候程序员使用 new Function 是因为他们没有理解内部函数是如何工作的。</p>
<p><code>selObj.onchange = new Function(”dynamicOptionListObjects[”+dol.index+”].change(this)”);</code></p>
<p>如果我们让用字符串做函数体，编译器不能看到它们。如果我们用字符串表达式做函数体，我们同样也看不到它们。更好的方式就是不要盲目编程。通过制造一个返回值为函数的函数调用，我们可以明确的按值传递我们想要绑定的值。这允许我们在循环中初始化一系列 selObj 对象。<br />
<code><br />
selObj.onchange = function (i) {<br />
    return function () {<br />
        dynamicOptionListObjects[i].change(this);<br />
    };<br />
}(dol.index);</code></p>
<p>直接对一个函数使用new永远不是一个好主意。比如， new function 对构造新对象没有提供什么优势。<br />
<code><br />
myObj = new function () {<br />
    this.type = ‘core’;<br />
};</code></p>
<p>更好的方式是使用对象字面量，它更轻巧，更快捷。<br />
<code><br />
myObj = {<br />
    type: ‘core’<br />
};</code></p>
<p>假如我们需要创建的对象包含的方法需要访问私有变量或者函数，更好的方式仍然是避免使用new.<br />
<code><br />
var foo = new function() {<br />
    function processMessages(message) {<br />
        alert(”Message: ” + message.content);<br />
    }<br />
    this.init = function() {<br />
        subscribe(”/mytopic”, this, processMessages);<br />
    }<br />
}</code><br />
通过使用 new 去调用函数，对象会持有一个无意义的原型对象。这只会浪费内存而不会带来任何好处。如果我们不使用new，我们就不用在对象链维护一个无用的prototype对象。所以我们可以用（）来正确的调用工厂函数。<br />
<code>var foo = function () {<br />
    function processMessages(message) {<br />
        alert(”Message: ” + message.content);<br />
    }<br />
    return {<br />
        init: function () {<br />
            subscribe(”/mytopic”, this, processMessages);<br />
        }<br />
    };<br />
}();</code><br />
所以原则很简单： 唯一应该要用到new操作符的地方就是调用一个古老的构造器函数的时候。当调用一个构造器函数的时候，是强制要求使用new的。有时候可以来new一下, 有的时候还是不要了吧。 </p>
]]></content:encoded>
			<wfw:commentRss>http://adam.ofeva.com/blog/59.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>理解jQuery对象</title>
		<link>http://adam.ofeva.com/blog/58.html</link>
		<comments>http://adam.ofeva.com/blog/58.html#comments</comments>
		<pubDate>Fri, 22 Aug 2008 05:12:14 +0000</pubDate>
		<dc:creator>阿当</dc:creator>
				<category><![CDATA[javascript]]></category>
		<category><![CDATA[jquery]]></category>

		<guid isPermaLink="false">http://adam.ofeva.com/?p=58</guid>
		<description><![CDATA[学习JQuery时对$(&#8230;)操作后返回的jQuery对象具有如此灵活的操作方法感到惊讶，阅读JQuery源码后逐步明白一些道理。 下面仿照JQuery构造一个超简单的框架，以说明对JQuery的理解。 var jQuery = $ = function(selector){ return new jQuery.fn.init(selector); }; 使用jQuery(&#8230;)和$是一样的，都是返回一个对象，这个对象是由jQuery.fn.init的构造体构造的，这个对象有什么来头呢 ？ jQuery.fn = jQuery.prototype = { init:function(selector){ var elem = document.getElementById(selector); this[0] = elem; this.length = 1; return this; }, each:function(method){ for(var i=0; i&#60;this.length; i++){ method.call(this[i],i); } } }; 可见，返回的是一个数组，所以并没有什么神秘的东西。 但这个数组还不能进行each的操作，需要扩展其原型方法，下面的这句很重要： jQuery.fn.init.prototype = jQuery.fn; 这样，通过$(&#8230;)得到的对象就能进行each调用了。 下面测试一下： &#60;div id=&#34;myid&#34;&#62;测试内容&#60;/div&#62; &#60;script&#62; $(&#34;myid&#34;).each(function(i){ alert(i+&#34;:&#34;+this.id+&#34;:&#34;+this.innerHTML); }); [...]]]></description>
			<content:encoded><![CDATA[<p>学习JQuery时对$(&#8230;)操作后返回的jQuery对象具有如此灵活的操作方法感到惊讶，阅读JQuery源码后逐步明白一些道理。</p>
<p>下面仿照JQuery构造一个超简单的框架，以说明对JQuery的理解。<br />
<code><br />
var jQuery = $ = function(selector){<br />
    return new jQuery.fn.init(selector);<br />
};</code></p>
<p>使用jQuery(&#8230;)和$是一样的，都是返回一个对象，这个对象是由jQuery.fn.init的构造体构造的，这个对象有什么来头呢 ？<br />
<code><br />
jQuery.fn = jQuery.prototype = {<br />
    init:function(selector){<br />
        var elem = document.getElementById(selector);<br />
        this[0] = elem;<br />
        this.length = 1;<br />
        return this;<br />
    },<br />
    each:function(method){<br />
        for(var i=0; i&lt;this.length; i++){<br />
            method.call(this[i],i);<br />
        }<br />
    }<br />
};</code></p>
<p>可见，返回的是一个数组，所以并没有什么神秘的东西。</p>
<p>但这个数组还不能进行each的操作，需要扩展其原型方法，下面的这句很重要：</p>
<p><code> jQuery.fn.init.prototype = jQuery.fn;</code></p>
<p>这样，通过$(&#8230;)得到的对象就能进行each调用了。</p>
<p>下面测试一下：<br />
<code><br />
   &lt;div id=&quot;myid&quot;&gt;测试内容&lt;/div&gt;<br />
   &lt;script&gt;<br />
       $(&quot;myid&quot;).each(function(i){<br />
           alert(i+&quot;:&quot;+this.id+&quot;:&quot;+this.innerHTML);<br />
       });<br />
   &lt;/script&gt; </code></p>
]]></content:encoded>
			<wfw:commentRss>http://adam.ofeva.com/blog/58.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>jquery语法总结和注意事项</title>
		<link>http://adam.ofeva.com/blog/57.html</link>
		<comments>http://adam.ofeva.com/blog/57.html#comments</comments>
		<pubDate>Fri, 22 Aug 2008 04:59:54 +0000</pubDate>
		<dc:creator>阿当</dc:creator>
				<category><![CDATA[javascript]]></category>
		<category><![CDATA[jquery]]></category>

		<guid isPermaLink="false">http://adam.ofeva.com/?p=57</guid>
		<description><![CDATA[1、关于页面元素的引用 通过jquery的$()引用元素包括通过id、class、元素名以及元素的层级关系及dom或者xpath条件等方法，且返回的对象为jquery对象（集合对象），不能直接调用dom定义的方法。 2、jQuery对象与dom对象的转换 只有jquery对象才能使用jquery定义的方法。注意dom对象和jquery对象是有区别的，调用方法时要注意操作的是dom对象还是jquery对象。 普通的dom对象一般可以通过$()转换成jquery对象。 如：$(document.getElementById(&#8220;msg&#8221;))则为jquery对象，可以使用jquery的方法。 由于jquery对象本身是一个集合。所以如果jquery对象要转换为dom对象则必须取出其中的某一项，一般可通过索引取出。 如：$(&#8220;#msg&#8221;)[0]，$(&#8220;div&#8221;).eq(1)[0]，$(&#8220;div&#8221;).get()[1]，$(&#8220;td&#8221;)[5]这些都是dom对象，可以使用dom中的方法，但不能再使用Jquery的方法。 以下几种写法都是正确的： $("#msg").html(); $("#msg")[0].innerHTML; $("#msg").eq(0)[0].innerHTML; $("#msg").get(0).innerHTML; 3、如何获取jQuery集合的某一项 对于获取的元素集合，获取其中的某一项（通过索引指定）可以使用eq或get(n)方法或者索引号获取，要注意，eq返回的是jquery对象，而get(n)和索引返回的是dom元素对象。对于jquery对象只能使用jquery的方法，而dom对象只能使用dom的方法，如要获取第三个 元素的内容。有如下两种方法： $("div").eq(2).html(); //调用jquery对象的方法 $("div").get(2).innerHTML; //调用dom的方法属性 4、同一函数实现set和get Jquery中的很多方法都是如此，主要包括如下几个： $(&#8220;#msg&#8221;).html(); //返回id为msg的元素节点的html内容。 $(&#8220;#msg&#8221;).html(&#8220;new content&#8220;); //将“new content” 作为html串写入id为msg的元素节点内容中,页面显示粗体的new content $(&#8220;#msg&#8221;).text(); //返回id为msg的元素节点的文本内容。 $(&#8220;#msg&#8221;).text(&#8220;new content&#8220;); //将“new content” 作为普通文本串写入id为msg的元素节点内容中,页面显示new content $(&#8220;#msg&#8221;).height(); //返回id为msg的元素的高度 $(&#8220;#msg&#8221;).height(&#8220;300&#8243;); //将id为msg的元素的高度设为300 $(&#8220;#msg&#8221;).width(); //返回id为msg的元素的宽度 $(&#8220;#msg&#8221;).width(&#8220;300&#8243;); //将id为msg的元素的宽度设为300 $(&#8220;input&#8221;).val(&#8220;); //返回表单输入框的value值 $(&#8220;input&#8221;).val(&#8220;test&#8221;); //将表单输入框的value值设为test $(&#8220;#msg&#8221;).click(); //触发id为msg的元素的单击事件 $(&#8220;#msg&#8221;).click(fn); //为id为msg的元素单击事件添加函数 同样blur,focus,select,submit事件都可以有着两种调用方法 [...]]]></description>
			<content:encoded><![CDATA[<p>1、关于页面元素的引用<br />
通过jquery的$()引用元素包括通过id、class、元素名以及元素的层级关系及dom或者xpath条件等方法，且返回的对象为jquery对象（集合对象），不能直接调用dom定义的方法。<br />
<span id="more-57"></span><br />
2、jQuery对象与dom对象的转换<br />
只有jquery对象才能使用jquery定义的方法。注意dom对象和jquery对象是有区别的，调用方法时要注意操作的是dom对象还是jquery对象。<br />
普通的dom对象一般可以通过$()转换成jquery对象。<br />
如：$(document.getElementById(&#8220;msg&#8221;))则为jquery对象，可以使用jquery的方法。<br />
由于jquery对象本身是一个集合。所以如果jquery对象要转换为dom对象则必须取出其中的某一项，一般可通过索引取出。<br />
如：$(&#8220;#msg&#8221;)[0]，$(&#8220;div&#8221;).eq(1)[0]，$(&#8220;div&#8221;).get()[1]，$(&#8220;td&#8221;)[5]这些都是dom对象，可以使用dom中的方法，但不能再使用Jquery的方法。<br />
以下几种写法都是正确的：<br />
<code><br />
$("#msg").html();<br />
$("#msg")[0].innerHTML;<br />
$("#msg").eq(0)[0].innerHTML;<br />
$("#msg").get(0).innerHTML;</code></p>
<p>3、如何获取jQuery集合的某一项<br />
对于获取的元素集合，获取其中的某一项（通过索引指定）可以使用eq或get(n)方法或者索引号获取，要注意，eq返回的是jquery对象，而get(n)和索引返回的是dom元素对象。对于jquery对象只能使用jquery的方法，而dom对象只能使用dom的方法，如要获取第三个
<div>元素的内容。有如下两种方法：<br />
<code><br />
$("div").eq(2).html();               //调用jquery对象的方法<br />
$("div").get(2).innerHTML;       //调用dom的方法属性</code></p>
<p>4、同一函数实现set和get<br />
Jquery中的很多方法都是如此，主要包括如下几个：<br />
$(&#8220;#msg&#8221;).html();               //返回id为msg的元素节点的html内容。<br />
$(&#8220;#msg&#8221;).html(&#8220;<b>new content</b>&#8220;);<br />
//将“<b>new content</b>” 作为html串写入id为msg的元素节点内容中,页面显示粗体的new content</p>
<p>$(&#8220;#msg&#8221;).text();               //返回id为msg的元素节点的文本内容。<br />
$(&#8220;#msg&#8221;).text(&#8220;<b>new content</b>&#8220;);<br />
//将“<b>new content</b>” 作为普通文本串写入id为msg的元素节点内容中,页面显示<b>new content</b></p>
<p>$(&#8220;#msg&#8221;).height();               //返回id为msg的元素的高度<br />
$(&#8220;#msg&#8221;).height(&#8220;300&#8243;);       //将id为msg的元素的高度设为300<br />
$(&#8220;#msg&#8221;).width();               //返回id为msg的元素的宽度<br />
$(&#8220;#msg&#8221;).width(&#8220;300&#8243;);       //将id为msg的元素的宽度设为300</p>
<p>$(&#8220;input&#8221;).val(&#8220;);       //返回表单输入框的value值<br />
$(&#8220;input&#8221;).val(&#8220;test&#8221;);       //将表单输入框的value值设为test</p>
<p>$(&#8220;#msg&#8221;).click();       //触发id为msg的元素的单击事件<br />
$(&#8220;#msg&#8221;).click(fn);       //为id为msg的元素单击事件添加函数<br />
同样blur,focus,select,submit事件都可以有着两种调用方法</p>
<p>5、集合处理功能<br />
对于jquery返回的集合内容无需我们自己循环遍历并对每个对象分别做处理，jquery已经为我们提供的很方便的方法进行集合的处理。<br />
包括两种形式：<br />
<code><br />
$("p").each(function(i){this.style.color=['#f00','#0f0','#00f'][i]})<br />
//为索引分别为0，1，2的p元素分别设定不同的字体颜色。<br />
$("tr").each(function(i){this.style.backgroundColor=['#ccc','#fff'][i%2]})<br />
//实现表格的隔行换色效果<br />
$("p").click(function(){alert($(this).html())})<br />
//为每个p元素增加了click事件，单击某个p元素则弹出其内容<br />
</code><br />
6、扩展我们需要的功能<br />
<code><br />
$.extend({<br />
       min: function(a, b){return a &lt;b&gt; b?a:b; }<br />
});       //为jquery扩展了min,max两个方法<br />
</code></p>
<p>使用扩展的方法（通过“$.方法名”调用）：<br />
<code>alert("a=10,b=20,max="+$.max(10,20)+",min="+$.min(10,20));</code></p>
<p>7、支持方法的连写<br />
所谓连写，即可以对一个jquery对象连续调用各种不同的方法。</code><br />
例如：<br />
<code>$("p").click(function(){alert($(this).html())})<br />
.mouseover(function(){alert('mouse over event')})<br />
.each(function(i){this.style.color=['#f00','#0f0','#00f'][i]});</code></p>
<p>8、操作元素的样式<br />
主要包括以下几种方式：<br />
<code><br />
$("#msg").css("background");               //返回元素的背景颜色<br />
$("#msg").css("background","#ccc")       //设定元素背景为灰色<br />
$("#msg").height(300); $("#msg").width("200");       //设定宽高<br />
$("#msg").css({ color: "red", background: "blue" });//以名值对的形式设定样式<br />
$("#msg").addClass("select");       //为元素增加名称为select的class<br />
$("#msg").removeClass("select");       //删除元素名称为select的class<br />
$("#msg").toggleClass("select");       //如果存在（不存在）就删除（添加）名称为select的class<br />
</code><br />
9、完善的事件处理功能<br />
Jquery已经为我们提供了各种事件处理方法，我们无需在html元素上直接写事件，而可以直接为通过jquery获取的对象添加事件。<br />
如：<br />
<code><br />
$("#msg").click(function(){alert("good")})       //为元素添加了单击事件<br />
$("p").click(function(i){this.style.color=['#f00','#0f0','#00f'][i]})<br />
//为三个不同的p元素单击事件分别设定不同的处理</code><br />
jQuery中几个自定义的事件：<br />
（1）hover(fn1,fn2)：一个模仿悬停事件（鼠标移动到一个对象上面及移出这个对象）的方法。当鼠标移动到一个匹配的元素上面时，会触发指定的第一个函数。当鼠标移出这个元素时，会触发指定的第二个函数。<br />
//当鼠标放在表格的某行上时将class置为over，离开时置为out。<br />
<code><br />
$("tr").hover(function(){<br />
$(this).addClass("over");<br />
},<br />
       function(){<br />
       $(this).addClass("out");<br />
});</code><br />
（2）ready(fn):当DOM载入就绪可以查询及操纵时绑定一个要执行的函数。<br />
<code>$(document).ready(function(){alert("Load Success")})<br />
//页面加载完毕提示“Load Success”,相当于onload事件。与$(fn)等价</code><br />
（3）toggle(evenFn,oddFn): 每次点击时切换要调用的函数。如果点击了一个匹配的元素，则触发指定的第一个函数，当再次点击同一元素时，则触发指定的第二个函数。随后的每次点击都重复对这两个函数的轮番调用。<br />
      <code> //每次点击时轮换添加和删除名为selected的class。<br />
       $("p").toggle(function(){<br />
               $(this).addClass("selected");<br />
       },function(){<br />
               $(this).removeClass("selected");<br />
       });</code><br />
（4）trigger(eventtype): 在每一个匹配的元素上触发某类事件。<br />
例如：<br />
       <code>$("p").trigger("click");               //触发所有p元素的click事件</code><br />
（5）bind(eventtype,fn)，unbind(eventtype): 事件的绑定与反绑定<br />
从每一个匹配的元素中（添加）删除绑定的事件。<br />
例如：<br />
<code>$("p").bind("click", function(){alert($(this).text());});       //为每个p元素添加单击事件<br />
$("p").unbind();       //删除所有p元素上的所有事件<br />
$("p").unbind("click")       //删除所有p元素上的单击事件</code></p>
<p>10、几个实用特效功能<br />
其中toggle()和slidetoggle()方法提供了状态切换功能。<br />
如toggle()方法包括了hide()和show()方法。<br />
slideToggle()方法包括了slideDown()和slideUp方法。</p>
<p>11、几个有用的jQuery方法<br />
<code>$.browser.浏览器类型：检测浏览器类型。有效参数：safari, opera, msie, mozilla。如检测是否ie：$.browser.isie，是ie浏览器则返回true。<br />
$.each(obj, fn)：通用的迭代函数。可用于近似地迭代对象和数组（代替循环）。<br />
如<br />
$.each( [0,1,2], function(i, n){ alert( "Item #" + i + ": " + n ); });<br />
等价于：<br />
var tempArr=[0,1,2];<br />
for(var i=0;i&lt;tempArr.length;i++){<br />
       alert("Item #"+i+": "+tempArr[i]);<br />
}</code><br />
也可以处理json数据，如<br />
<code>$.each( { name: "John", lang: "JS" }, function(i, n){ alert( "Name: " + i + ", Value: " + n ); });</code><br />
结果为：<br />
Name:name, Value:John<br />
Name:lang, Value:JS<br />
$.extend(target,prop1,propN)：用一个或多个其他对象来扩展一个对象，返回这个被扩展的对象。这是jquery实现的继承方式。<br />
如：<br />
<code><br />
$.extend(settings, options);<br />
//合并settings和options，并将合并结果返回settings中，相当于options继承setting并将继承结果保存在setting中。<br />
var settings = $.extend({}, defaults, options);<br />
//合并defaults和options，并将合并结果返回到setting中而不覆盖default内容。<br />
可以有多个参数（合并多项并返回）<br />
$.map(array, fn)：数组映射。把一个数组中的项目(处理转换后)保存到到另一个新数组中，并返回生成的新数组。<br />
如：<br />
var tempArr=$.map( [0,1,2], function(i){ return i + 4; });<br />
tempArr内容为：[4,5,6]<br />
var tempArr=$.map( [0,1,2], function(i){ return i &gt; 0 ? i + 1 : null; });<br />
tempArr内容为：[2,3]<br />
$.merge(arr1,arr2):合并两个数组并删除其中重复的项目。<br />
如：$.merge( [0,1,2], [2,3,4] )       //返回[0,1,2,3,4]<br />
$.trim(str)：删除字符串两端的空白字符。<br />
如：$.trim("   hello, how are you?   ");         //返回"hello,how are you? "<br />
</code><br />
12、解决自定义方法或其他类库与jQuery的冲突<br />
很多时候我们自己定义了$(id)方法来获取一个元素，或者其他的一些js类库如prototype也都定义了$方法，如果同时把这些内容放在一起就会引起变量方法定义冲突，Jquery对此专门提供了方法用于解决此问题。<br />
使用jquery中的jQuery.noConflict();方法即可把变量$的控制权让渡给第一个实现它的那个库或之前自定义的$方法。之后应用 Jquery的时候只要将所有的$换成jQuery即可，如原来引用对象方法$("#msg")改为jQuery("#msg")。<br />
如：<br />
jQuery.noConflict();<br />
// 开始使用jQuery<br />
jQuery("div   p").hide();<br />
// 使用其他库的 $()<br />
$("content").style.display = 'none';</p>
]]></content:encoded>
			<wfw:commentRss>http://adam.ofeva.com/blog/57.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>算法，产品经理的必修课</title>
		<link>http://adam.ofeva.com/blog/56.html</link>
		<comments>http://adam.ofeva.com/blog/56.html#comments</comments>
		<pubDate>Fri, 22 Aug 2008 02:39:42 +0000</pubDate>
		<dc:creator>阿当</dc:creator>
				<category><![CDATA[产品经理的盒子]]></category>
		<category><![CDATA[pm]]></category>

		<guid isPermaLink="false">http://adam.ofeva.com/?p=56</guid>
		<description><![CDATA[你知道IMBD的电影排行算法么？你有兴趣研究一下么？ 加权平均分(WR) = (v ÷ (v+m)) × R + (m ÷ (v+m)) × C 在这里： R = 该电影的平均分 v = 该电影的总投票数 m = 列入前250所需要的最少票数(目前是1300票) C = 数据库中所有电影的总平均分(目前是6.7) 哈哈，厉害吧，投票数多的话，那么R，也就是每个人的电影评分就起到主要作用； 如果投票数少呢，也不能说电影不好，小众的东西还往往是精品呢，文化的东西，毕竟有可能因为语言等因素，或者曲高和寡变得在IMDB上变得小众，这样如果投票数少，那么也不能一棒子打死，就趋于平均水平。 如果票数相同呢，那么就看R了，平均分越高，那么自然排名就越靠前啦。 好玩吧，再看一个。 很多网站在设计博客的排行时，在时间相关性上很困惑，为了满足访问网站频率不同的用户需求，他们只好按日、周、月来做排行，非常明显的缺陷是，让每周或每月的开始时，周榜和月榜上的精彩文章精彩程度不够，而上周末或月末的精彩文章可能被过早撤出用户视线。但是如果按七天排行或者三十天排行，算法又太复杂。 有没有兼顾的算法呢？答案是有的。 假定用户访问周期介于T1到TN，TN=N*T1，用户访问频率在T1到TN之间线性分布。 设文章在每个时间TX里获得的访问量为TXPV，设计系数A=(N-1)/N 使用计算式 T1PV*A^(N-1)+T2PV*A^(N-2)+T3PV*A^(N-3)+……+TNPV=∑TXPV*A^(N-X) 来计算随时间衰减的关注度。 太TMD牛了，佩服的五体投地。 如果算周排行榜，一篇文章在第一天的访问量跟第七天的访问量在权重上明显是不同的，到第七天还有人在访问，那就说明这篇文章更热。 通过这件事情我想，作为一个产品经理，其实应该有这样的基本功力。今天大家在研究热文的的出现和排序算法的时候，我为自己是个理科生感到非常惭愧，对于分析一种算法的改变对于效果的影响上，我表现得跟个文科生一样。倒也不是说文科生同学们就干不了这个事儿，只是说我这么多年的专业训练啊，都tmd练到哪儿去啦？？！！ 作为一个将致力于成为一个有创意的、脚踏实地的、有执行力的、成熟的、经验丰富的、牛B哄哄的产品经理作为毕生奋斗目标的人，我今天的体会是一定要严于律己，有意识的培养自己对于算法的感觉，积极参与工程师同志们的算法讨论，不断修炼，逐渐成仙。]]></description>
			<content:encoded><![CDATA[<p>你知道IMBD的电影排行算法么？你有兴趣研究一下么？</p>
<p><code>加权平均分(WR) = (v ÷ (v+m)) × R + (m ÷ (v+m)) × C<br />
在这里：<br />
R = 该电影的平均分<br />
v = 该电影的总投票数<br />
m = 列入前250所需要的最少票数(目前是1300票)<br />
C = 数据库中所有电影的总平均分(目前是6.7)</code></p>
<p>哈哈，厉害吧，投票数多的话，那么R，也就是每个人的电影评分就起到主要作用；<br />
如果投票数少呢，也不能说电影不好，小众的东西还往往是精品呢，文化的东西，毕竟有可能因为语言等因素，或者曲高和寡变得在IMDB上变得小众，这样如果投票数少，那么也不能一棒子打死，就趋于平均水平。<br />
如果票数相同呢，那么就看R了，平均分越高，那么自然排名就越靠前啦。</p>
<p>好玩吧，再看一个。</p>
<p>很多网站在设计博客的排行时，在时间相关性上很困惑，为了满足访问网站频率不同的用户需求，他们只好按日、周、月来做排行，非常明显的缺陷是，让每周或每月的开始时，周榜和月榜上的精彩文章精彩程度不够，而上周末或月末的精彩文章可能被过早撤出用户视线。但是如果按七天排行或者三十天排行，算法又太复杂。<br />
有没有兼顾的算法呢？答案是有的。</p>
<p><code><br />
假定用户访问周期介于T1到TN，TN=N*T1，用户访问频率在T1到TN之间线性分布。<br />
设文章在每个时间TX里获得的访问量为TXPV，设计系数A=(N-1)/N</code></p>
<p>使用计算式<br />
  <code> T1PV*A^(N-1)+T2PV*A^(N-2)+T3PV*A^(N-3)+……+TNPV=∑TXPV*A^(N-X)</code><br />
来计算随时间衰减的关注度。<br />
太TMD牛了，佩服的五体投地。<br />
如果算周排行榜，一篇文章在第一天的访问量跟第七天的访问量在权重上明显是不同的，到第七天还有人在访问，那就说明这篇文章更热。</p>
<p>通过这件事情我想，作为一个产品经理，其实应该有这样的基本功力。今天大家在研究热文的的出现和排序算法的时候，我为自己是个理科生感到非常惭愧，对于分析一种算法的改变对于效果的影响上，我表现得跟个文科生一样。倒也不是说文科生同学们就干不了这个事儿，只是说我这么多年的专业训练啊，都tmd练到哪儿去啦？？！！</p>
<p>作为一个将致力于成为一个有创意的、脚踏实地的、有执行力的、成熟的、经验丰富的、牛B哄哄的产品经理作为毕生奋斗目标的人，我今天的体会是一定要严于律己，有意识的培养自己对于算法的感觉，积极参与工程师同志们的算法讨论，不断修炼，逐渐成仙。</p>
]]></content:encoded>
			<wfw:commentRss>http://adam.ofeva.com/blog/56.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>使用 jQuery 简化 Ajax 开发</title>
		<link>http://adam.ofeva.com/blog/55.html</link>
		<comments>http://adam.ofeva.com/blog/55.html#comments</comments>
		<pubDate>Thu, 21 Aug 2008 03:52:20 +0000</pubDate>
		<dc:creator>阿当</dc:creator>
				<category><![CDATA[javascript]]></category>
		<category><![CDATA[jquery]]></category>

		<guid isPermaLink="false">http://adam.ofeva.com/?p=55</guid>
		<description><![CDATA[jQuery 是一个JavaScript 库，它有助于简化 JavaScript? 以及 Asynchronous JavaScript + XML (Ajax) 编程。与类似的 JavaScript 库不同，jQuery 具有独特的基本原理，可以简洁地表示常见的复杂代码。学习 jQuery 基本原理，探索其特性和功能，执行一些常见的 Ajax 任务并掌握如何使用插件扩展 jQuery。 jQuery 是什么？ jQuery 由 John Resig 创建于 2006 年初，对于任何使用 JavaScript 代码的程序员来说，它是一个非常有用的 JavaScript 库。无论您是刚刚接触 JavaScript 语言，并且希望获得一个能解决文档对象模型（Document Object Model，DOM）脚本和 Ajax 开发中一些复杂问题的库，还是作为一个厌倦了 DOM 脚本和 Ajax 开发中无聊的重复工作的资深 JavaScript 专家，jQuery 都会是您的首选。 jQuery 能帮助您保证代码简洁易读。您再也不必编写大堆重复的循环代码和 DOM 脚本库调用了。使用 jQuery，您可以把握问题的要点，并使用尽可能最少的代码实现您想要的功能。 毫无疑问，jQuery 的原理是独一无二的：它的目的就是保证代码简洁并可重用。当您理解并体会这一原理后，便可以开始学习本教程了，看看 jQuery 对我们的编程方式有多少改进吧。 一些简单的代码简化 [...]]]></description>
			<content:encoded><![CDATA[<p>   jQuery 是一个JavaScript 库，它有助于简化 JavaScript? 以及 Asynchronous JavaScript + XML (Ajax) 编程。与类似的 JavaScript 库不同，jQuery 具有独特的基本原理，可以简洁地表示常见的复杂代码。学习 jQuery 基本原理，探索其特性和功能，执行一些常见的 Ajax 任务并掌握如何使用插件扩展 jQuery。<br />
<span id="more-55"></span><br />
jQuery 是什么？</p>
<p>jQuery 由 John Resig 创建于 2006 年初，对于任何使用 JavaScript 代码的程序员来说，它是一个非常有用的 JavaScript 库。无论您是刚刚接触 JavaScript 语言，并且希望获得一个能解决文档对象模型（Document Object Model，DOM）脚本和 Ajax 开发中一些复杂问题的库，还是作为一个厌倦了 DOM 脚本和 Ajax 开发中无聊的重复工作的资深 JavaScript 专家，jQuery 都会是您的首选。</p>
<p>jQuery 能帮助您保证代码简洁易读。您再也不必编写大堆重复的循环代码和 DOM 脚本库调用了。使用 jQuery，您可以把握问题的要点，并使用尽可能最少的代码实现您想要的功能。</p>
<p>毫无疑问，jQuery 的原理是独一无二的：它的目的就是保证代码简洁并可重用。当您理解并体会这一原理后，便可以开始学习本教程了，看看 jQuery 对我们的编程方式有多少改进吧。</p>
<p>一些简单的代码简化</p>
<p>下面是一个简单示例，它说明了 jQuery 对代码的影响。要执行一些真正简单和常见的任务，比方说为页面的某一区域中的每个链接附加一个单击（click）事件，您可以使用纯 JavaScript 代码和 DOM 脚本来实现，如 清单 1 所示。</p>
<p>清单 1. 没有使用 jQuery 的 DOM 脚本</p>
<p><code>var external_links = document.getElementById('external_links');<br />
var links = external_links.getElementsByTagName('a');<br />
for (var i=0;i &lt; links.length;i++) {<br />
    var link = links.item(i);<br />
    link.onclick = function() {<br />
        return confirm('You are going to visit: ' + this.href);<br />
    };<br />
}</code></p>
<p>清单 2 显示了使用 jQuery 实现的相同的功能。</p>
<p>清单 2. 使用了 jQuery 的 DOM 脚本</p>
<p><code>$('#external_links a').click(function() {<br />
    return confirm('You are going to visit: ' + this.href);<br />
});<br />
</code></p>
<p>是不是很神奇？ 使用 jQuery，您可以把握问题的要点，只让代码实现您想要的功能，而省去了一些繁琐的过程。无需对元素进行循环，click() 函数将完成这些操作。同样也不需要进行多个 DOM 脚本调用。您只需要使用一个简短的字符串对所需的元素进行定义即可。</p>
<p>理解这一代码的工作原理可能会有一点复杂。首先，我们使用了 $() 函数 —— jQuery 中功能最强大的函数。通常，我们都是使用这个函数从文档中选择元素。在本例中，一个包含有一些层叠样式表（Cascading Style Sheet，CSS）语法的字符串被传递给函数，然后 jQuery 尽可能高效地把这些元素找出来。</p>
<p>如果您具备 CSS 选择器的基本知识，那么应该很熟悉这些语法。在 清单 2 中，#external_links 用于检索 id 为 external_links 的元素。a 后的空格表示 jQuery 需要检索 external_links 元素中的所有 <a> 元素。用英语说起来非常绕口，甚至在 DOM 脚本中也是这样，但是在 CSS 中这再简单不过了</p>
<p>$() 函数返回一个含有所有与 CSS 选择器匹配的元素的 jQuery 对象。jQuery 对象 类似于数组，但是它附带有大量特殊的 jQuery 函数。比方说，您可以通过调用 click 函数把 click 处理函数指定给 jQuery 对象中的所有元素。</p>
<p>还可以向 $() 函数传递一个元素或者一个元素数组，该函数将把这些元素封装在一个 jQuery 对象中。您可能会想要使用这个功能将 jQuery 函数用于一些对象，比方说 window 对象。例如，我们通常会像下面这样把函数分配给加载事件：<br />
<code>window.onload = function() {<br />
    // do this stuff when the page is done loading<br />
};</code></p>
<p>使用 jQuery 编写的功能相同的代码：<br />
<code>$(window).load(function() {<br />
    // run this when the whole page has been downloaded<br />
});</code></p>
<p>您可能有所体会，等待窗口加载的过程是非常缓慢而且令人痛苦的，这是因为必须等整个页面加载完所有的内容，包括页面上所有的的图片。有的时候，您希望首先完成图片加载，但是在大多数情况下，您只需加载超文本标志语言（Hypertext Markup Language，HTML）就可以了。通过在文档中创建特殊的 ready 事件，jQuery 解决了这个问题，方法如下：</p>
<p><code>$(document).ready(function() {<br />
    // do this stuff when the HTML is all ready<br />
});<br />
</code></p>
<p>这个代码围绕 document 元素创建了一个 jQuery 对象，然后建立一个函数，用于在 HTML DOM 文档就绪的时候调用实例。可以根据需要任意地调用这个函数。并且能够以真正的 jQuery 格式，使用快捷方式调用这个函数。这很简单，只需向 $() 函数传递一个函数就可以了：</p>
<p><code>$(function() {<br />
    // run this when the HTML is done downloading<br />
});</code></p>
<p>到目前以止，我已经向大家介绍了 $() 函数的三种用法。第四种方法可以使用字符串来创建元素。结果会产生一个包含该元素的 jQuery 对象。清单 3 显示的示例在页面中添加了一个段落。</p>
<p>清单 3. 创建和附加一个简单的段落<br />
<code>$('
</p>
<p>')<br />
    .html('Hey World!')<br />
    .css('background', 'yellow')<br />
    .appendTo("body");<br />
</code></p>
<p>在前一个例子中您可能已经注意到，jQuery 中的另一个功能强大的特性就是方法链接（method chaining）。每次对 jQuery 对象调用方法时，方法都会返回相同的 jQuery 对象。这意味着如果您需要对 jQuery 对象调用多个方法，那么您不必重新键入选择器就可以实现这一目的：</p>
<p><code>$('#message').css('background', 'yellow').html('Hello!').show();</code></p>
<p>使 Ajax 变得简单</p>
<p>使用 jQuery 将使 Ajax 变得及其简单。jQuery 提供有一些函数，可以使简单的工作变得更加简单，复杂的工作变得不再复杂。</p>
<p>Ajax 最常见的用法就是把一块 HTML 代码加载到页面的某个区域中去。为此，只需简单地选择所需的元素，然后使用 load() 函数即可。下面是一个用于更新统计信息的示例：</p>
<p><code>$('#stats').load('stats.html');</code></p>
<p>通常，我们只需简单地把一些参数传递给服务器中的某个页面。正如您所预料的，使用 jQuery 实现这一操作非常地简单。您可以使用 $.post() 或者 $.get()，这由所需的方法决定。如果需要的话，您还可以传递一个可选的数据对象和回调函数。清单 4 显示了一个发送数据和使用回调的简单示例。</p>
<p>清单 4. 使用 Ajax 向页面发送数据</p>
<p><code>$.post('save.cgi', {<br />
    text: 'my string',<br />
    number: 23<br />
}, function() {<br />
    alert('Your data has been saved.');<br />
});<br />
</code></p>
<p>如果您确实需要编写一些复杂的 Ajax 脚本，那么需要用到 $.ajax() 函数。您可以指定 xml、script、html 或者 json，jQuery 将自动为回调函数准备合适的结果，这样您便可以立即使用该结果。还可以指定 beforeSend、error、success 或者 complete 回调函数，向用户提供更多有关 Ajax 体验的反馈。此外，还有一些其它的参数可供使用，您可以使用它们设置 Ajax 请求的超时，也可以设置页面 “最近一次修改” 的状态。清单 5 显示了一个使用一些我所提到的参数检索 XML 文档的示例。</p>
<p>清单 5. $.ajax() 使 Ajax 由复杂变简单</p>
<p><code>$.ajax({<br />
    url: 'document.xml',<br />
    type: 'GET',<br />
    dataType: 'xml',<br />
    timeout: 1000,<br />
    error: function(){<br />
        alert('Error loading XML document');<br />
    },<br />
    success: function(xml){<br />
        // do something with xml<br />
    }<br />
});</code></p>
<p>当 success 回调函数返回 XML 文档后，您可以使用 jQuery 检索这个 XML 文档，其方式与检索 HTML 文档是一样的。这样使得处理 XML 文档变得相当地容易，并且把内容和数据集成到了您的 Web 站点里面。清单 6 显示了 success 函数的一个扩展，它为 XML 中的每个  元素都添加了一个列表项到 Web 页面中。</p>
<p>清单 6. 使用 jQuery 处理 XML 文档</p>
<p><code>success: function(xml){<br />
    $(xml).find('item').each(function(){<br />
        var item_text = $(this).text();<br />
        $('
<li></li>
<p>')<br />
            .html(item_text)<br />
            .appendTo('ol');<br />
    });<br />
}</code></p>
<p>为 HTML 添加动画</p>
<p>可以使用 jQuery 处理基本的动画和显示效果。animate() 函数是动画代码的核心，它用于更改任何随时间变化的数值型的 CSS 样式值。比方说，您可以变化高度、宽度、不透明度和位置。还可以指定动画的速度，定为毫秒或者预定义的速度：慢速，中速或快速。</p>
<p>下面是一个同时变化某个元素高度和宽度的示例。请注意，这些参数没有开始值，只有最终值。开始值取自元素的当前尺寸。同时我也附加了一个回调函数。</p>
<p><code>$('#grow').animate({ height: 500, width: 500 }, "slow", function(){<br />
    alert('The element is done growing!');<br />
});</code></p>
<p>jQuery 的内置函数使更多常见的动画更容易完成。可以使用 show() 和 hide() 元素，立即显示或者以特定的速度显示。还可以通过使用 fadeIn() 和 fadeOut()，或者 slideDown() 和 slideUp() 显示和隐藏元素，这取决于您所需要的显示效果。下面的示例定义了一个下滑的导航菜单。</p>
<p><code>$('#nav').slideDown('slow');</code></p>
<p>DOM 脚本和事件处理</p>
<p>或许 jQuery 最擅长的就是简化 DOM 脚本和事件处理。遍历和处理 DOM 非常简单，同时附加、移除和调用事件也十分容易，且不像手动操作那样容易出错。</p>
<p>从本质上说，jQuery 可以使 DOM 脚本中的常用操作变得更加容易。您可以创建元素并且使用 append() 函数把它们与其它的一些元素链接到一起，使用 clone() 复制元素，使用 html() 设置内容，使用 empty() 函数删除内容，使用 remove() 函数删除所有的元素，即便是使用 wrap() 函数，用其他元素将这些元素包装起来。</p>
<p>通过遍历 DOM，一些函数可以用于更改 jQuery 对象本身的内容。可以获得元素所有的 siblings()、parents() 和 children()。还可以选择 next() 和 prev() 兄弟元素。find() 函数或许是功能最强大的函数，它允许使用 jQuery 选择器搜索 jQuery 对象中元素的后代元素。</p>
<p>如果结合使用 end() 函数，那么这些函数将变得更加强大。这个函数的功能类似于 undo 函数，用于返回到调用 find() 或 parents() 函数（或者其它遍历函数）之前的 jQuery 对象。</p>
<p>如果配合方法链接（method chaining）一起使用，这些函数可以使复杂的操作看上去非常简单。清单 7 显示了一个示例，其中包含有一个登录表单并处理了一些与之有关的元素。</p>
<p>清单 7. 轻松地遍历和处理 DOM</p>
<p><code>$('form#login')<br />
    // hide all the labels inside the form with the 'optional' class<br />
    .find('label.optional').hide().end()</p>
<p>    // add a red border to any password fields in the form<br />
    .find('input:password').css('border', '1px solid red').end()</p>
<p>    // add a submit handler to the form<br />
    .submit(function(){<br />
        return confirm('Are you sure you want to submit?');<br />
    });</code></p>
<p>不管您是否相信，这个示例只是一行满是空白的被链接的代码。首先，选择登录表单。然后，发现其中含有可选标签，隐藏它们，并调用 end() 返回表单。然后，我创建了密码字段，将其边界变为红色，再次调用 end() 返回表单。最后，我在表单中添加了一个提交事件处理程序。其中尤为有趣的就是（除了其简洁性以外），jQuery 完全优化了所有的查询操作，确保将所有内容很好地链接在一起后，不需要对一个元素执行两次查询。</p>
<p>处理常见事件就像调用函数（比方说 click()、submit() 或 mouseover()）和为其传递事件处理函数一样简单。此外，还可以使用 bind(&#8216;eventname&#8217;, function(){}) 指定自定义的事件处理程序。可以使用 unbind(&#8216;eventname&#8217;) 删除某些事件或者使用 unbind() 删除所有的事件。有关这些函数的使用方法的完整列表，请参阅 参考资料 中的 jQuery 应用程序编程接口（Application Program Interface，API）文档。</p>
<p>释放 jQuery 选择器的强大能量</p>
<p>我们经常会使用 ID 来选择元素，比如 #myid，或者通过类名，比如 div.myclass 来选择元素。然而，jQuery 提供了更为复杂和完整的选择器语法，允许我们在单个选择器中选择几乎所有的元素组合。</p>
<p>jQuery 的选择器语法主要是基于 CSS3 和 XPath 的。对 CSS3 和 XPath 了解的越多，使用 jQuery 时就越加得心应手。有关 jQuery 选择器的完整列表，包括 CSS 和 XPath，请参阅 参考资料 中的链接。</p>
<p>CSS3 包含一些并不是所有浏览器都支持的语法，因此我们很少使用它。然而，我们仍然可以在 jQuery 中使用 CSS3 选择元素，因为 jQuery 具备自己的自定义选择器引擎。比方说，要在表格中的每一个空列中都添加一个横杠，可以使用：:empty 伪选择器（pseudo-selector）：<br />
<code>$('td:empty').html('-');</code></p>
<p>如果需要找出所有不含特定类的元素呢？ CSS3 同样提供了一个语法可以完成这个目的，使用 :not 伪选择器： 如下代码显示了如何隐藏所有不含 required 类的输入内容：</p>
<p><code>$('input:not(.required)').hide();</code></p>
<p>与在 CSS 中一样，可以使用逗号将多个选择器连接成一个。下面是一个同时隐藏页面上所有类型列表的简单示例：</p>
<p><code>$('ul, ol, dl').hide();</code></p>
<p>XPath 是一种功能强大的语法，用于在文档中搜寻元素。它与 CSS 稍有区别，不过它能实现的功能略多于 CSS。要在所有复选框的父元素中添加一个边框，可以使用 XPath 的 /.. 语法：</p>
<p><code>$("input:checkbox/..").css('border', '1px solid #777');</code></p>
<p>jQuery 中也加入了一些 CSS 和 XPath 中没有的选择器。比方说，要使一个表更具可读性，通常可以在表格的奇数行或偶数行中附加一个不同的类名 —— 也可以称作把表分段（striping）。使用 jQuery 不费吹灰之力就可以做到这点，这需要归功于 odd 伪选择器。下面这个例子使用 striped 类改变了表格中所有奇数行的背景颜色：</p>
<p><code>$('table.striped &gt; tr:odd').css('background', '#999999');</code></p>
<p>我们可以看到强大的 jQuery 选择器是如何简化代码的。不论您想处理什么样的元素，不管这个元素是具体的还是模糊的，都有可能找到一种方法使用一个 jQuery选择器对它们进行定义。</p>
<p>使用插件扩展 jQuery</p>
<p>与大多数软件不同，使用一个复杂的 API 为 jQuery 编写插件并不是非常困难。事实上，jQuery 插件非常易于编写，您甚至希望编写一些插件来使代码更加简单。下面是可以编写的最基本的 jQuery 插件：</p>
<p><code>$.fn.donothing = function(){<br />
    return this;<br />
};</code></p>
<p>虽然非常简单，但是还是需要对这个插件进行一些解释。首先，如果要为每一个 jQuery 对象添加一个函数，必须把该函数指派给 $.fn。第二，这个函数必须要返回一个 this（jQuery 对象），这样才不至于打断 方法链接（method chaining）。</p>
<p>可以轻松地在这个示例之上构建。要编写一个更换背景颜色的插件，以替代使用 css(&#8216;background&#8217;)，可以使用下面的代码：</p>
<p><code>$.fn.background = function(bg){<br />
    return this.css('background', bg);<br />
};</code></p>
<p>清注意，可以只从 css() 返回值，因为已经返回了 jQuery 对象。因此，方法链接（method chaining）仍然运作良好。</p>
<p>我建议在需要重复工作的时候使用 jQuery 插件。比方说，如果您需要使用 each() 函数反复执行相同的操作，那么可以使用一个插件来完成。</p>
<p>由于 jQuery 插件相当易于编写，所以有上百种可供你选择使用。jQuery 提供的插件可用于制表、圆角、滑动显示、工具提示、日期选择器，以及我们可以想到的一切效果。有关插件的完整列表，请参阅 参考资料。</p>
<p>最为复杂、使用最为广泛的插件要属界面（Interface），它是一种动画插件，用于处理排序、拖放功能、复杂效果、以及其它有趣和复杂的用户界面（User Interface，UI）。界面对于 jQuery 来说就如 Scriptaculous 对于Prototype 一样。</p>
<p>表单插件也同样流行且非常有用，通过它可以使用 Ajax 在后台中轻松地提交表单。这个插件用于处理一些常见的情况：您需要截获某个表单的提交事件，找出所有不同的输入字段，并使用这些字段构造一个 Ajax 调用。</p>
<p>我只是简要地介绍了使用 jQuery 可能完成的任务。jQuery 使用起来非常有趣，因此我们总是能学到看上去很简单的新技巧和新特性。从刚开始使用 jQuery 的那一刻起，jQuery 便可以完全简化您的 JavaScript 和 Ajax 编程；每学会一点新知识，您的代码就会更简单一点。</p>
<p>学习了 jQuery 之后，我在使用 JavaScript 语言进行编程的同时也获得了许多的乐趣。不用操心所有无聊的内容，我可以专注地编写有趣的内容。使用 jQuery 后，我几乎就告别了编写 for 循环代码的时代。甚至在想到要使用其它 JavaScript 库时，不禁会有所畏缩不前。jQuery 确确实实改变了我对 JavaScript 编程的看法。</p>
<ul>
<li><a href="http://jquery.com/">jQuery</a>：访问 jQuery 主页并下载源代码。</li>
<li><a href="http://docs.jquery.com/DOM/Traversing/Selectors">选择器</a>：获取在 jQuery 中可以使用的选择器的完整列表，包括 CSS3 和 XPath 选择器。</li>
<li><a href="http://docs.jquery.com/Plugins">jQuery 插件</a>：获取可以使用的 jQuery 插件的完整列表。</li>
<li><a href="http://interface.eyecon.ro/">Interface</a>：尝试使用 jQuery 最基本的插件，可用于动画、显示效果、拖放功能和用户界面（UI）。</li>
<li><a href="http://jquery.com/dev/svn/trunk/plugins/form/form.js?format=txt">Form 插件</a>：获取 jQuery 插件，它可用于使用 Ajax 提交表单。</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://adam.ofeva.com/blog/55.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>如何提高页面响应速度</title>
		<link>http://adam.ofeva.com/blog/54.html</link>
		<comments>http://adam.ofeva.com/blog/54.html#comments</comments>
		<pubDate>Thu, 21 Aug 2008 03:10:50 +0000</pubDate>
		<dc:creator>阿当</dc:creator>
				<category><![CDATA[javascript]]></category>
		<category><![CDATA[xhtml+css]]></category>

		<guid isPermaLink="false">http://adam.ofeva.com/?p=54</guid>
		<description><![CDATA[访问速度缓慢的互联网已经成为过去,但页面能瞬时呈现也只是未来才会发生的事情. Christianheilmann的一些技巧，现在就可以令你的网站访问起来更为流畅。 页面首次访问时的延时是网站和Web应用设计中遇到的一个很重要的问题。用户总是感觉页面相应的不够快。 为什么用户会有这样的感觉？也许用户还保留多年前那种按分钟计费的接入网方式的使用习惯（现在的饭店或机场还保持那种接入方式）；也许是因为用户觉得页面要有足够快的响应才能称得上所谓的信息高速路。但在我看来这是好莱坞的责任。在每部好莱坞的动作大片中：敲击一下键盘的同时，电脑屏幕上就会出现超高分辨率，有着密密麻麻数据显示的精美网站操作界面；而且网站上读取一部百科全书也不过短短几毫秒就可以完成。 但在现实生活中却完全不是这么回事,因为无论你如何尽量简化你的页面,延迟还是存在的。而且为了使页面更绚丽，通常会使用flash，但如果滥用flash会严重影响页面的响应速度。作为网络应用，还必须保证页面元素的加载顺序，不会因为页面上未完全加载的元素被用户访问而造成异常。 哪些因素造成网站响应缓慢？ 当谈到提高网站响应速度，最通常的做法就是尽可能的减少网站文件的尺寸。(这也导致针对JavaScript库文件大小的无休止的讨论，但最终也没什么结果。）实际上,有许多因素会影响到网页初次访问的响应速度: * HTML文档的大小。 * 页面中嵌入的脚本、图像、多媒体元素文档的大小。 * HTML页面的复杂程度。(浏览器可以很快的展现简单的页面) * 用户的接入速度 * 会被页面访问的第三方内容所在服务器的访问速度。 * 网站域名及其页面包含的外部域名的ＤＮＳ解析速度。 * 用户计算机的性能。(浏览器会因为系统消耗过多的资源在其他任务上而变得响应缓慢)。 * 服务器的相应速度。 除了以上的技术上的因素会导致网站响应变慢以外，还有一些人为设计上的因素。例如：为了避免页面加载过程中出现的页面区域错乱和无图像显示；页面被设计为需要页面内容完全加载完毕后才一起显示。 让网站响应便快的方法 经过实践，以下经验对于解决由于技术和人为因素造成网站响应变慢的问题会有所帮助。 * 在不影响页面显示质量的前提下，尽可能的优化HTML代码。（这包括在发布页面时，去掉HTML文档中的注释以及冗余的换行标记。但为了保证页面的可读性，这些代码还应当保留在源代码中） * 页面中尽可能少的包含其他外部引用，减少文档之间的依赖。（可以将多个脚本放入一个脚本文件，用CSS的sprite技巧将多个小图片合并为一个大图，这样就只需要加载一次） * 确保你没有从外部服务器上引用第三方的内容：用一个脚本将远程的RSS源缓存在本地。这样不仅可以避免DNS解析所造成的延时，而且也不会因为外部服务器的宕机影响你的服务。 * 尽可能的制定图片及包含图片的元素的尺寸。这样可以避免页面展现时由于图片陆续加载而造成页面元素跳动的现象。 * 在页面的末端加载大的脚本，这样页面的可以在大的脚本加载完成前展示出来。如果把大的脚本在页面头元素中加载，则浏览器会等到脚本完全加载完成后才显示页面内容。 web开发准则与提高响应速度 可惜上文提到的这些计巧与我们通常认为的web开发的准则有冲突。例如减少页面包含文件数量会造成产品的可维护性变差。为了使网站的不用页面（首页，文章页，存档页）保持不同的页面风格最简单的方法是不同类型的页面的特殊的样式存放在他们各自的样式文件中。一个页面可以有一个最基本的样式文件，然后根据页面的不同类型再包含该类型页面的指定的样式文件。 脚本的存放也可以用相同的方法，将功能相近的脚本放到一个文件中。这样有助于代码的维护。你不用察看所有的脚本代码就可以很快的找到你需要的函数。此外，将脚本加入到页面的主体部分通常被认为是不良的编码习惯，因为他将用户的行为控制逻辑嵌入的页面的结构中。 幸好有技术方案可以解决这些问题。 用一个包含来引用多个样式或脚本。 爱德华艾略特提出的解决方法是用一个PHP的脚本将多个CSS样式或脚本整理成单一的档案。这个脚本对于javascript还可以调用DouglasCrockford’s的JSmin进行压缩。这个脚本使用很简单，而且可以缓存归并后的文件，直到这些被归并的文件被修改。这就意味着当你修改被归并的某个文件后，脚本会自动重新打包缓存。这样就很简单的解决了代码维护和页面响应速度间的矛盾。 解决页面加载的问题 另一个棘手的问题是，嵌入在页面头元素的脚本，必要要等页面加载完成后才能被调用。这样就会有些延时 ，而且还会产生问题。 延时是由于浏览器加载，解析和展现文档的方式造成的。当你用window的onload事件加载脚本时，浏览器的处理顺序是这样的： * 解析HTML代码装载外部脚本和样式表 * 执行被解析后的脚本 * 建立HTML的DOM树 * 装载图片和其它外部引用内容 * [...]]]></description>
			<content:encoded><![CDATA[<p>访问速度缓慢的互联网已经成为过去,但页面能瞬时呈现也只是未来才会发生的事情. Christianheilmann的一些技巧，现在就可以令你的网站访问起来更为流畅。</p>
<p>页面首次访问时的延时是网站和Web应用设计中遇到的一个很重要的问题。用户总是感觉页面相应的不够快。<br />
为什么用户会有这样的感觉？也许用户还保留多年前那种按分钟计费的接入网方式的使用习惯（现在的饭店或机场还保持那种接入方式）；也许是因为用户觉得页面要有足够快的响应才能称得上所谓的信息高速路。但在我看来这是好莱坞的责任。在每部好莱坞的动作大片中：敲击一下键盘的同时，电脑屏幕上就会出现超高分辨率，有着密密麻麻数据显示的精美网站操作界面；而且网站上读取一部百科全书也不过短短几毫秒就可以完成。<br />
但在现实生活中却完全不是这么回事,因为无论你如何尽量简化你的页面,延迟还是存在的。而且为了使页面更绚丽，通常会使用flash，但如果滥用flash会严重影响页面的响应速度。作为网络应用，还必须保证页面元素的加载顺序，不会因为页面上未完全加载的元素被用户访问而造成异常。</p>
<p>哪些因素造成网站响应缓慢？</p>
<p>当谈到提高网站响应速度，最通常的做法就是尽可能的减少网站文件的尺寸。(这也导致针对JavaScript库文件大小的无休止的讨论，但最终也没什么结果。）实际上,有许多因素会影响到网页初次访问的响应速度:</p>
<p>    * HTML文档的大小。<br />
    * 页面中嵌入的脚本、图像、多媒体元素文档的大小。<br />
    * HTML页面的复杂程度。(浏览器可以很快的展现简单的页面)<br />
    * 用户的接入速度<br />
    * 会被页面访问的第三方内容所在服务器的访问速度。<br />
    * 网站域名及其页面包含的外部域名的ＤＮＳ解析速度。<br />
    * 用户计算机的性能。(浏览器会因为系统消耗过多的资源在其他任务上而变得响应缓慢)。<br />
    * 服务器的相应速度。</p>
<p>除了以上的技术上的因素会导致网站响应变慢以外，还有一些人为设计上的因素。例如：为了避免页面加载过程中出现的页面区域错乱和无图像显示；页面被设计为需要页面内容完全加载完毕后才一起显示。<br />
让网站响应便快的方法</p>
<p> 经过实践，以下经验对于解决由于技术和人为因素造成网站响应变慢的问题会有所帮助。</p>
<p>    * 在不影响页面显示质量的前提下，尽可能的优化HTML代码。（这包括在发布页面时，去掉HTML文档中的注释以及冗余的换行标记。但为了保证页面的可读性，这些代码还应当保留在源代码中）<br />
    * 页面中尽可能少的包含其他外部引用，减少文档之间的依赖。（可以将多个脚本放入一个脚本文件，用CSS的sprite技巧将多个小图片合并为一个大图，这样就只需要加载一次）<br />
    * 确保你没有从外部服务器上引用第三方的内容：用一个脚本将远程的RSS源缓存在本地。这样不仅可以避免DNS解析所造成的延时，而且也不会因为外部服务器的宕机影响你的服务。<br />
    * 尽可能的制定图片及包含图片的元素的尺寸。这样可以避免页面展现时由于图片陆续加载而造成页面元素跳动的现象。<br />
    * 在页面的末端加载大的脚本，这样页面的可以在大的脚本加载完成前展示出来。如果把大的脚本在页面头元素中加载，则浏览器会等到脚本完全加载完成后才显示页面内容。</p>
<p>web开发准则与提高响应速度</p>
<p>可惜上文提到的这些计巧与我们通常认为的web开发的准则有冲突。例如减少页面包含文件数量会造成产品的可维护性变差。为了使网站的不用页面（首页，文章页，存档页）保持不同的页面风格最简单的方法是不同类型的页面的特殊的样式存放在他们各自的样式文件中。一个页面可以有一个最基本的样式文件，然后根据页面的不同类型再包含该类型页面的指定的样式文件。<br />
 脚本的存放也可以用相同的方法，将功能相近的脚本放到一个文件中。这样有助于代码的维护。你不用察看所有的脚本代码就可以很快的找到你需要的函数。此外，将脚本加入到页面的主体部分通常被认为是不良的编码习惯，因为他将用户的行为控制逻辑嵌入的页面的结构中。</p>
<p>幸好有技术方案可以解决这些问题。</p>
<p>用一个包含来引用多个样式或脚本。</p>
<p> 爱德华艾略特提出的解决方法是用一个PHP的脚本将多个CSS样式或脚本整理成单一的档案。这个脚本对于javascript还可以调用DouglasCrockford’s的JSmin进行压缩。这个脚本使用很简单，而且可以缓存归并后的文件，直到这些被归并的文件被修改。这就意味着当你修改被归并的某个文件后，脚本会自动重新打包缓存。这样就很简单的解决了代码维护和页面响应速度间的矛盾。<br />
解决页面加载的问题</p>
<p> 另一个棘手的问题是，嵌入在页面头元素的脚本，必要要等页面加载完成后才能被调用。这样就会有些延时 ，而且还会产生问题。</p>
<p>延时是由于浏览器加载，解析和展现文档的方式造成的。当你用window的onload事件加载脚本时，浏览器的处理顺序是这样的：</p>
<p>  * 解析HTML代码装载外部脚本和样式表<br />
  * 执行被解析后的脚本<br />
  * 建立HTML的DOM树<br />
  * 装载图片和其它外部引用内容<br />
  * 页面装载完毕</p>
<p> 在大多数情况下这样的加载会比较慢，而有些步骤需要提前。许多聪明的程序员致力于解决这个问题，不时的会有新的解决方案出现。大多数的javascript脚本都会有针对onAvailable或onDocumentReady的事件处理。这类事件会在部分文档装载完成后就会被触发，而不用等到大量图片被加载。但通过实践和反复测试，针对旧的浏览器和操作系统并没有无懈可击的解决方案。但我相信只要我们继续努力，再加上那么点运气一定会找到最终的解决方案的。</p>
<p>对于web应用由于调用未加载完成的元素而导致异常是很致命的问题。如果这类问题时发生在页面美化部分，则会有一些解决方法。</p>
<p>为了解决一次加载过多内容的问题，可以采按需分别加载内容。</p>
<p> 为了更好的装饰页面一次载入大量的内容，往往会产生问题。大量的内容有可能是每个标签中都包含过多的文字或者是一个有四级的导航栏。运用javacript脚本可以很轻松的动态展示这些内容。但是如果脚本被禁用，则这些动态内容就会失去样式而被打乱，无法正确地展示，这当然不是一个好主意。页面也没有必要一次将所有的动态展示内容都一次加载，这样会增加页面载入的负载。</p>
<p> 解决方法是当用户触发标签时再用javascript动态的加载显示的内容。当用户关闭脚本后也会显示一个基本的静态文本。</p>
<p> 用什么方法来加载额外的内容取决于你需要引用的是什么。最简单的方法是动态的生成脚本标签。这是一个很早就有的方法，被用来引入大量的javascript数据集或在页面加载后再引入脚本。</p>
<p><code><br />
function pull(){<br />
var s = document.createElement('script');<br />
s.type = 'text/javascript';<br />
s.src = 'largeJavaScriptBlock.js';<br />
 document.getElementsByTagName('head')[0].appendChild(s);<br />
}<br />
window.onload = pull;<br />
</code></p>
<p>del.icio.us页面就到这种方法来引入由json返回的数据。由于json返回的不过是一堆javascript代码，你可以在页面装载完成后，生成javascript脚本标签包含这些代码；然后执行这些代码原来替换页面中某个元素的内容。Dishy作为封装了json的对象可以让你很轻松的完成这些。另一个列子是不太显眼的Flickr头像，Flickr用json输出显示你最新相片，当用户禁用脚本，哪个位置显示的只是一个链接。</p>
<p>如果你要引入的是非javascript的内容，你可以使用Ajax或者AHAH或者Hijax或者其他不包含xml的ajax（你愿意把称作什么名称都可以）。例如，一个用ajax实现的导航，它甚至可以根据用户需要可选择的加载更为复杂的界面。</p>
<p>Imaging trickery 图像显示技巧<br />
 最后一个方法的渊源会追溯到可能你还没开始接触网站开发时。那时浏览器大战中最为ie的竞争对手苦命的Netscape（但在我看来那时还是不错的）浏览器支持一个自定义的HTML属性‘lowsrc’，这个属性可以指定一个小图片提前加载作为大图载入时的替代显示图片。这样即使用户的接入速度很慢，也可以看到即将呈现图片的一个预览。<br />
 你可以重用这一想法，当页面加载时不要嵌入过大的图片而是载入更为规范化的小图片；等页面加载完成后再用大图片进行替换。你甚至可以简化到开始只载入背景色，然后等页面载入完成再用javascript或者DOM加载原图进行覆盖。<br />
这种方法还适用于你需要从多个服务器取得许多不能被缓存的图片。（例如gravatars）通常可以先载入一个占位的图片，等页面加载完成后再动态取得分散在其他服务器上的小图片。</p>
]]></content:encoded>
			<wfw:commentRss>http://adam.ofeva.com/blog/54.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>CSS 清除浮动的三种方式</title>
		<link>http://adam.ofeva.com/blog/53.html</link>
		<comments>http://adam.ofeva.com/blog/53.html#comments</comments>
		<pubDate>Tue, 19 Aug 2008 08:39:45 +0000</pubDate>
		<dc:creator>阿当</dc:creator>
				<category><![CDATA[xhtml+css]]></category>

		<guid isPermaLink="false">http://adam.ofeva.com/?p=53</guid>
		<description><![CDATA[用空标签清除 .clr {clear: both;} &#60;div id=&#34;layout&#34;&#62; &#60;div id=&#34;left&#34;&#62;Left&#60;/div&#62; &#60;div id=&#34;right&#34;&#62;Right&#60;/div&#62; &#60;p class=&#34;clr&#34;&#62;&#60;/p&#62; &#60;/div&#62; 使用 overflow 属性 #layout {overflow:auto; zoom:1;} &#60;div id=&#34;layout&#34;&#62; &#60;div id=&#34;left&#34;&#62;Left&#60;/div&#62; &#60;div id=&#34;right&#34;&#62;Right&#60;/div&#62; &#60;/div&#62; 使用 :after （非 IE 浏览器） #layout:after{ display: block; clear: both; content: &#34;&#34;; visibility:hidden; height: 0; } &#60;div id=&#34;layout&#34;&#62; &#60;div id=&#34;left&#34;&#62;Left&#60;/div&#62; &#60;div id=&#34;right&#34;&#62;Right&#60;/div&#62; &#60;/div&#62; 注：使用 :after 需要注意几点，设置高度为零（height: 0;）；content 是必须的，但其值可以为空。]]></description>
			<content:encoded><![CDATA[<p>用空标签清除<br />
<code><br />
.clr {clear: both;}<br />
&lt;div id=&quot;layout&quot;&gt;<br />
    &lt;div id=&quot;left&quot;&gt;Left&lt;/div&gt;<br />
    &lt;div id=&quot;right&quot;&gt;Right&lt;/div&gt;<br />
    &lt;p class=&quot;clr&quot;&gt;&lt;/p&gt;<br />
&lt;/div&gt;<br />
</code><br />
使用 overflow 属性</p>
<p><code><br />
#layout {overflow:auto; zoom:1;}<br />
&lt;div id=&quot;layout&quot;&gt;<br />
    &lt;div id=&quot;left&quot;&gt;Left&lt;/div&gt;<br />
    &lt;div id=&quot;right&quot;&gt;Right&lt;/div&gt;<br />
&lt;/div&gt;<br />
</code><br />
使用 :after （非 IE 浏览器）<br />
<code><br />
#layout:after{<br />
    display: block;<br />
    clear: both; content: &quot;&quot;;<br />
    visibility:hidden; height: 0;<br />
}<br />
&lt;div id=&quot;layout&quot;&gt;<br />
    &lt;div id=&quot;left&quot;&gt;Left&lt;/div&gt;<br />
    &lt;div id=&quot;right&quot;&gt;Right&lt;/div&gt;<br />
&lt;/div&gt;<br />
</code><br />
注：使用 :after 需要注意几点，设置高度为零（height: 0;）；content 是必须的，但其值可以为空。</p>
]]></content:encoded>
			<wfw:commentRss>http://adam.ofeva.com/blog/53.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>用户是一个绝顶聪明的傻子！</title>
		<link>http://adam.ofeva.com/blog/52.html</link>
		<comments>http://adam.ofeva.com/blog/52.html#comments</comments>
		<pubDate>Tue, 19 Aug 2008 01:06:19 +0000</pubDate>
		<dc:creator>阿当</dc:creator>
				<category><![CDATA[UI/UE]]></category>

		<guid isPermaLink="false">http://adam.ofeva.com/?p=52</guid>
		<description><![CDATA[在写这篇文章之前，我一直困惑于如何表达自己的思想和如何让看到这篇文章的读者完全理解我要表达的含义？于是写了又删，删了又写，最后是在一次与天下网产品总监李天锋先生进行用户体验交流时获得了灵感（李天锋先生是无线互联网的前辈，有着多年的互联网从业经验和多年的无线职业经历，对无线互联网有独到的理解，并长期对用户体验进行研究；也是我职业生涯的老师；他的设计理念：把用户当成一个绝顶聪明的傻子。）正是他的这句话让我对用户体验进行重新思考和诠释。 我不想从物理学角度对产品结构元素进行用户体验分析，因为那种分析过于技术化，而是从用户能够感受得到的角度进行分析（做用户体验的原则：坚决做用户感受得到，绝不做用户感受不到的——控制成本的有效办法），把用户体验划分为四步（用户体验四步曲）： 第一步视觉体验。视觉体验是用户对界面设计的第一感官体验。它是完全是用户几乎没有经过思考前的一种视觉感受，如何给用户提供一种近于完美的视觉体验，需要考虑到用户看到界面时可能出现的任何一种感受，这就提出UI设计师需要对目标用户偏爱的准确了解和把握，精确传达需要传达的含义，才会获得预期的效果；视觉体验直接影响到用户是否停留在此页面进行一下步的表达理解。注意：任何一种让用户感官视觉不适的界面都可能导致用户产生怨言和流失。 第二步表达体验。表达体验是用户在视觉体验的基础上对界面内容的理解，界面内容可能是文字，也可能是图案，这些内容是引导用户进行操作的“提示或说明”，是需要用户进行理解的内容，如何给用户提供一种简单直白有趣易懂的表达体验，需要产品设计人员考虑到用户对需要理解的内容可能出现的任何一种理解进行预估，消除一些传达出错误信息的内容，因为这些内容都是单向的表达，没有解释和补充的机会来纠正用户瞬间的理解；表达体验直接影响到用户下一步的操作行为是否合理。注意：任何一种让用户产生误解的表达都可能导致用户产生怨言和流失。 第三步操作体验。操作体验是用户通过对表达内容的理解后做出的行为动作的体验过程。功能逻辑是操作体验的直接影响因素，如何优化用户的操作过程是产品设计人员需要思考的核心问题之一，既需要考虑正常的逻辑，又需要考虑非正常的逻辑形式，尽可能考虑到用户可能出现的任何一种行为方式，才能保证用户进行正确的愉悦的操作过程；操作体验直接影响到用户下一步的内容体验。注意：任何一种繁琐和复杂的操作逻辑都可能导致用户产生怨言和流失。 第四步结果体验。结果体验是用户通过操作行为获得的行为结果的一种体验过程。用户的任何一种操作可能出现的任何一个行为结果都直接影响到用户结果体验。如果通过用户的任何行为来预估用户的准确需求是产品设计人员需要思考的核心问题之一，这就要求涉及好每一种用户行为可能出现的任何一种结果，并预测每一种结果是否能够满足用户的需求，如果不能，如何采用其他方式来满足用户的行为需求。注意：任何一种不能满足用户需求的操作结果都可能导致用户产生怨言和流失。 总结：用户是绝顶聪明的天才，因为他可能发现任何一个细微的不足；用户是傻子，因为任何一个微不足道的细节失误都可能导致用户出现挫败感；每一个细节看起来都微不足道，但是每一个细节的综合就会促使整个产品趋于完美，也许用户只是感动于某个细节，但他接受你一定是认同你的全部！ 原文：http://column.iresearch.cn/u/yuxuejun/archives/2008/32363.shtml]]></description>
			<content:encoded><![CDATA[<p>在写这篇文章之前，我一直困惑于如何表达自己的思想和如何让看到这篇文章的读者完全理解我要表达的含义？于是写了又删，删了又写，最后是在一次与天下网产品总监李天锋先生进行用户体验交流时获得了灵感（李天锋先生是无线互联网的前辈，有着多年的互联网从业经验和多年的无线职业经历，对无线互联网有独到的理解，并长期对用户体验进行研究；也是我职业生涯的老师；他的设计理念：把用户当成一个绝顶聪明的傻子。）正是他的这句话让我对用户体验进行重新思考和诠释。</p>
<p>我不想从物理学角度对产品结构元素进行用户体验分析，因为那种分析过于技术化，而是从用户能够感受得到的角度进行分析（做用户体验的原则：坚决做用户感受得到，绝不做用户感受不到的——控制成本的有效办法），把用户体验划分为四步（用户体验四步曲）：</p>
<p><strong>第一步视觉体验</strong>。视觉体验是用户对界面设计的第一感官体验。它是完全是用户几乎没有经过思考前的一种视觉感受，如何给用户提供一种近于完美的视觉体验，需要考虑到用户看到界面时可能出现的任何一种感受，这就提出UI设计师需要对目标用户偏爱的准确了解和把握，精确传达需要传达的含义，才会获得预期的效果；视觉体验直接影响到用户是否停留在此页面进行一下步的表达理解。注意：任何一种让用户感官视觉不适的界面都可能导致用户产生怨言和流失。</p>
<p><strong>第二步表达体验</strong>。表达体验是用户在视觉体验的基础上对界面内容的理解，界面内容可能是文字，也可能是图案，这些内容是引导用户进行操作的“提示或说明”，是需要用户进行理解的内容，如何给用户提供一种简单直白有趣易懂的表达体验，需要产品设计人员考虑到用户对需要理解的内容可能出现的任何一种理解进行预估，消除一些传达出错误信息的内容，因为这些内容都是单向的表达，没有解释和补充的机会来纠正用户瞬间的理解；表达体验直接影响到用户下一步的操作行为是否合理。注意：任何一种让用户产生误解的表达都可能导致用户产生怨言和流失。</p>
<p><strong>第三步操作体验</strong>。操作体验是用户通过对表达内容的理解后做出的行为动作的体验过程。功能逻辑是操作体验的直接影响因素，如何优化用户的操作过程是产品设计人员需要思考的核心问题之一，既需要考虑正常的逻辑，又需要考虑非正常的逻辑形式，尽可能考虑到用户可能出现的任何一种行为方式，才能保证用户进行正确的愉悦的操作过程；操作体验直接影响到用户下一步的内容体验。注意：任何一种繁琐和复杂的操作逻辑都可能导致用户产生怨言和流失。</p>
<p><strong>第四步结果体验</strong>。结果体验是用户通过操作行为获得的行为结果的一种体验过程。用户的任何一种操作可能出现的任何一个行为结果都直接影响到用户结果体验。如果通过用户的任何行为来预估用户的准确需求是产品设计人员需要思考的核心问题之一，这就要求涉及好每一种用户行为可能出现的任何一种结果，并预测每一种结果是否能够满足用户的需求，如果不能，如何采用其他方式来满足用户的行为需求。注意：任何一种不能满足用户需求的操作结果都可能导致用户产生怨言和流失。</p>
<p>总结：用户是绝顶聪明的天才，因为他可能发现任何一个细微的不足；用户是傻子，因为任何一个微不足道的细节失误都可能导致用户出现挫败感；每一个细节看起来都微不足道，但是每一个细节的综合就会促使整个产品趋于完美，也许用户只是感动于某个细节，但他接受你一定是认同你的全部！</p>
<p>原文：http://column.iresearch.cn/u/yuxuejun/archives/2008/32363.shtml</p>
]]></content:encoded>
			<wfw:commentRss>http://adam.ofeva.com/blog/52.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>段正淳的css笔记</title>
		<link>http://adam.ofeva.com/blog/51.html</link>
		<comments>http://adam.ofeva.com/blog/51.html#comments</comments>
		<pubDate>Mon, 18 Aug 2008 15:35:48 +0000</pubDate>
		<dc:creator>阿当</dc:creator>
				<category><![CDATA[xhtml+css]]></category>

		<guid isPermaLink="false">http://adam.ofeva.com/?p=51</guid>
		<description><![CDATA[试想过总结出这几年来写css与xhtml的经验 ，汇总成一片”旷世奇文”分享给大家。无奈寡人年世已高，真是有点力不从心了。于是转念一想，可以用笔记的形式展现，这样就不用担心写不出来了。 现在就来说个淘宝首页上的一个小技巧。 类目之间的横竖线 从很久很久以前开始，类目间的竖线无非都只有三种。 背景图在a标签设置一个padding 用宽1px高不等的背景图来position到右侧。缺点：最后一个还是要用class来隐藏掉背景。 符号在每个a标签之间用”&#124;”符号来填充。缺点：html文件变大，文件维护变得很麻烦，而且在html中毫无意义。 a标签右侧的boder。同背景图一样，只不过使用border-right来代替。缺点也同上。 看到这里，可能已经有人打开淘宝首页用firebug查看源码来看是怎么做了。 其实现有是利用ul的overflow:hidden 再将li的margin-left:-1px的做法做出来的。这样的做法就可以同时避免以上的缺点了。为什么之前都没有那么做的，寡人也不知道了。难道寡人是第一个发现这样的做法？不管是谁先此之前利用了这样的方法实现类目间竖线。不过在淘宝首页上线后不久就有同行的网站在首页改版中也用了这样的方法。那个网站不看也罢。class实在是写的有点多。加载html会变得多得多。反正一个首页需要加载1.17m的网页我的大脑会自动屏蔽。 圆角的做法. 为了这个圆角,前段开发们付出的努力是在是太多了.又要考虑http连接数,又要考虑css与html的代码量与语义.贴出的是最近考虑替换现有圆角做法的方案,可能还有许多未考虑的状况.但是大体的编写方式便是如下.好处是便于维护,只有一个图片.还可以某种程度上的任意缩放.缺点是多了无意义的html代码. css：.c,.c i,.c i i,.c b,.c b b,.c p{background-image:url(http://pics.taobao.com/bao/album/promotion/uiblog/purple.png);/*背景图片*/background-repeat:no-repeat;}.c{width:200px;/*临时定的宽度*/background-position:0 -4px;}.c i{display:block;height:4px;}.c i i{margin:0 0 0 4px;background-position:right 0;}.c b{display:block;height:4px;background-position:0 bottom;}.c b b{margin:0 0 0 4px;background-position:right bottom;}.c p{margin:0 0 0 4px;padding:0 4px 0 0;background-position:right -4px;} html：&#60; div class="c"&#62;&#60; i&#62;&#60; i&#62;&#60; /i&#62;&#60; /i&#62;&#60; p&#62;按钮按钮按钮按钮按钮按钮按按按按按钮按钮按钮按钮按钮按钮按钮按钮按钮按钮按钮按钮&#60; /p&#62;&#60; [...]]]></description>
			<content:encoded><![CDATA[<p>试想过总结出这几年来写css与xhtml的经验 ，汇总成一片”旷世奇文”分享给大家。无奈寡人年世已高，真是有点力不从心了。于是转念一想，可以用笔记的形式展现，这样就不用担心写不出来了。</p>
<p>现在就来说个淘宝首页上的一个小技巧。</p>
<p><strong>类目之间的横竖线</strong></p>
<p align="center"><img src="http://www.blueidea.com/articleimg/2007/10/5008/01.jpg" border="0" width="300" height="150"></p>
<p>从很久很久以前开始，类目间的竖线无非都只有三种。</p>
<ol>
<li>背景图<br />在a标签设置一个padding 用宽1px高不等的背景图来position到右侧。<br />缺点：最后一个还是要用class来隐藏掉背景。</li>
<li>符号<br />在每个a标签之间用”|”符号来填充。<br />缺点：html文件变大，文件维护变得很麻烦，而且在html中毫无意义。</li>
<li>a标签右侧的boder。<br />同背景图一样，只不过使用border-right来代替。缺点也同上。</li>
</ol>
<p>看到这里，可能已经有人打开淘宝首页用firebug查看源码来看是怎么做了。</p>
<p>其实现有是利用ul的overflow:hidden 再将li的margin-left:-1px的做法做出来的。这样的做法就可以同时避免以上的缺点了。<br />为什么之前都没有那么做的，寡人也不知道了。难道寡人是第一个发现这样的做法？<br />不管是谁先此之前利用了这样的方法实现类目间竖线。<br />不过在淘宝首页上线后不久就有同行的网站在首页改版中也用了这样的方法。<br />那个网站不看也罢。class实在是写的有点多。加载html会变得多得多。<br />反正一个首页需要加载1.17m的网页我的大脑会自动屏蔽。</p>
<p><strong>圆角的做法.</strong></p>
<p>为了这个圆角,前段开发们付出的努力是在是太多了.又要考虑http连接数,又要考虑css与html的代码量与语义.<br />贴出的是最近考虑替换现有圆角做法的方案,可能还有许多未考虑的状况.但是大体的编写方式便是如下.<br />好处是便于维护,只有一个图片.还可以某种程度上的任意缩放.缺点是多了无意义的html代码.</p>
<p><code>
<p>css：<br /><span class="code">.c,.c i,.c i i,.c b,.c b b,.c p{<br />background-image:url(http://pics.taobao.com/bao/album/promotion/uiblog/purple.png);/*背景图片*/<br />background-repeat:no-repeat;<br />}<br />.c{<br />width:200px;/*临时定的宽度*/<br />background-position:0 -4px;<br />}<br />.c i{<br />display:block;<br />height:4px;<br />}<br />.c i i{<br />margin:0 0 0 4px;<br />background-position:right 0;<br />}<br />.c b{<br />display:block;<br />height:4px;<br />background-position:0 bottom;<br />}<br />.c b b{<br />margin:0 0 0 4px;<br />background-position:right bottom;<br />}<br />.c p{<br />margin:0 0 0 4px;<br />padding:0 4px 0 0;<br />background-position:right -4px;<br />}</span></p>
<p>html：<br /><span class="code">&lt; div class="c"&gt;<br />&lt; i&gt;&lt; i&gt;&lt; /i&gt;&lt; /i&gt;<br />&lt; p&gt;<br />按钮按钮按钮按钮按钮按钮<br />按按按按按钮按钮按钮按钮按钮按钮按钮按钮按钮按钮按钮按钮<br />&lt; /p&gt;<br />&lt; b&gt;&lt; b&gt;&lt; /b&gt;&lt; /b&gt;<br />&lt; /div&gt;</span></p>
<p><strong>table的全局定义</strong></p>
<p></code></p>
<p>caption这个标签在firefox下会有左边有1px空隙的bug,很讨厌.能想到的简单的方法只有-1px的margin了.</p>
<p>css：<br /><span class="code">table{<br />border-collapse:collapse;<br />}<br />table caption,table td,table th{<br />border:1px solid #a2bbdd;/*边框颜色*/<br />background:#c3d9ff;/*背景颜色*/<br />}<br />table caption{<br />text-align:left;<br />border-bottom:none;<br />margin-left:-1px;<br />}</span></p>
<p>html：<br /><span class="code">&lt; table&gt;<br />&lt; caption&gt;表格标题&lt; /caption&gt;<br />&lt; tr&gt;<br />&lt; th&gt;标题&lt; /th&gt;<br />&lt; th&gt;标题&lt; /th&gt;<br />&lt; th&gt;标题&lt; /th&gt;<br />&lt; th&gt;标题&lt; /th&gt;<br />&lt; /tr&gt;<br />&lt; tr&gt;<br />&lt; td&gt; 内容&lt; /td&gt;<br />&lt; td&gt; 内容&lt; /td&gt;<br />&lt; td&gt; 内容&lt; /td&gt;<br />&lt; td&gt; 内容&lt; /td&gt;<br />&lt; /tr&gt;<br />&lt; /table&gt;</span></p>
<p><strong>需要正视的二个标签</strong></p>
<ol>
<li>acronym这个标签用来解释名词很爽,但是用得太少.(我也一直想用来着,所以记下了.) <br />css：<br /><span class="code" style="width: 442px;height: 30px">acronym{cursor:help}</span><br />html：<br /><span class="code" style="width: 441px;height: 84px">&lt; acronym title=&#8221;段正淳又是金庸笔下一个十分奇特的人物。他奇特在到处留情，情人极多，见一个爱一个，而又绝不是徒然风流薄幸，当他是单独对着一个情人的时候，他真是真心真意爱这个情人的，只好说这个人的感情特别丰富，别无其他解释。&#8221; &gt;文字&lt; /acronym&gt;</span></li>
<li>ins这个标签忘记是在哪个网站上看到过用来在h2里显示更多的链接,后来查了书,大家都觉得有点欠妥,有点争议.<br />css：<br />还未写入css组件…欠奉上了<br />html：<br />
<code><br />
<h2>标题<ins><a>更多&gt;&gt;</a></ins></h2>
<p></code></p>
<p><strong>标题右侧“更多”的实现</strong></p>
<p align="center"><img src="/articleimg/2007/10/5008/02.gif" border="0" width="236" height="100"></p>
<p align="left">曾经做上图所示的效果,会使用到position来相对定位到h2标签的右侧.这样的做法,代码确实会多好几行. 其实可以用个笨一点的办法来实现的：</p>
<p>譬如html代码如下：</p>
<p><span class="code">&lt; h2&gt;&lt; a h ref=&#8221;#&#8221; &gt;标题&lt; /a&gt; &lt; span&gt;更多…&lt; /span&gt; &lt; /h2&gt;</span></p>
<p>使用potsition的css差不多如下：</p>
<p><span class="code">h2{<br />position:relative;<br />height:20px;<br />}<br />span{<br />position:absolute;<br />right:0;<br />top:0;<br />display:block;<br />height:20px;<br />}</span></p>
<p>这样才能实现更多在右侧.其实真的还可以更简单:</p>
<p><code>
<p><span class="code">h2{<br />height:20px;<br />}<br />span{<br />float:right;<br />display:block;<br />margin:-10px 0 0 0;<br />height:20px;<br />}</span></p>
<p></code></p>
<p>其实只是利用了margin-top 的负数来实现,因为默认的float会换行到h2标签下面去，所以让它自个跳上去。大致代码就是如此了,是不是很简单?我说很简单嘛！由于很简单,所以就不放出单独的测试页面了.</p>
<p>ps:我说咱们啥时候也得搞个和蓝色理想一样的编辑器吧…</p>
<p><strong>淘宝的css属性顺序书写规范</strong></p>
<p>以前部门的同事们，每个人都有一套书写的规范，导致看对方的css代码非常吃力，所以就推行了一套书写标准 ，或许对您也有帮助。</p>
<p><code>*{<br />
/*显示属性*/<br />
display<br />
position<br />
float<br />
clear<br />
cursor<br />
/*盒模型*/<br />
margin<br />
padding<br />
width<br />
height<br />
/*排版*/<br />
vertical-align<br />
white-space<br />
text-decoration<br />
text-align</p>
<p>/*文字*/<br />
color<br />
font<br />
content</p>
<p>/*边框背景 为什么要把 boder和background放在最后的原因是修改的频率会较之前的频繁，放在最后查看起来方便，哈哈。*/<br />
border<br />
background<br />
}</code></p>
<p>说到底其实属性的书写顺序规范就是：神仙?妖怪? &#8211; 身材怎么样！- 服装类型(比基尼?棉袄?) &#8211; 服装款式(黑色?白色?纽扣?拉链?) &#8211; 用了啥化妆品和发型.<br />这个书写标准小部分并不是浏览器厂商推行的书写规范，所以可能被广大标准推广者所不认同 。但这些个都是弟兄几个实践出来认为最符合现有淘宝环境的。</p>
<p><strong>css代码的简写</strong></p>
<p>css缩写的语法，对新手有一定帮助,老鸟就不用看了.</p>
<ol>
<li>0px不需要单位，直接:margin:0</li>
<li>盒模型的缩写，语法是margin:上 右 下 左;.甚至可以简写成margin:上 (右左) 下,当然右左的值应该是一样的</li>
<li>css属性的最后一项”;”号省略。（不建议 ^_^）</li>
<li>字体宽度normal用400代替,bold用700代替。</li>
<li>16进制的色彩值，如果每两位的值相同，可以缩写一半，例如：#000000可以缩写为#000;#0044DD可以缩写为#04D;</li>
<li>border边框的缩写，语法是border:width style color,类似boder:1px solid red;</li>
<li>背景background的缩写,语法是color image repeat attachment position.类似:background:#f00 url(background.gif) no-repeat fixed 0 0( 为什么我从不写fixed呢?)</li>
<li>字体的缩写,类似font:italic small-caps bold 1em/140% “SimSun”,sans-serif,可以省略到最简单font:12px “SimSun”.</li>
<li>list的属性缩写,语法list-style:square inside url(image.gif) ,不过一般咱们都不用.</li>
<li>想凑10条, 还真难.就把删除无用换行符和空格算一个吧</li>
</ol>
<p>一天大家在团队中讨论“未知图片垂直居中”的问题，突发奇想用vertical-align:middle这个属性来实现，于是抽了时间做了下面这个不成熟的例子：</p>
<p><code>
<p>CSS:</p>
<p><span class="code">&nbsp;div{<br />&nbsp; width:140px;<br />&nbsp; height:140px;<br />&nbsp; text-indent:-8px;<br />&nbsp; text-align:center;<br />&nbsp; line-height:138px;<br />&nbsp; background:red;<br />&nbsp; font-size:12px;<br />&nbsp; *font-size:120px;<br />&nbsp; *text-indent:-60px;<br />}<br />img{<br />&nbsp; width:100px;<br />&nbsp; height:100px;<br />&nbsp; vertical-align:middle;<br />} </span></p>
<p>HTML:</p>
<p><span class="code">&lt; div&gt;&amp; nbsp;&lt;img src="ipodclassics.jpg" alt="iPod classic" /&gt;</span></p>
<p></code></p>
<p>初衷是不想用position来做,毕竟 大量的图片显示浏览器在渲染的时候会消耗 较多的资源。</p>
<p>缺点是 输出了 无意义的nbsp;, 而且font-size和font-indentd的计算公式不是很简单(所以以上的数字都是凑的^_^).</p>
<p>后来小马见了这个思路，用了点时间升级了代码,用:after输出代替了无趣nbsp ,代码见下。</p>
<p><code>
<p>CSS:</p>
<p><span class="code">&nbsp;.tb-p-c{<br />&nbsp; display: block;<br />&nbsp; width:140px;<br />&nbsp; height:140px;<br />&nbsp; line-height:140px;<br />&nbsp; text-align:center;<br />&nbsp; *font-size:123px;<br />}<br />.tb-p-c img{<br />&nbsp; vertical-align:middle;<br />}<br />.tb-p-c:after {<br />&nbsp; content: ".";<br />&nbsp; visibility: hidden;<br />&nbsp; font-size: 12px;<br />&nbsp; margin-left: -5px;<br />} </span></p>
<p>HTML:</p>
<p><span class="code">&lt;a class="tb-p-c"&gt;&lt;img src="jishi070912_2.jpg" /&gt;&lt;/a&gt; </span></p>
<p></code></p>
<p>这样子，恼人的nbsp消失了。大家对vertical-align:middle的渲染方式有了更进一步的了解了。还让很多同志发现了after这个伪类还可以用来hack.</p>
<p>结果圆心又给CSS升了次级：</p>
<p><code>
<p>CSS:</p>
<p><span class="code">&nbsp;.tb-p-c{<br />&nbsp; display: table-cell;<br />&nbsp; vertical-align:middle;<br />&nbsp; width:140px;<br />&nbsp; height:140px;<br />&nbsp; text-align:center;<br />&nbsp; *display: block;<br />&nbsp; *font-size: 122px;<br />&nbsp; background:red;<br />}<br />.tb-p-c img {<br />&nbsp; vertical-align:middle;<br />} </span></p>
<p></code></p>
<p>这是第三次的升级，由于时间关系，没有测试上面的代码，原因是利用了display来渲染，感觉上还是会让浏览器多次渲染，所以偶没测试 。<br />这次探讨给了我们很多很多的收获，相信如果有大家的参与，会有更多方式和我们没发现的观点。大伙别小气，多发表回复，一起提高。</p>
]]></content:encoded>
			<wfw:commentRss>http://adam.ofeva.com/blog/51.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Web标准的web UI</title>
		<link>http://adam.ofeva.com/blog/50.html</link>
		<comments>http://adam.ofeva.com/blog/50.html#comments</comments>
		<pubDate>Mon, 18 Aug 2008 15:26:27 +0000</pubDate>
		<dc:creator>阿当</dc:creator>
				<category><![CDATA[UI/UE]]></category>
		<category><![CDATA[xhtml+css]]></category>

		<guid isPermaLink="false">http://adam.ofeva.com/?p=50</guid>
		<description><![CDATA[Web标准的web UI——来源、谬误与个人理解 序 我从2004年末开始接触web标准，2005年5月正式采取完全的web标准方式的网页制作，2005年8月，第一个符合web标准的网站UI开发工作完成。直至今日，经历了无数的艰辛，也有过许多的困惑。所幸，我的瑞典籍的Project Leader是一个很有经验的人，他告诉了我很多关于web标准国内并不了解的东西，我这几年技术方面的成长离不开他的支持和引导，感谢Andreas Liljefilt！在这里，我把它们告诉大家，也希望能有更多的人来讨论。 Chaper 1 什么是web标准？Div+css的谬误。 提到web标准，就不得不先说一说国内业界非常流行的一个词——Div+css。这个词在国内简直是一个潮流，不仅互联网上一直在提，大量的教程中使用这个词，就连一些出版的书籍也是用了这个概念。然而，甚少人知道的是，这个概念本身是错误的。有好事的朋友不妨去google搜索一下（先调整到英文界面，这样可以强制让它搜索google.com而不是google.cn），&#8221;div+css&#8221;这样一个关键字是根本找不到任何一个英文网页，全部都是中文的。没错，其实所谓的div+css就是一个中国特有的理解和概念。我甚至不知道这个词是谁先提出来的，然而，它对web标准中xhtml/css的网页构建方法的理解几乎是完全错误的。 回归正题，web标准究竟是什么？Web标准是w3c组织规定的各种web上所使用的语言的标准和规范的集合。 我们目前究竟接触到了web标准的多少？打开 w3c的官方网站，我们在左侧可以看到如下列表： # Accessibility # Amaya # CC/PP # Compound Document Formats (CDF) # CSS # CSS Validator # Databinding # DOM # Efficient XML Interchange # eGovernment # GRDDL # Health Care and Life Sciences # HTML # HTML Tidy # HTML Validator [...]]]></description>
			<content:encoded><![CDATA[<p><strong>Web标准的web UI——来源、谬误与个人理解</strong></p>
<p><strong>序</strong></p>
<p>我从2004年末开始接触web标准，2005年5月正式采取完全的web标准方式的网页制作，2005年8月，第一个符合web标准的网站UI开发工作完成。直至今日，经历了无数的艰辛，也有过许多的困惑。所幸，我的瑞典籍的Project Leader是一个很有经验的人，他告诉了我很多关于web标准国内并不了解的东西，我这几年技术方面的成长离不开他的支持和引导，感谢Andreas Liljefilt！在这里，我把它们告诉大家，也希望能有更多的人来讨论。</p>
<p><strong>Chaper 1 什么是web标准？Div+css的谬误。</strong></p>
<p>提到web标准，就不得不先说一说国内业界非常流行的一个词——Div+css。这个词在国内简直是一个潮流，不仅互联网上一直在提，大量的教程中使用这个词，就连一些出版的书籍也是用了这个概念。然而，甚少人知道的是，这个概念本身是错误的。有好事的朋友不妨去google搜索一下（先调整到英文界面，这样可以强制让它搜索google.com而不是google.cn），&#8221;div+css&#8221;这样一个关键字是根本找不到任何一个英文网页，全部都是中文的。没错，其实所谓的div+css就是一个中国特有的理解和概念。我甚至不知道这个词是谁先提出来的，然而，它对web标准中xhtml/css的网页构建方法的理解几乎是完全错误的。</p>
<p>回归正题，web标准究竟是什么？Web标准是w3c组织规定的各种web上所使用的语言的标准和规范的集合。</p>
<p>我们目前究竟接触到了web标准的多少？打开 w3c的官方网站，我们在左侧可以看到如下列表：</p>
<p><code><br />
# Accessibility<br />
# Amaya<br />
# CC/PP<br />
# Compound Document Formats<br />
(CDF)<br />
# CSS<br />
# CSS<br />
Validator<br />
# Databinding<br />
# DOM<br />
# Efficient XML<br />
Interchange<br />
# eGovernment<br />
# GRDDL<br />
# Health Care and Life<br />
Sciences<br />
# HTML<br />
# HTML Tidy<br />
# HTML Validator<br />
# HTTP<br />
# Incubator<br />
# InkML<br />
# Internationalization<br />
# Jigsaw<br />
# Libwww<br />
# MathML<br />
# Mobile Web Initiative<br />
(W3C-MWI)<br />
# Multimodal<br />
Interaction<br />
# OWL<br />
# Patent Policy<br />
# PICS<br />
# PNG<br />
# POWDER<br />
# Privacy and P3P<br />
# RDF<br />
# Rich Web Clients<br />
# Rules<br />
# Security<br />
# Semantic Web<br />
# Service Modeling Language<br />
(SML)<br />
# SMIL<br />
# SOAP/XMLP<br />
# SPARQL<br />
# Style<br />
# SVG<br />
# Timed Text<br />
# URI/URL<br />
# Validators<br />
# Voice<br />
# Ubiquitous Web<br />
Applications<br />
# WAI<br />
# Web API<br />
# Web Application<br />
Formats<br />
# Web Architecture<br />
(TAG)<br />
# WebCGM<br />
# Web Services<br />
# WS-Addressing<br />
# WS-CDL<br />
# WSDL<br />
# WS-Policy<br />
# XForms<br />
# XHTML<br />
# XHTML2<br />
# XLink<br />
# XML<br />
# XML Base<br />
# XML Encryption<br />
# XML Key Management<br />
# XML Processing<br />
# XML Query<br />
# XML Schema<br />
# XML Signature<br />
# XPath<br />
# XPointer<br />
# XSL and XSLT<br />
</code></p>
<p>全看下来后是不是觉得很晕？没错，这个就是web标准目前的全部技术规范。web标准包含了这么多的内容，而我们目前所说的div+css只是其中xhtml/css实现方式的不完整的一部分而已。</p>
<p><strong>* 为什么是xhtml/css？</strong></p>
<p>其他的部分，我不想说的太多，第一是因为我也没办法全都弄懂，第二是其中有一大半浏览器支持不完全甚至根本就不支持。XML是web标准中对网页实现的最终目标。也就是web页面数据化和语义化，然而由于浏览器的支持不完善和兼容问题，目前优秀、兼容性强的纯xml网站只是停留在幻想里而已。因此，现在主流的网页实现方式就是xhtml/css。首先，xhtml与html大部分兼容，并且可以让目前大多数的浏览器直接阅读。css主流的几大浏览器也支持的非常完善。再加上ECMAScript(不说Javascript的原因是Javascript的概念中包含了很多与标准不同的浏览器私有定义)，已经足够实现web UI布局的大部分需要了。</p>
<p align="left"><strong>Chapter 2 web标准真的是要完全不用表格？</strong></p>
<p>好像是在2005年的时候，一篇叫做《把表格从你的网页中扔出去》（找不到文章的链接了，但确实有这篇文章）的在线文章，似乎给了人们一个错觉，要符合标准，那么表格在网页中就完全不能使用了。必须用div来代替。也许div+css这个概念就是这样被想当然的创造出来的（源自表格布局）。但事实上，web标准并不是完全不允许使用表格。而是要求摒弃使用表格来布局的做法。同时，也不再使用布局这个概念。而是提倡结构与表现分离。这时，就有一些人会产生一个疑惑，如果说xhtml代表结构，css用来控制表现，那么，布局该如何解决？相信现在接触web标准的朋友不会再有这个疑惑了吧？结构和表现结合起来就形成了布局。既然不能用表格来做布局了，那么表格还有什么用呢？似乎很多人忘了表格在html中的原始定义——展现结构化数据表格。举个例子，某学校班级的期中考试成绩表，这种数据，就是一个非常明显的表格。web标准中绝对没有要求你用div来模拟表格，那是非常蠢的做法。这几天在经典论坛上还看到有人产生这样的疑惑，表格形式的格式化数据，用表格比用div要方便的多，他们搞不懂为什么一定要用div来表现表格，现在我告诉你答案了，该用表格展现数据的时候就要用表格。</p>
<p><strong>Chapter 3 为什么要用web标准？怎么样才算是符合web标准？</strong></p>
<p>很多人会说例如兼容性好、代码易懂、代码量小、结构合理、甚至有人说使用标准可以实现比表格更丰富的样式等各种理由来支持web标准，而web标准也的确具有这些优点，然而，这些却并非web标准真正要做的。</p>
<p>并非把表格换成div就是符合web标准了。也并非使用xhtml和css就是符合web标准。甚至就算你的代码能够通过w3c的验证，也很难说它就完全符合web标准。事实上，web标准的最终目标不是为了让人容易看懂网页如果仅仅是为了让人容易看懂，那么表格布局已经足够了。它的最终目标是为了让计算机能够读懂网页。看下面几个例子：</p>
<p><strong>表格布局的一段代码：</strong></p>
<p><span class="code">&lt;table width=&#8221;50%&#8221;&gt;<br />&lt;tr&gt;<br />&lt;td width=&#8221;30%&#8221;&gt;&lt;/td&gt;<br />&lt;td width=&#8221;30%&#8221;&gt;&lt;font size=&#8221;3&#8243;&gt;web&lt;/font&gt;标准的概念&lt;/td&gt;<br />&lt;td width=&#8221;40%&#8221;&gt;如何实现&lt;b&gt;标准化制作&lt;/b&gt;&lt;/td&gt;<br />&lt;/tr&gt;<br />&lt;tr&gt;<br />&lt;td colspan=&#8221;3&#8243;&gt;&lt;font color=&#8221;red&#8221;&gt;&lt;font size=&#8221;3&#8243;&gt;web&lt;/font&gt;标准在网页中的使用&lt;/font&gt;&lt;/td&gt;<br />&lt;/table&gt;</span></p>
<p><strong>web标准（XHTML/CSS）实现的一段代码：</strong></p>
<p><span class="code">&lt;h3&gt;&lt;span&gt;web&lt;/span&gt;标准的概念&lt;/h3&gt;<br />&lt;h3&gt;如何实现&lt;em&gt;标准化制作&lt;/em&gt;&lt;/h3&gt;<br />&lt;h3 class=&#8221;important&#8221;&gt;&lt;span&gt;web&lt;/span&gt;标准在网页中的使用&lt;/td&gt;</span></p>
<p><strong>web标准(XML)实现的另一段代码：</strong></p>
<p><span class="code">&lt;articles&gt;<br />&nbsp;&nbsp;&nbsp; &lt;articletitle&gt;web标准的概念&lt;/articletitle&gt;<br />&nbsp;&nbsp;&nbsp; &lt;articletitle em=&#8221;4&#8243; emlegth=&#8221;3&#8243;&gt;如何实现标准化制作&lt;/articletitle&gt;<br />&nbsp;&nbsp;&nbsp; &lt;articletitle level=&#8221;important&#8221;&gt;web标准在网页中的使用&lt;/articletitle&gt;<br />&lt;articles&gt;</span></p>
<p>看过以上几段代码后，我们先来分析一下。第一段是表格布局的代码，它把整块分成了两行，第一行用了三列，第一列是空的用于缩进，后面两列分别放了两篇文章的标题。其中的英文文字采用了不同于中文的字号。第二篇文章的标题上还有一部分是加粗强调的。第二行则是一篇文章的标题占用了整行，并且以红色显示文章标题。在页面展现出来之后，我们能够明白下面的信息：第一篇文章是普通文章，第二篇文章中有一个概念是很重要的。而第三篇文章则非常重要。然而，在代码中我们却很难看出这一点。因为没有人说过加粗就一定是强调。也没有人告诉我们红色就一定表示重要（如果是在暗红色背景下，它甚至表示不重要，光看代码是不知道页面展现出来是什么样子的，是否重要自然无从判断），在这段代码中甚至没有告诉我们，这几段文字是文章标题。</p>
<p>第二段代码就要清楚的多了，首先，h3标签告诉我们，它是一个标题。span标签完全没有含义，会被分析器忽略掉。而em标签则表示强调。程序很容易明白它究竟是什么。最后一行指定的的类important则可以告诉分析器，这篇文章十分重要。</p>
<p>最后，我们再看第三段中的XML代码，首先，articletitle已经明明白白的告诉我们，它是文章标题，多余的信息没有了。事实上多数情况下是否强调一段文字中某一个部分对于分析器来说并不重要。因此，加粗强调被放到了属性里面。最后一条，level属性非常明白告诉分析器，这个属性定义的是文章的级别。而它的属性important则告诉分析器，它的级别是重要。将来采用这种结构，我们的网络将会更加智能，而搜索引擎的搜索结果也将会更加准确。当然，好处远远不只是这些。</p>
<p>直到现在为止，第三段代码要想真正完美实现并且兼容，仍然只能停留在我们的梦想里。</p>
<p><strong>Chapter 4 web标准的局限</strong></p>
<p>web标准并没有有些人说的那么天花乱坠无所不能。正如很多在学习web标准开发的朋友所体会到的那样，如果想要开发的产品完全符合web标准，它的局限性其实很大。举例来说，如果按照web标准的建议不使用空结构块(如空div)、不使用无意义块（如仅作为装饰边角的图片）、不做无意义的DOM结构嵌套，那么想实现一个可拉伸大小的园角块都是非常困难的。目前网上流行的几种做法都不符合这个要求。这就是为什么欧美的许多网站往往结构以方块为主并且非常干净简练，一个原因是他们习惯这样的风格，但更重要的原因是为了UI的可用性和符合标准而在牺牲了美观，因为网站的DOM结构越复杂，互动表现越复杂，触发BUG的可能性就越大，兼容性也越难调整，此外，这些效果往往还不能完善。有兴趣的朋友不妨仔细看看一直被设计人员称道的大多数韩国网站，这种类型的网站和欧美的主流风格正好完全相背，走了另一个极端，以界面华丽花哨著称，因此特别能获得美术出身的朋友的青睐，在使用过程中总会出现各种各样的小问题。在 FF下也没有几个可以完美表现的。此外，这类网站在中国也是行不通的。大家不妨想想，究竟有哪个模仿韩国的网站能够获得比较高的知名度的？原因嘛，第一是它们经常造成浏览器速度过慢，第二是在网络条件不好的情况下下载经常很慢，第三，对服务器的负载压力非常大，很容易提高服务器的投入成本，最后，带来高带宽成本。</p>
<p><strong>Chapter 5 web标准的背后，企业该如何适应web标准</strong>？</p>
<p>web标准有诸多好处，也带来了美好的前景，应当普及和推广。然而，盲目地追随标准，却可能造成整个项目的失败。要知道，web标准并非孤立的产生，而是于整个软件工程和web项目管理的发展有关。下面，我们来看一下，在适应web标准的过程中，究竟有哪些问题会造成项目失败。</p>
<p>1. 对标准的理解错误</p>
<p>前面说了，国内其实大多数企业和开发人员并不了解web标准。甚至有很多连web标准这个概念都不知道。反而对div+css这个被人错误解释出来的怪胎耳熟能详。设计师在进行设计的时候，往往天马行空的去做设计，完全没有任何章法可言，同样的内容，甚至在首页是三个字的标题，到了二级栏目页就会变成五个字，从根本上破坏了结构的可重用性。而UI程序员（请原谅我使用这个词语，因为发展到现在的web标准网页开发已经不是美术出身的设计师能够完全掌握的了）为了适应设计师的设计，只好拼命叠加各种奇怪的DOM结构，结果使本来用十行html代码就能写出来的页面最后用了三十行甚至更多，结构也一片混乱。css就更不用说了，不仅乱，而且乱的毫无章法。这种开发的方式经常造成最后只要设计上修改一点，就要对代码作非常大的改动，甚至整个开发流程从头做一遍，根本没有做到web标准中宣传的改版成本小，正好相反，改版成本有时会被无限提高。而混乱的结构和样式也会引发浏览器更多的BUG，让UI程序员不得不花更多的精力去写hack。从而进一步提高开发成本。</p>
<p>2. 没有任何规划，上手就做</p>
<p>在早期，由于表格布局的完全可视化编辑，使网页开发是可以完全不需要规划，一边做一边修改的。而我们大多数企业目前的开发流程也是如此。往往网站开发接近完成，策划人员还没有完全确定网站要展示的内容和提供的功能。但欧美许多公司的做法却是先做一份十分完善的策划和需求描述，然后建立用例模型、分析网站需求、建立逻辑模型，规划UI模块、规划功能模块、定义UI和功能模块的接口（大多数情况下这个接口就是我们现在经常使用的各种模板标签，事实上在欧洲比较完善的团队，这些标签在开始设计前就已经规定好了）、定义 flash应用程序的数据接口（一般情况下是XML文档）、定义内容框架（以便设计师在设计网站时了解网站的每个页面上究竟应该放些什么），这一大堆的各种文档几乎可以让任何两个不同的团队做出功能一模一样的两个网站来，除了美术设计不同。我就见过一份不过二十多个模板的策划案，仅仅是涉及UI设计和开发的策划和分析文档打印出来有300多页，密密麻麻的几十万字！为什么要说中国和欧美企业的开发过程的不同呢？原因很简单。中国的流程随意性更大，而欧美的流程则更加系统。然而web标准在设计的时候却是以欧美的设计流程为主。这就是我上面说的，没有任何规划，上手就做经常会造成项目失败的原因。一个边策划边构建的开发流程，采用了一份为完善的系统工程要求订制的标准，不失败才是奇迹！</p>
<p>3. 主导人员角色错误，外行管理内行</p>
<p>这几乎是中国百分之八十项目失败的主要原因！对比东西方的管理，会发现一个奇怪的现象，在西方一个项目是由专业的项目经理主导，而东方则是谁职位最高就由谁主导。总经理、部门行政经理甚至市场人员干预网站开发进程在中国屡见不鲜，甚至有向非web专业市场人员主导项目管理的倾向。在一个web开发团队中，有时起主导作用的项目经理或者策划人员并非专业的项目经理或者web策划人员，最夸张的，我目前在做的项目竟然是以设计师的设计稿为主导，设计成什么样子，就必须作成什么样子，并且整个网站的设计稿完全没有任何关于互动方面的说明（其实是绘图师，他们对web的结构和技术限制是完全懂的）。而我认识的很多朋友也都因为上级在开发进程中的胡乱干预（注意，是开发进程中，而不是策划过程）叫苦连天，甚至有时会造成整个项目必须彻底推翻重来的尴尬境地。不断延期或者推翻重来的项目开发过程，无限翻倍的项目成本，造成项目失败也就不怎么新鲜了。似乎这一点与web标准并没有关系，然而，在web标准化开发要求的团队和流程里，第一点要求就是项目经理和策划人员必须专业并且其技能范围符合项目规模！事实上这也是任何项目管理的必要条件。</p>
<p><strong>那么，企业和开发人员究竟该如何适应web标准？以下几点注意事项仅供参考</strong>：</p>
<ol>
<li>完善的前期策划和分析</li>
<li>完善的前期逻辑模型以及项目规范性文档的制定</li>
<li>尽可能将行政性干预移到策划阶段（按照国内的情况，做到这一点可能很困难）</li>
<li>尽可能向后兼容，在项目规范性文档制定阶段对网站进行完善的模块化规范（主要是为了提高网站模块代码的可重用性以及最大程度上降低改版成本）。</li>
<li>尽可能简化UI代码的DOM结构，以降低维护成本</li>
<li>在设计和开发过程中首先保证UI的可用性，在此基础上保证其美观（好用第一，好看第二）。</li>
<li>项目阶段明确，要让单位的高层明白，在项目的alpha期之前是不可能有能让他们看的懂用的通的完善网站出现的。</li>
<li>项目团队主要成员必须要用专业人员，并且要让这些人员有足够的决定权（如果项目负责人无法主导项目走向，项目就必然产生缺陷甚至失败）。</li>
</ol>
<p>这篇文章到这里已经结束了，我不知道这篇文章究竟会不会让那些一意孤行的BOSS们看到，更不指望能给他们带来多少影响。如果哪个BOSS看到了，希望你考虑一下你的投资和钞票。我所说的这一切，不仅仅是为了减轻开发人员的负担，更是为了让开发人员能够实现一个赚钱的项目，从而在这个赚钱的项目中获得更多的金钱和良好的心情。而能够决定这一切的，并非开发和设计人员。</p>
<p>原文：<a href="http://blog.icebirds.net/site/comments/a20071224001/" target="_blank">http://blog.icebirds.net/site/comments/a20071224001/</a></p>
]]></content:encoded>
			<wfw:commentRss>http://adam.ofeva.com/blog/50.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>惰性函数定义模式</title>
		<link>http://adam.ofeva.com/blog/49.html</link>
		<comments>http://adam.ofeva.com/blog/49.html#comments</comments>
		<pubDate>Mon, 18 Aug 2008 15:15:55 +0000</pubDate>
		<dc:creator>阿当</dc:creator>
				<category><![CDATA[javascript]]></category>

		<guid isPermaLink="false">http://adam.ofeva.com/?p=49</guid>
		<description><![CDATA[这篇文章阐述的是一种函数式编程（functional-programming）设计模式，我称之为惰性函数定义（Lazy Function Definition）。我不止一次发现这种模式在JavaScript中大有用处，尤其是编写跨浏览器的、高效运行的库之时。 热身问题 编写一个函数foo，它返回的是Date对象，这个对象保存的是foo首次调用的时间。 方法一：上古时代的技术 这个最简陋的解决方案使用了全局变量t来保存Date对象。foo首次调用时会把时间保存到t中。接下来的再次调用，foo只会返回保存在t中的值。 var t;function foo() {&#160;&#160;&#160; if (t) {&#160;&#160;&#160;&#160;&#160;&#160;&#160; return t;&#160;&#160;&#160; }&#160;&#160;&#160; t = new Date();&#160;&#160;&#160; return t;} 但是这样的代码有两个问题。第一，变量t是一个多余的全局变量，并且在 foo调用的间隔期间有可能被更改。第二，在调用时这些代码的效率并没有得到优化因为每次调用 foo都必须去求值条件。虽然在这个例子中，求值条件并不显得低效，但在现实世界的实践例子中常常会有极为昂贵的条件求值，比如在if-else-else-…的结构中。 方法二：模块模式 我们可以通过被认为归功于Cornford 和 Crockford 的模块模式来弥补第一种方法的缺陷。使用闭包可以隐藏全局变量t，只有在 foo内的代码才可以访问它。 var foo = (function() {&#160;&#160;&#160; var t;&#160;&#160;&#160; return function() {&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (t) {&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; return t;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }&#160;&#160;&#160;&#160;&#160;&#160;&#160; t = new Date();&#160;&#160;&#160;&#160;&#160;&#160;&#160; return t;&#160;&#160;&#160; }})(); [...]]]></description>
			<content:encoded><![CDATA[<p>这篇文章阐述的是一种函数式编程（functional-programming）设计模式，我称之为惰性函数定义（Lazy Function Definition）。我不止一次发现这种模式在JavaScript中大有用处，尤其是编写跨浏览器的、高效运行的库之时。</p>
<p><strong>热身问题</strong></p>
<p>编写一个函数foo，它返回的是Date对象，这个对象保存的是foo首次调用的时间。</p>
<p><strong>方法一：上古时代的技术</strong></p>
<p>这个最简陋的解决方案使用了全局变量t来保存Date对象。foo首次调用时会把时间保存到t中。接下来的再次调用，foo只会返回保存在t中的值。</p>
<p><span class="code">var t;<br />function foo() {<br />&nbsp;&nbsp;&nbsp; if (t) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return t;<br />&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; t = new Date();<br />&nbsp;&nbsp;&nbsp; return t;<br />}</span></p>
<p>但是这样的代码有两个问题。第一，变量t是一个多余的全局变量，并且在 foo调用的间隔期间有可能被更改。第二，在调用时这些代码的效率并没有得到优化因为每次调用 foo都必须去求值条件。虽然在这个例子中，求值条件并不显得低效，但在现实世界的实践例子中常常会有极为昂贵的条件求值，比如在if-else-else-…的结构中。</p>
<p><strong>方法二：模块模式</strong></p>
<p>我们可以通过被认为归功于Cornford 和 Crockford 的模块模式来弥补第一种方法的缺陷。使用闭包可以隐藏全局变量t，只有在 foo内的代码才可以访问它。</p>
<p><span class="code">var foo = (function() {<br />&nbsp;&nbsp;&nbsp; var t;<br />&nbsp;&nbsp;&nbsp; return function() {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (t) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return t;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; t = new Date();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return t;<br />&nbsp;&nbsp;&nbsp; }<br />})();</span></p>
<p>但这仍然没有优化调用时的效率，因为每次调用foo依然需要求值条件。</p>
<p>虽然模块模式是一个强大的工具，但我坚信在这种情形下它用错了地方。</p>
<p><strong>方法三：函数作为对象</strong></p>
<p>由于JavaScript的函数也是对象，所以它可以带有属性，我们可以据此实现一种跟模块模式质量差不多的解决方案。</p>
<p><span class="code">function foo() {<br />&nbsp;&nbsp;&nbsp; if (foo.t) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return foo.t;<br />&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; foo.t = new Date();<br />&nbsp;&nbsp;&nbsp; return foo.t;<br />}</span></p>
<p>在一些情形中，带有属性的函数对象可以产生比较清晰的解决方案。我认为，这个方法在理念上要比模式模块方法更为简单。</p>
<p>这个解决方案避免了第一种方法中的全局变量t，但仍然解决不了foo每次调用所带来的条件求值。</p>
<p><strong>方法四：惰性函数定义</strong></p>
<p>现在，这是你阅读这篇文章的理由：</p>
<p><span class="code">var foo = function() {<br />&nbsp;&nbsp;&nbsp; var t = new Date();<br />&nbsp;&nbsp;&nbsp; foo = function() {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return t;<br />&nbsp;&nbsp;&nbsp; };<br />&nbsp;&nbsp;&nbsp; return foo();<br />};</span></p>
<p>当foo首次调用，我们实例化一个新的Date对象并重置 foo到一个新的函数上，它在其闭包内包含Date对象。在首次调用结束之前，foo的新函数值也已调用并提供返回值。</p>
<p>接下来的foo调用都只会简单地返回t保留在其闭包内的值。这是非常快的查找，尤其是，如果之前那些例子的条件非常多和复杂的话，就会显得很高效。</p>
<p>弄清这种模式的另一种途径是，外围（outer）函数对foo的首次调用是一个保证（promise）。它保证了首次调用会重定义foo为一个非常有用的函数。笼统地说，术语“保证” 来自于Scheme的惰性求值机制（lazy evaluation mechanism）。每一位JavaScript程序员真的都应该 <a href="http://www.amazon.com/Scheme-Programming-Language-3rd/dp/0262541483/ref=pd_bbs_sr_1/102-4214146-5559331?ie=UTF8&amp;s=books&amp;qid=1186852441&amp;sr=8-1" target="_blank">学习Scheme</a> ，因为它有很多函数式编程相关的东西，而这些东西会出现在JavaScript中。</p>
<p><strong>确定页面滚动距离</strong></p>
<p>编写跨浏览器的JavaScript, 经常会把不同的浏览器特定的算法包裹在一个独立的JavaScript函数中。这就可以通过隐藏浏览器差异来标准化浏览器API，并让构建和维护复杂的页面特性的JavaScript更容易。当包裹函数被调用，就会执行恰当的浏览器特定的算法。</p>
<p>在拖放库中，经常需要使用由鼠标事件提供的光标位置信息。鼠标事件给予的光标坐标相对于浏览器窗口而不是页面。加上页面滚动距离鼠标的窗口坐标的距离即可得到鼠标相对于页面的坐标。所以我们需要一个反馈页面滚动的函数。演示起见，这个例子定义了一个函数getScrollY。因为拖放库在拖拽期间会持续运行，我们的getScrollY必须尽可能高效。</p>
<p>不过却有四种不同的浏览器特定的页面滚动反馈算法。Richard Cornford在他的<a href="http://www.jibbering.com/faq/faq_notes/not_browser_detect.html#bdScroll" target="_blank">feature detection article</a> 文章中提到这些算法。最大的陷阱在于这四种页面滚动反馈算法其中之一使用了 document.body. JavaScript库通常会在HTML文档的&lt;head&gt;加载，与此同时docment.body并不存在。所以在库载入的时候，我们并不能使用特性检查（feature detection）来确定使用哪种算法。</p>
<p>考虑到这些问题，大部分JavaScript库会选择以下两种方法中的一种。第一个选择是使用浏览器嗅探navigator.userAgent，为该浏览器创建高效、简洁的getScrollY. 第二个更好些的选择是getScrollY在每一次调用时都使用特性检查来决定合适的算法。但是第二个选择并不高效。</p>
<p>好消息是拖放库中的getScrollY只会在用户与页面的元素交互时才会用到。如果元素业已出现在页面中，那么document.body也会同时存在。getScrollY的首次调用，我们可以使用惰性函数定义模式结合特性检查来创建高效的getScrollY.</p>
</p>
<p><span class="code"></span></p>
<p>var getScrollY = function() {</p>
<p>&nbsp;&nbsp;&nbsp; if (typeof window.pageYOffset == &#8216;number&#8217;) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; getScrollY = function() {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return window.pageYOffset;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; };</p>
<p>&nbsp;&nbsp;&nbsp; } else if ((typeof document.compatMode == &#8216;string&#8217;) &amp;&amp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (document.compatMode.indexOf(&#8216;CSS&#8217;) &gt;= 0) &amp;&amp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (document.documentElement) &amp;&amp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (typeof document.documentElement.scrollTop == &#8216;number&#8217;)) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; getScrollY = function() {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return document.documentElement.scrollTop;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; };</p>
<p>&nbsp;&nbsp;&nbsp; } else if ((document.body) &amp;&amp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (typeof document.body.scrollTop == &#8216;number&#8217;)) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; getScrollY = function() {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return document.body.scrollTop;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; } else {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; getScrollY = function() {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return NaN;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; };</p>
<p>&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; return getScrollY();<br />}</p>
<p><strong>总结</strong></p>
<p>惰性函数定义模式让我可以编写一些紧凑、健壮、高效的代码。用到这个模式的每一次，我都会抽空赞叹JavaScript的函数式编程能力。</p>
<p>JavaScript同时支持函数式和面向对象便程。市面上有很多重点着墨于面向对象设计模式的书都可以应用到JavaScript编程中。不过却没有多少书涉及函数式设计模式的例子。对于JavaScript社区来说，还需要很长时间来积累良好的函数式模式。</p>
<p><strong>更新</strong>：</p>
<p>这个模式虽然有趣，但由于大量使用闭包，可能会由于内存管理的不善而导致性能问题。来自 <a href="http://www.fckeditor.net/" target="_blank">FCKeditor</a> 的FredCK改进了getScrollY，既使用了这种模式，也避免了闭包：</p>
<p><span class="code"></span></p>
<p>var getScrollY = function() {</p>
<p>&nbsp;&nbsp;&nbsp; if (typeof window.pageYOffset == &#8216;number&#8217;)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return (getScrollY = getScrollY.case1)();</p>
<p>&nbsp;&nbsp;&nbsp; var compatMode = document.compatMode;<br />&nbsp;&nbsp;&nbsp; var documentElement = document.documentElement;</p>
<p>&nbsp;&nbsp;&nbsp; if ((typeof compatMode == &#8216;string&#8217;) &amp;&amp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (compatMode.indexOf(&#8216;CSS&#8217;) &gt;= 0) &amp;&amp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (documentElement) &amp;&amp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (typeof documentElement.scrollTop == &#8216;number&#8217;))<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return (getScrollY = getScrollY.case2)();</p>
<p>&nbsp;&nbsp;&nbsp; var body = document.body ;<br />&nbsp;&nbsp;&nbsp; if ((body) &amp;&amp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (typeof body.scrollTop == &#8216;number&#8217;))<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return (getScrollY = getScrollY.case3)();</p>
<p>&nbsp;&nbsp;&nbsp; return (getScrollY = getScrollY.case4)();<br />};</p>
<p>getScrollY.case1 = function() {<br />&nbsp;&nbsp;&nbsp; return window.pageYOffset;<br />};</p>
<p>getScrollY.case2 = function() {<br />&nbsp;&nbsp;&nbsp; return documentElement.scrollTop;<br />};</p>
<p>getScrollY.case3 = function() {<br />&nbsp;&nbsp;&nbsp; return body.scrollTop;<br />};</p>
<p>getScrollY.case4 = function() {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return NaN;<br />};</p>
</p>
<p><strong>原文</strong>：<a href="http://peter.michaux.ca/article/3556" target="_blank">Lazy Function Definition Pattern</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://adam.ofeva.com/blog/49.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>CSS模块化设计——从空格谈起</title>
		<link>http://adam.ofeva.com/blog/48.html</link>
		<comments>http://adam.ofeva.com/blog/48.html#comments</comments>
		<pubDate>Mon, 18 Aug 2008 15:09:09 +0000</pubDate>
		<dc:creator>阿当</dc:creator>
				<category><![CDATA[xhtml+css]]></category>

		<guid isPermaLink="false">http://adam.ofeva.com/?p=48</guid>
		<description><![CDATA[引子： 今天在蓝点看了Yang的博客《CSS样式表中继承关系的空格与不空格》，思考了一下，本来想写《CSS样式的复合定义与复合调用及简单的模块化设计》，但是内容太大，还是来点简单的实惠。所以改叫《CSS样式——从空格谈起》吧。 一、空格运算符 （1）CSS语言 简单地说，CSS语言类似JS语言，是通过客户端下载后，通过本地浏览器解析。而CSS语言又是非常低级的“弱类型”语言，离JS这种基于对象的比较完善的“弱类型”语言，还差相当一段距离。要知道CSS样式是定义出来的，而样式的呈现是根据文档流顺序和CSS优先级别，浏览器自己识别计算后显示出来的。而浏览器又有忽略和纠错功能（尤以IE为甚），所以样式定义的语法有错误，并不影响浏览器正常工作，只不过显示不出应有的效果罢了。在我们设计定义样式的时候，排错是比较令人苦恼的，其本质原因是于这种弱类型CSS语言本身的不严禁性有关系的，所以我们就更应该注重CSS定义的严禁，才能出较少的错误，较快更好的完成工作 （2）CSS的运算符 首先说，CSS语言的运算符就不多，有.#{}:&#8221;;还有一个非常重要的空格。这几个运算符，都是常用的定义声明符号。而在CSS样式定义中，空格就有点特殊，我们可以把它视为在.Net或Java中命名空间或类包定义中的 . 运算符。换句话说，我们可以把空格视为路径指向的箭头，表明HTML标签的父子级别关系。CSS是与HTML想关联的，也就是说，CSS的每一个定义都与“某个HTML标签”或“某段模块化HTML代码”相对应，而HTML可以调用多个样式类。一个CSS样式类可以根据HTML代码来“复合定义”；一个HTML标签也可以“复合调用”多个样式类。所以说，CSS样式定义的复杂性与关联的HTML是密不可分。 （3）实例说明 &#60;style type=&#8221;text/css&#8221;&#62;td .b { &#160;&#160;&#160;&#160;&#160;&#160; color:#00ff00; }th.b {&#160; &#160;&#160;&#160;&#160;&#160;&#160; color:#ff0000;&#160;&#160;&#160;&#160;&#160;&#160; font-family:黑体;&#160;&#160;&#160;&#160;&#160;&#160; font-size:20px;}.b {&#160;&#160;&#160;&#160;&#160;&#160; color:#0000ff;&#160;&#160;&#160;&#160;&#160;&#160; font-size:12px;}&#60;/style&#62; &#60;table&#62;&#160;&#160;&#160;&#160;&#160;&#160; &#60;tr&#62;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &#60;td&#62;&#60;div class=&#8221;b&#8221;&#62;第一个类b的类路径是th .b&#60;/div&#62;&#60;/td&#62; &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &#60;th class=&#8221;b&#8221;&#62;第二个类b的类路径是th.b&#60;/td&#62;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &#60;td class=&#8221;b&#8221;&#62;第三个类b的类路径是 .b&#60;/th&#62;&#160;&#160;&#160;&#160;&#160;&#160; &#60;/tr&#62;&#60;/table&#62;&#60;div class=&#8221;b&#8221;&#62;第三个类b的类路径是 .b&#60;/div&#62; 讲解： 1、第一个类b的类路径是td .b，定义该HTML文档内所有的td标签内的带class=&#8221;b&#8221;的标签的样式 。也就是说：td .b { &#160;&#160;&#160;&#160;&#160;&#160; color:#00ff00; }定义的是&#60;td&#62;&#60;div class=&#8221;b&#8221;&#62;text&#60;/div&#62;&#60;/td&#62;这组代码块中的b类，class=&#8221;b&#8221;是包含在td标签内的，是td的子级，所以在这里要用“空格”指向明确表明父子级别关系。 2、第二个类b的类路径是th.b，定义的是该HTML文档内所有的带class=&#8221;b&#8221;的th标签的样式。也就是说，th.b {&#160; &#160;&#160;&#160;&#160;&#160;&#160; color: #FF0000}定义的是&#60;th [...]]]></description>
			<content:encoded><![CDATA[<p><strong>引子</strong>：</p>
<p>今天在蓝点看了Yang的博客《CSS样式表中继承关系的空格与不空格》，思考了一下，本来想写《CSS样式的复合定义与复合调用及简单的模块化设计》，但是内容太大，还是来点简单的实惠。所以改叫《CSS样式——从空格谈起》吧。</p>
<p><strong>一、空格运算符</strong></p>
<p><strong>（1）CSS语言</strong></p>
<p>简单地说，CSS语言类似JS语言，是通过客户端下载后，通过本地浏览器解析。而CSS语言又是非常低级的“弱类型”语言，离JS这种基于对象的比较完善的“弱类型”语言，还差相当一段距离。要知道CSS样式是定义出来的，而样式的呈现是根据文档流顺序和CSS优先级别，浏览器自己识别计算后显示出来的。而浏览器又有忽略和纠错功能（尤以IE为甚），所以样式定义的语法有错误，并不影响浏览器正常工作，只不过显示不出应有的效果罢了。在我们设计定义样式的时候，排错是比较令人苦恼的，其本质原因是于这种弱类型CSS语言本身的不严禁性有关系的，所以我们就更应该注重CSS定义的严禁，才能出较少的错误，较快更好的完成工作</p>
<p><strong>（2）CSS的运算符</strong></p>
<p>首先说，CSS语言的运算符就不多，有.#{}:&#8221;;还有一个非常重要的空格。这几个运算符，都是常用的定义声明符号。而在CSS样式定义中，空格就有点特殊，我们可以把它视为在.Net或Java中命名空间或类包定义中的 . 运算符。换句话说，我们可以把空格视为路径指向的箭头，表明HTML标签的父子级别关系。CSS是与HTML想关联的，也就是说，CSS的每一个定义都与“某个HTML标签”或“某段模块化HTML代码”相对应，而HTML可以调用多个样式类。一个CSS样式类可以根据HTML代码来“复合定义”；一个HTML标签也可以“复合调用”多个样式类。所以说，CSS样式定义的复杂性与关联的HTML是密不可分。</p>
<p><strong>（3）实例说明</strong></p>
<p><span class="code"></p>
<p>&lt;style type=&#8221;text/css&#8221;&gt;<br />td .b { <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; color:#00ff00; <br />}<br />th.b {&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; color:#ff0000;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; font-family:黑体;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; font-size:20px;<br />}<br />.b {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; color:#0000ff;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; font-size:12px;<br />}<br />&lt;/style&gt;</p>
<p>&lt;table&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;tr&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;td&gt;&lt;div class=&#8221;b&#8221;&gt;第一个类b的类路径是th .b&lt;/div&gt;&lt;/td&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;th class=&#8221;b&#8221;&gt;第二个类b的类路径是th.b&lt;/td&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;td class=&#8221;b&#8221;&gt;第三个类b的类路径是 .b&lt;/th&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/tr&gt;<br />&lt;/table&gt;<br />&lt;div class=&#8221;b&#8221;&gt;第三个类b的类路径是 .b&lt;/div&gt;</p>
<p></span>
</p>
</p>
<p><strong>讲解</strong>：</p>
<p>1、第一个类b的类路径是td .b，定义该HTML文档内所有的td标签内的带class=&#8221;b&#8221;的标签的样式 。<br />也就是说：<br /><span class="code">td .b { <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; color:#00ff00; <br />}</span><br />定义的是<br /><span class="code">&lt;td&gt;&lt;div class=&#8221;b&#8221;&gt;text&lt;/div&gt;&lt;/td&gt;</span><br />这组代码块中的b类，class=&#8221;b&#8221;是包含在td标签内的，是td的子级，所以在这里要用“空格”指向明确表明父子级别关系。</p>
<p>2、第二个类b的类路径是th.b，定义的是该HTML文档内所有的带class=&#8221;b&#8221;的th标签的样式。<br />也就是说，<br /><span class="code">th.b {&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; color: #FF0000<br />}</span><br />定义的是<br /><span class="code">&lt;th class=&#8221;b&#8221;&gt;text&lt;/td&gt;</span><br />在这里的代码中，th和class=&#8217;b'是平级的，先th后.b组成一个同级类路径th.b，所以没有空格代表“HTML类”和“自定义类”具有同级路径！</p>
<p>3、第三个类b的类路径是 .b，定义该HTML文档内所有的td标签的样式，它是该文档的一个全局样式，是body .b的简写。<br />也就是说，<br /><span class="code">.b {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; color:#0000ff;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; font-size:12px;<br />}</span><br />定义了<br /><span class="code">&lt;td class=&#8221;b&#8221;&gt;第三个类b的类路径是td.b&lt;/th&gt;</span><br />和<br /><span class="code">&lt;div class=&#8221;b&#8221;&gt;第三个类b的类路径是 .b&lt;/div&gt;</span></p>
<p>这两处的b类没有明确的路径指向，优先级别要比有明确路径的低。</p>
<p>4、大家可以看到，在HTML代码中，同样都是class=&#8221;b&#8221;，但是在CSS定义时，采用的类路径不同，作用就不同了。类路径越完整，优先级越高。在具体应用的时候，我们可以，使用完整类路径来定义某HTML代码块某一些特殊地方，做异化处理。例如本例表头th的黑体字显示效果。</p>
<p><strong>二、HTML中复合调用样式类</strong></p>
<p>（1）在一个HTML标签内，可以复合调用多个样式类，也是用空格做运算符，复合类名总字符不能超过256。<br />（2）示例：</p>
<p><span class="code">&lt;style type=&#8221;text/css&#8221;&gt;<br />.myTxt {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; font-size:50px;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; font-family:Arial Black;<br />}<br />.txtRed {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; color:red;<br />}<br />.txtOrange {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; color:orange;<br />}<br />.txtGreen {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; color:green;<br />}<br />.txtBlue {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; color:blue;<br />}<br />&lt;/style&gt;<br />&lt;ul&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;li class=&#8221;myTxt txtRed&#8221;&gt;123&lt;/li&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;li class=&#8221;myTxt txtOrange&#8221;&gt;Text&lt;/li&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;li class=&#8221;txtGreen&#8221;&gt;Text&lt;/li&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;li class=&#8221;myTxt txtBlue&#8221;&gt;Text&lt;/li&gt;<br />&lt;/ul&gt;</span></p>
<p>（3）应用：<br />对于某些多数样式属性累同，仅有几个不同样式属性的定义，可以用这个方法来缩写。<br />也可以在某个不改变某个通用样式类的同时，用HTML调用复合类，突出局部特例。</p>
<p><strong>三、CSS+HTML的模块化设计</strong></p>
<p>（1）举个简单例子：<br /><span class="code">.classNameA .classNameB .classNameC </span><br />就是一个类包路径,A包含B,B包含C. <br />意思就是,在A块内的全部HTML代码(包括B块、C块),先应用样式classNameA； <br />然后，在B块内的全部HTML代码(包括C块),先应用样式classNameA，之后再先应用样式classNameB； <br />最后，在C块内的全部HTML代码,先应用样式classNameA，再先应用样式classNameB,最后应用样式classNameB；</p>
<p>（2）在样式表中，关于类包的路径，对于某些复杂的HMTL代码，最后写绝对路径，就是每一个类名都不要拉下。这样可读性更强，错误率更小；当然，宽容度就越低。 </p>
<p>例如：</p>
<p><span class="code" style="width: 481px;height: 1022px"></p>
<p>&lt;style type=&#8221;text/css&#8221;&gt; <br />/*控制 li 的样式*/ <br />.a1 ul li { <br />&nbsp;&nbsp;&nbsp; color:red; <br />} </p>
<p>/*控制class=&#8221;a&#8221;的div块内，全部连接 a 的样式*/ <br />.a1 a { <br />&nbsp;&nbsp;&nbsp; font-size:20px; <br />} </p>
<p>/*控制class=&#8221;a&#8221;的div块内，一个一个为 class=&#8221;mylink&#8221;的样式*/ <br />.a1 .myLink { <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; font-size:12px; <br />} </p>
<p>/*控制 li 内连接a的样式*/ <br />.a1 ul li a { <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; font-size:40px; <br />} </p>
<p>/*控制名 li 内，一个为 class=&#8221;mylink&#8221;的连接的样式 */ <br />.a1 ul li .myLink { <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; font-size:60px; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; font-family:黑体; <br />} </p>
<p>/*b1样式*/ <br />.b1 { <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; color:blue; <br />} <br />/*控制 li 内 b1 的样式*/ <br />.a1 ul li .b1 { <br />&nbsp;&nbsp;&nbsp;&nbsp; color:green; <br />} <br />&lt;/style&gt; </p>
<p>&lt;div class=&#8221;a1&#8243;&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;a href=&#8221;#&#8221;&gt;linkText&lt;/a&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;a href=&#8221;#&#8221; class=&#8221;myLink&#8221;&gt;titleText&lt;/a&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;div class=&#8221;b1&#8243;&gt;b11111111&lt;/div&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;ul&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;li&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;a href=&#8221;#&#8221;&gt;titleText&lt;/a&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;div class=&#8221;b1&#8243;&gt;nameCN&lt;/div&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/li&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;li&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;a href=&#8221;#&#8221; class=&#8221;myLink&#8221;&gt;titleText&lt;/a&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;div class=&#8221;b1&#8243;&gt;nameCN&lt;span class=&#8221;c1&#8243;&gt;nameEN&lt;/span&gt;&lt;/div&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/li&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;li&gt;titleText&lt;/li&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;li&gt;titleText&lt;/li&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;li&gt;titleText&lt;/li&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/ul&gt; <br />&lt;/div&gt;</p>
</p>
<p></span></p>
</p>
<p>样式，按*.HTML从内层到外层；按*.CSS上下文，从下文到上文；按内联样式表上下文，从下文到上文；按内嵌&gt;内联&gt;外联的优先级； <br />叠加覆盖计算最终显示效果。</p>
<p>CSS语句，严格说是JS代码的一类，换句话说，CSS语句也是“弱类型”的，空格是一个“运算符”，由于“弱类型”不严谨，所以，没有空格的时候，虽然不报错，也有显示效果，但那是按错误逻辑运算的，有时歪打正着，但确莫名其妙。再加上有很多满足各浏览器的HACK语法，CSS语句就更加零乱不堪。所以，写的时候，尽量在满足宽容度的情况下，严禁一些。</p>
<p>（3）给大家一个案例</p>
<p>模块化设计，要求相对封闭独立性、可重复性、可修改性、统一性等等是比较高的，当然，模块化越高越复杂的，修改起来也要月谨慎，因为牵一发而动全身，这就要求，在开发设计前期要做好详尽的策划，从目录结构、命名规范，到全局和特例的界限划分、后期修改的宽容度估计等等，都要有个把握。</p>
<p>下面就给一个我做的网站的地址：<a href="http://www.51youcai.com" target="_blank">http://www.51youcai.com</a></p>
<ol>
<li>样式表是按类路径来模块化定义的，有中文注释；</li>
<li>由于加了jsp程序，代码格式有变动，也许会有不少垃圾代码；</li>
<li>在FireFox下，有几处显示不当、JS未做相应优化，请用IE6+浏览；</li>
<li>感兴趣者可以当HTML和CSS来，做参考。</li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://adam.ofeva.com/blog/48.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Unobtrusive的Web开发</title>
		<link>http://adam.ofeva.com/blog/47.html</link>
		<comments>http://adam.ofeva.com/blog/47.html#comments</comments>
		<pubDate>Mon, 18 Aug 2008 15:02:53 +0000</pubDate>
		<dc:creator>阿当</dc:creator>
				<category><![CDATA[javascript]]></category>

		<guid isPermaLink="false">http://adam.ofeva.com/?p=47</guid>
		<description><![CDATA[原文：Unobtrusive Ajax。 今天才看见的一个Presentation，是Jesse Skinner在06年10月发表的。虽然题目是关于Ajax的，但实际上前面很大篇幅再讲什么是Unobtrusive的Web开发，而且将得也很有意思。下面把其中的要点摘录出来翻译，分享一下。 对Web前端进行分层 Web前端的分层： 核心思想：结构（HTML）、表现（CSS）和行为 （JavaScript） 物理上：.html、.css和.js文件 概念上：各层之间，相互独立，互不影响 借用MVC的思想： Model &#8211; HTML View &#8211; CSS Controller &#8211; JavaScript 物理上的分层 CSS只出现在.css文件中，JavaScript只出现在.js文件中 在HTML中不会出现onload、onclick或者style属性 不使用不赞成使用的HTML，比如font标签和align属性 概念上的分层 内容和表单在纯HTML中（没有CSS和JavaScript）也能够正常显示和使用 表单和链接在没有JavaScript时候也能正常工作 只在CSS中定义表现，而不是在HTML或JavaScript中 任何人可以通过任何客户端访问内容，即便是没有CSS、JavaScript，甚至没有鼠标 两种分层有何不同 物理分层主要使开发者受益 概念分层主要让使用者受益 Unobtrusive的前端 Unobtrusive的HTML 使用更多的HTML标签 只将&#60;table&#62;用于表格式的数据 避免使用无意义的&#60;div&#62;和&#60;span&#62; Unobtrusive的CSS All CSS is unobtrusive 尽量使用可重用的class 将CSS放在外部.css文件中，或者&#60;style&#62;标签中 最好使用&#60;h1&#62;，而不是&#60;div class="header"&#62; Unobtrusive的Flash对象 用JavaScript将HTML替换成Flash Bobby van der Sluis的脚本（bobbyvandersluis.com） 将Flash的内容同样放在HTML中 如果浏览器支持Flash，用户将会欣赏性感的Flash版本 不要在HTML中加入混乱的Flash代码 [...]]]></description>
			<content:encoded><![CDATA[<p>原文：<a href="http://www.thefutureoftheweb.com/talks/2006-10-ajax-experience/slides/" target="_blank"><u>Unobtrusive Ajax</u></a>。</p>
<p>今天才看见的一个Presentation，是<a href="http://www.thefutureoftheweb.com/" target="_blank"><u>Jesse Skinner</u></a>在06年10月发表的。虽然题目是关于Ajax的，但实际上前面很大篇幅再讲什么是Unobtrusive的Web开发，而且将得也很有意思。下面把其中的要点摘录出来翻译，分享一下。</p>
<h2>对Web前端进行分层</h2>
<ul>
<li>Web前端的分层：
<ul>
<li>核心思想：结构（HTML）、表现（CSS）和行为 （JavaScript）
</li>
<li>物理上：.html、.css和.js文件
</li>
<li>概念上：各层之间，相互独立，互不影响 </li>
</ul>
</li>
<li>借用MVC的思想：
<ul>
<li>Model &#8211; HTML
</li>
<li>View &#8211; CSS
</li>
<li>Controller &#8211; JavaScript </li>
</ul>
</li>
</ul>
<h3>物理上的分层</h3>
<ul>
<li>CSS只出现在.css文件中，JavaScript只出现在.js文件中
</li>
<li>在HTML中不会出现<code>onload</code>、<code>onclick</code>或者<code>style</code>属性
</li>
<li>不使用不赞成使用的HTML，比如<code>font</code>标签和<code>align</code>属性 </li>
</ul>
<h3>概念上的分层</h3>
<ul>
<li>内容和表单在纯HTML中（没有CSS和JavaScript）也能够正常显示和使用
</li>
<li>表单和链接在没有JavaScript时候也能正常工作
</li>
<li>只在CSS中定义表现，而不是在HTML或JavaScript中
</li>
<li>任何人可以通过任何客户端访问内容，即便是没有CSS、JavaScript，甚至没有鼠标 </li>
</ul>
<h3>两种分层有何不同</h3>
<ul>
<li>物理分层主要使开发者受益
</li>
<li>概念分层主要让使用者受益 </li>
</ul>
<h2>Unobtrusive的前端</h2>
<h3>Unobtrusive的HTML</h3>
<ul>
<li>使用更多的HTML标签
</li>
<li>只将<code>&lt;table&gt;</code>用于表格式的数据
</li>
<li>避免使用无意义的<code>&lt;div&gt;</code>和<code>&lt;span&gt;</code> </li>
</ul>
<h3>Unobtrusive的CSS</h3>
<ul>
<li>All CSS is unobtrusive
</li>
<li>尽量使用可重用的<code>class</code>
</li>
<li>将CSS放在外部.css文件中，或者<code>&lt;style&gt;</code>标签中
</li>
<li>最好使用<code>&lt;h1&gt;</code>，而不是<code>&lt;div class="header"&gt;</code> </li>
</ul>
<h3>Unobtrusive的Flash对象</h3>
<ul>
<li>用JavaScript将HTML替换成Flash
</li>
<li>Bobby van der Sluis的脚本（<a href="http://www.bobbyvandersluis.com/"><u>bobbyvandersluis.com</u></a>）
</li>
<li>将Flash的内容同样放在HTML中
</li>
<li>如果浏览器支持Flash，用户将会欣赏性感的Flash版本
</li>
<li>不要在HTML中加入混乱的Flash代码 </li>
</ul>
<h3>Unobtrusive的JavaScript</h3>
<ul>
<li>从纯HTML入手
</li>
<li>JavaScript只用来为HTML添彩
</li>
<li>不要摆架子，测试每一个细节
</li>
<li>离了JavaScript，页面仍然能够正常工作
</li>
<li>不要使用<code>onclick</code>或<code>javascript:void(0)</code>
</li>
<li>将JavaScript放在外部.js文件中，或者<code>&lt;script&gt;</code>标签中 </li>
</ul>
<h2>为什么要Unobtrusive</h2>
<ul>
<li>首先，这不是必须的
</li>
<li>大部分开发者都没有在用
</li>
<li>一些比较值得一提的例子：
<ul>
<li>Google Maps (<a href="http://maps.google.com/" target="_blank"><u>maps.google.com</u></a>)
</li>
<li>TWERQ (<a href="http://www.twerq.com/" target="_blank"><u>twerq.com</u></a>)
</li>
<li>Marlboro (<a href="http://www.marlboro.com/" target="_blank"><u>marlboro.com</u></a>) </li>
</ul>
</li>
</ul>
<h3>Unobtrusive开发的优势</h3>
<ul>
<li>代码更佳简洁，并且易于维护
<ul>
<li>易读和易懂意味着更容易修改
</li>
<li>修改全部的样式仅仅需要修改CSS文件
</li>
<li>JavaScript能够很容易的修改
</li>
<li>HTML的修改变得更加保险 </li>
</ul>
</li>
<li>能够提高可访问性（accessibility）
<ul>
<li>可访问性意味着所有人都可以访问你的内容
</li>
<li>你不能揣测所有人
</li>
<li>但你可以肯定的是所有浏览器都能够处理HTML
</li>
<li>大约10%的访问者是不能够使用JavaScript功能（<a href="http://www.w3schools.com/browsers/browsers_stats.asp" target="_blank"><u>www.w3schools.com/browsers/browsers_stats.asp</u></a>）
</li>
<li>有些人使用的是屏幕阅读器，有些人无法使用鼠标 </li>
</ul>
</li>
<li>有利于搜索引擎优化
<ul>
<li>搜索爬虫无法解释CSS和JavaScript
</li>
<li>搜索爬虫只会顺着<code>&lt;a&gt;</code>继续爬行
</li>
<li>JavaScript和Flash中的内容无法被搜索到
</li>
<li>更多的内容，更结构化的HTML，使得页面相关性更高 </li>
</ul>
</li>
</ul>
<h3>不过有时，不得不使用JavaScript</h3>
<ul>
<li>当然，离了JavaScript，JavaScript游戏肯定无法运行
</li>
<li>很多Web统计服务的脚本依赖于JavaScript
</li>
<li>Google Ads需要JavaScript
</li>
<li>对于Unobtrusive，只要尽量做到就好了 </li>
</ul>
<h2>如何进行Unobtrusive的开发</h2>
<h3>不要去问别人，直接去做</h3>
<h3>从没有JavaScript开始入手</h3>
<ul>
<li>使用带有链接和表单的纯HTML
</li>
<li>使用CSS来实现hover和滚动效果
</li>
<li>使用HTTP的功能（比如用”Location”头来转向） </li>
</ul>
<h3>重视链接</h3>
<ul>
<li>页面上的所有链接，离了JavaScript都应该能够正常工作
</li>
<li>这也意味着不使用<code>javascript:</code>伪协议
</li>
<li>甚至不要使用<code>&lt;a href="#"&gt;</code>
</li>
<li>如果有链接一定要使用JavaScript，那么就用JavaScript来把链接添加到页面中 </li>
</ul>
<h3>为CSS和JavaScript提供hook</h3>
<ul>
<li>给一个页面中唯一的元素加上ID
</li>
<li>给重复使用的元素加上class
</li>
<li>使用列表等其他有语义的结构 </li>
</ul>
<h3>使用可靠的技术</h3>
<ul>
<li>从纯HTML和CSS开始
</li>
<li>动态的为链接和表单添加高级交互功能
</li>
<li>同时通过HTML和JSON/XML两种方式提供内容
</li>
<li>为有或没有JavaScript的情况提供额外的CSS
</li>
<li>为有JavaScript和无JavaScript的用户提供不同的应用 </li>
</ul>
<h2>我的看法</h2>
<p>Unobtrusive应该是对<a href="http://www.webstandards.org/" target="_blank"><u>Web Standards</u></a>的进一步深化，也可以理解为Web Standards的一个方面。但是，这种开发方式目前仍然是一种比较理想化的方式，也许在个人，或者小团队的开发中可以贯彻的很好。但是到了大型项目中，严格的执行未必是一种高效的方式。尽管代码的易维护性显而易见，但是大部分项目的前端代码不一定有维护（或者大量维护）的需求，而项目要求的更多是能够在最短时间内完成。</p>
<p>然而，Unobtrusive绝对是一个具有指导性的Web前端开发方式，实现得越接近，无论是对用户，还是对开发者，甚至对计算机，都会更有好处。</p>
]]></content:encoded>
			<wfw:commentRss>http://adam.ofeva.com/blog/47.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>JavaScript开发时的五个小提示</title>
		<link>http://adam.ofeva.com/blog/46.html</link>
		<comments>http://adam.ofeva.com/blog/46.html#comments</comments>
		<pubDate>Mon, 18 Aug 2008 14:59:09 +0000</pubDate>
		<dc:creator>阿当</dc:creator>
				<category><![CDATA[javascript]]></category>

		<guid isPermaLink="false">http://adam.ofeva.com/?p=46</guid>
		<description><![CDATA[真是五个很quick的小提示： 只在 元素上使用submit事件 如果要在form中绑定事件处理程序时，应该只在 元素上绑定submit事件，而不是给提交按钮绑定click事件。 March：这个方式固然很好，但是，公司开发时使用了Web Flow，一个页面就一个大form，而里面可能有若干个提交按钮，所以不得不把部分事件处理程序绑定在了提交按钮的click事件上。 可点击的都应该是链接 不要给除锚元素（）以外的元素绑定click事件。这一点对于键盘用户很重要，因为他们在仅通过键盘获取元素焦点时会遇到困难。 March：不过个人感觉锚元素还是应该只用作链接，而一些功能性的操作（比如Google Reader的Mark all as new），最好还是用来标注，accessibility的问题可以通过快捷键等方式解决。这样做可以更好的还原HTML元素的语义。 简单的for循环优化 在你写一个for循环时，有个很简单的技巧能够提高性能。 for ( var i = 0; i &#60; elements.length; ++i ) 使用下面的语句代替上面的： for ( var i = 0, j = elements.length; i &#60; j; ++i ) 这样可以把元素的个数（elements.length的值）储存在一个变量j中，这样就不必在每次循环时都计算一遍元素的个数。 用匿名函数来作为事件处理程序 尤其是对于短小的函数，创建一个匿名函数会比使用一个命名函数的引用更具可读性。 anchor.onclick = function() { map.goToPosition( home ); return false; } [...]]]></description>
			<content:encoded><![CDATA[<p>真是五个很quick的小提示：</p>
<p>只在<br />
<form>元素上使用submit事件</p>
<p>如果要在form中绑定事件处理程序时，应该只在<br />
<form>元素上绑定submit事件，而不是给提交按钮绑定click事件。<br />
March：这个方式固然很好，但是，公司开发时使用了Web Flow，一个页面就一个大form，而里面可能有若干个提交按钮，所以不得不把部分事件处理程序绑定在了提交按钮的click事件上。</p>
<p>可点击的都应该是链接</p>
<p>不要给除锚元素（<a>）以外的元素绑定click事件。这一点对于键盘用户很重要，因为他们在仅通过键盘获取元素焦点时会遇到困难。</p>
<p>March：不过个人感觉锚元素还是应该只用作链接，而一些功能性的操作（比如Google Reader的Mark all as new），最好还是用<span>来标注，accessibility的问题可以通过快捷键等方式解决。这样做可以更好的还原HTML元素的语义。</p>
<p>简单的for循环优化</p>
<p>在你写一个for循环时，有个很简单的技巧能够提高性能。</p>
<p><code>for ( var i = 0; i &lt; elements.length; ++i )</code><br />
使用下面的语句代替上面的：</p>
<p><code>for ( var i = 0, j = elements.length; i &lt; j; ++i )</code><br />
这样可以把元素的个数（elements.length的值）储存在一个变量j中，这样就不必在每次循环时都计算一遍元素的个数。</p>
<p>用匿名函数来作为事件处理程序</p>
<p>尤其是对于短小的函数，创建一个匿名函数会比使用一个命名函数的引用更具可读性。</p>
<p><code>anchor.onclick = function() { map.goToPosition( home ); return false; }</code><br />
March：在较复杂的JavaScript开发时还是使用命名函数效率更高。</p>
<p>使用Array.join代替字符串连接（concatenating strings）</p>
<p>在将很多字符串、变量等连接成一个很长的字符串时，将所有字符串和变量放入一个数组，然后用join方法将他们组成一个长字符串，这样无论从代码可读性还是从性能上都更胜于字符串连接。<br />
<code><br />
var text = 'There are' + elements.length + 'members in the elements array.';<br />
var text = ['There are', elements.length, 'members in the elements array.'].join(' ');</code></p>
]]></content:encoded>
			<wfw:commentRss>http://adam.ofeva.com/blog/46.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>JavaScript的IE和FF兼容性问题汇总</title>
		<link>http://adam.ofeva.com/blog/45.html</link>
		<comments>http://adam.ofeva.com/blog/45.html#comments</comments>
		<pubDate>Mon, 18 Aug 2008 14:29:11 +0000</pubDate>
		<dc:creator>阿当</dc:creator>
				<category><![CDATA[个人]]></category>

		<guid isPermaLink="false">http://adam.ofeva.com/?p=45</guid>
		<description><![CDATA[以下以 IE 代替 Internet Explorer，以 MF 代替 Mozzila FF 1. document.form.item 问题 现有问题： 现有代码中存在许多 document.formName.item(&#8220;itemName&#8221;) 这样的语句，不能在 MF 下运行 解决方法： 改用 document.formName.elements["elementName"] 其它 参见 2 2. 集合类对象问题 现有问题： 现有代码中许多集合类对象取用时使用 ()，IE 能接受，MF 不能。 解决方法： 改用 [] 作为下标运算。如：document.forms(&#8220;formName&#8221;) 改为 document.forms["formName"]。 又如：document.getElementsByName(&#8220;inputName&#8221;)(1) 改为 document.getElementsByName(&#8220;inputName&#8221;)[1] 3. window.event 现有问题： 使用 window.event 无法在 MF 上运行 解决方法： MF 的 event 只能在事件发生的现场使用，此问题暂无法解决。可以这样变通： 原代码(可在IE中运行)： function [...]]]></description>
			<content:encoded><![CDATA[<p>以下以 IE 代替 Internet Explorer，以 MF 代替 Mozzila FF</p>
<p>1. document.form.item 问题</p>
<p>现有问题：<br />
现有代码中存在许多 document.formName.item(&#8220;itemName&#8221;) 这样的语句，不能在 MF 下运行</p>
<p>解决方法：<br />
改用 document.formName.elements["elementName"]</p>
<p>其它<br />
参见 2</p>
<p>2. 集合类对象问题</p>
<p>现有问题：<br />
现有代码中许多集合类对象取用时使用 ()，IE 能接受，MF 不能。</p>
<p>解决方法：<br />
改用 [] 作为下标运算。如：document.forms(&#8220;formName&#8221;) 改为 document.forms["formName"]。<br />
又如：document.getElementsByName(&#8220;inputName&#8221;)(1) 改为 document.getElementsByName(&#8220;inputName&#8221;)[1]</p>
<p>3. window.event</p>
<p>现有问题：<br />
使用 window.event 无法在 MF 上运行</p>
<p>解决方法：<br />
MF 的 event 只能在事件发生的现场使用，此问题暂无法解决。可以这样变通：<br />
原代码(可在IE中运行)：</p>
<p>                function gotoSubmit() {</p>
<p>                    alert(window.event);    // use window.event</p>
<p>                }</p>
<p>新代码(可在IE和MF中运行)：</p>
<p>                function gotoSubmit(evt) {<br />
                    evt = evt ? evt : (window.event ? window.event : null);</p>
<p>                    alert(evt);             // use evt</p>
<p>                }</p>
<p>此外，如果新代码中第一行不改，与老代码一样的话(即 gotoSubmit 调用没有给参数)，则仍然只能在IE中运行，但不会出错。所以，这种方案 tpl 部分仍与老代码兼容。</p>
<p>4. HTML 对象的 id 作为对象名的问题</p>
<p>现有问题：<br />
在 IE 中，HTML 对象的 ID 可以作为 document 的下属对象变量名直接使用。在 MF 中不能。</p>
<p>解决方法：<br />
用 getElementById(&#8220;idName&#8221;) 代替 idName 作为对象变量使用。</p>
<p>5. 用idName字符串取得对象的问题</p>
<p>现有问题：<br />
在IE中，利用 eval(idName) 可以取得 id 为 idName 的 HTML 对象，在MF 中不能。</p>
<p>解决方法：<br />
用 getElementById(idName) 代替 eval(idName)。</p>
<p>6. 变量名与某 HTML 对象 id 相同的问题</p>
<p>现有问题：<br />
在 MF 中，因为对象 id 不作为 HTML 对象的名称，所以可以使用与 HTML 对象 id 相同的变量名，IE 中不能。</p>
<p>解决方法：<br />
在声明变量时，一律加上 var ，以避免歧义，这样在 IE 中亦可正常运行。<br />
此外，最好不要取与 HTML 对象 id 相同的变量名，以减少错误。</p>
<p>其它：<br />
参见 问题4</p>
<p>7. event.x 与 event.y 问题</p>
<p>现有问题：<br />
在IE 中，event 对象有 x, y 属性，MF中没有。</p>
<p>解决方法：<br />
在MF中，与event.x 等效的是 event.pageX。但event.pageX IE中没有。<br />
故采用 event.clientX 代替 event.x。在IE 中也有这个变量。<br />
event.clientX 与 event.pageX 有微妙的差别（当整个页面有滚动条的时候），不过大多数时候是等效的。</p>
<p>如果要完全一样，可以稍麻烦些：<br />
mX = event.x ? event.x : event.pageX;<br />
然后用 mX 代替 event.x</p>
<p>其它：<br />
event.layerX 在 IE 与 MF 中都有，具体意义有无差别尚未试验。</p>
<p>8. 关于frame</p>
<p>现有问题：<br />
在 IE中 可以用window.testFrame取得该frame，mf中不行</p>
<p>解决方法：<br />
在frame的使用方面mf和ie的最主要的区别是：<br />
如果在frame标签中书写了以下属性：</p>
<p>那么ie可以通过id或者name访问这个frame对应的window对象<br />
而mf只可以通过name来访问这个frame对应的window对象<br />
例如如果上述frame标签写在最上层的window里面的htm里面，那么可以这样访问<br />
ie： window.top.frameId或者window.top.frameName来访问这个window对象<br />
mf： 只能这样window.top.frameName来访问这个window对象</p>
<p>另外，在mf和ie中都可以使用window.top.document.getElementById(&#8220;frameId&#8221;)来访问frame标签<br />
并且可以通过window.top.document.getElementById(&#8220;testFrame&#8221;).src = &#8216;xx.htm&#8217;来切换frame的内容<br />
也都可以通过window.top.frameName.location = &#8216;xx.htm&#8217;来切换frame的内容<br />
关于frame和window的描述可以参见bbs的‘window与frame’文章<br />
以及/test/js/test_frame/目录下面的测试<br />
&#8212;-adun 2004.12.09修改</p>
<p>9. 在mf中，自己定义的属性必须getAttribute()取得<br />
10.在mf中没有 parentElement parement.children  而用parentNode parentNode.childNodes<br />
childNodes的下标的含义在IE和MF中不同，MF使用DOM规范，childNodes中会插入空白文本节点。<br />
一般可以通过node.getElementsByTagName()来回避这个问题。<br />
当html中节点缺失时，IE和MF对parentNode的解释不同，例如</p>
<form>
<table>
</table></form>
<p>MF中input.parentNode的值为form, 而IE中input.parentNode的值为空节点</p>
<p>MF中节点没有removeNode方法，必须使用如下方法 node.parentNode.removeChild(node)</p>
<p>11.const 问题</p>
<p>现有问题：<br />
在 IE 中不能使用 const 关键字。如 const constVar = 32; 在IE中这是语法错误。</p>
<p>解决方法：<br />
不使用 const ，以 var 代替。</p>
<p>12. body 对象</p>
<p>MF的body在body标签没有被浏览器完全读入之前就存在，而IE则必须在body完全被读入之后才存在</p>
<p>13. url encoding在js中如果书写url就直接写&amp;不要写&amp;例如var url = &#8216;xx.jsp?objectName=xx&amp;objectEvent=xxx&#8217;;<br />
frm.action = url那么很有可能url不会被正常显示以至于参数没有正确的传到服务器<br />
一般会服务器报错参数没有找到<br />
当然如果是在tpl中例外，因为tpl中符合xml规范，要求&amp;书写为&amp;<br />
一般MF无法识别js中的&amp;</p>
<p>14. nodeName 和 tagName 问题</p>
<p>现有问题：<br />
在MF中，所有节点均有 nodeName 值，但 textNode 没有 tagName 值。在 IE 中，nodeName 的使用好象<br />
有问题（具体情况没有测试，但我的IE已经死了好几次）。</p>
<p>解决方法：<br />
使用 tagName，但应检测其是否为空。</p>
<p>15. 元素属性</p>
<p>IE下 input.type属性为只读，但是MF下可以修改</p>
<p>16. document.getElementsByName() 和 document.all[name] 的问题</p>
<p>现有问题：<br />
在 IE 中，getElementsByName()、document.all[name] 均不能用来取得 div 元素（是否还有其它不能取的元素还不知道）。</p>
<p>17. DOM 数据岛的问题</p>
<p>现有问题：<br />
在IE中, 标签具有特殊意义, 可以内含XML DOM, 并能实现与HTML组件的数据     绑定. 在MF中,则仅仅是一个未知的标记而已.     另外, 对 IE 来说, 实际意味着这里是一个ActiveX对象, 但它却挂在HTML本     身的DOM树下作为一个节点, 因而会对DOM树的遍历造成严重影响.</p>
<p>解决方法：<br />
IE的数据绑定机制是可以用JS来模拟的, 但是太麻烦, 建议不使用数据绑定机制或者     寻找实现这种模拟的库来做. 我们只讨论如何实现DOM的兼容.     在MF中无论已知的HTML标记还是其它符合XML规范的标记, 都是用统一的DOM树来处理的, 因此, MF事实上完全可以使用DOM数据岛, 但与IE小小的不同在于: 在IE中     是个 DOM document, 而在MF只是 DOM node. 这个差别通常不足为患.     但是有一个小小的细节, 为了兼容HTML相当随意的语法, MF无法识别简写的空标记.     如: ,     这其中和是简写形式的, 会使MF无法识别, 应当写成:              不过, 我怀疑如果用XHTML, 可能就没有这种问题. 但我还没有试过.     对于IE中干扰HTML的DOM结构问题, 我现在的方法是处理完毕后把它从HTML的 DOM中删除. 不知道还有没有更好的解.</p>
]]></content:encoded>
			<wfw:commentRss>http://adam.ofeva.com/blog/45.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>以用户故事管理项目</title>
		<link>http://adam.ofeva.com/blog/44.html</link>
		<comments>http://adam.ofeva.com/blog/44.html#comments</comments>
		<pubDate>Mon, 18 Aug 2008 02:11:12 +0000</pubDate>
		<dc:creator>阿当</dc:creator>
				<category><![CDATA[产品经理的盒子]]></category>

		<guid isPermaLink="false">http://adam.ofeva.com/?p=44</guid>
		<description><![CDATA[什么是用户故事(user story) 假定这个项目的客户是个饮料自动售货机的制造商。他们要求我们为他们的售货机开发一款软件。我们可以找他们的市场经理了解这个软件的需求。 因此，我们的客户就是他们的市场经理。谈需求的时候，有一回他这样说：“用户往售货机每塞一个硬币，售货机都要显示当前该客户已经投了多少钱。当用户投的钱够买某一款饮料时，代表这款饮料的按钮的灯就会亮。如果那个用户按了这个按钮，售货机就放一罐饮料到出口，然后找零钱给他。” 上面的话描述的是一件事情，一件用户通过系统完成他一个有价值的目标（买一罐饮料）的事。这样的过程就叫“用户案例(user case)”或者“用户故事(user story)”。也就是说，上面我们的客户所说的话，就是在描述一个用户故事（user story）。 (我解释一下为什么用故事这个词，没兴趣也可以忽略。在一个系统面前，每个用户要完成同样的目标，都要做这个系统设定的例行的事，这件事情不是一个例子，所以不叫事例，这也不是故事，也不能算一段历程，而是一个例行的事。) 如果我们想要记下这段用户故事，我们可能会用这样的格式： 名称：卖饮料 事件： 1. 用户投入一些钱。 2. 售货机显示用户已经投了多少钱。 3. 如果投入的钱足够买某种饮料，这种饮料对应的按钮的灯就会亮。 4. 用户按了某个亮了的按钮。 5. 售货机卖出一罐饮料给他。 6. 售货机找零钱给他。 注意到，一个用户故事里面的事件可以这样描述： 1. 用户做XX。 2. 系统做YY。 3. 用户做ZZ。 4. 系统做TT。 5&#8230;. 用户故事只是描述系统的外在行为 一个用户故事只是以客户能够明白的方式，描述了一个系统的外在行为，它完全忽略了系统的内部动作。比如，下面有下划线的那些文字，就属于不应该出现在用户故事中的系统内部动作： 1. 用户投入一些钱。 2. 售货机将塞进来的钱存在钱箱里，然后发送一条命令给屏幕，屏幕显示目前已经投入的金额。 3. 售货机查询数据库里面所有饮料的价格，判定钱足够买哪些饮料，对于钱足够买的那些饮料，对应的按钮的灯就会亮起来。 4. 用户按下一个亮起来的按钮。 5. 售货机卖出一罐饮料给用户，然后将数据库里面该饮料的存货数量减1。 6. 售货机找零钱给用户。 不管是口头描述的，还是书面形式，这样的内容是描述用户故事时一个很常见的错误。特别的，千万不要提及任何有关数据库，记录，字段之类的对客户一点意义都没有的东西。 评估发布时间 用户故事是用来干嘛的？假定客户希望在50天内递交这个系统。我们做得了吗？为了解答这个问题，我们就要在项目开始的阶段，试着找出所有的用户故事，然后评估一下，每一项历程需要多长的开发时间。可是，怎么评估呢？ 比如，我们现在收集了下面这些用户故事： 卖饮料：如上面所说的。 取消购买：在投入了一些钱后，用户可以取消购买。 输入管理密码：授权的人可以输入管理密码，然后增加存货，设定价格，拿走里面的钱等等。 [...]]]></description>
			<content:encoded><![CDATA[<p><strong>什么是用户故事(user story)</strong></p>
<p>假定这个项目的客户是个饮料自动售货机的制造商。他们要求我们为他们的售货机开发一款软件。我们可以找他们的市场经理了解这个软件的需求。</p>
<p>因此，我们的客户就是他们的市场经理。谈需求的时候，有一回他这样说：“用户往售货机每塞一个硬币，售货机都要显示当前该客户已经投了多少钱。当用户投的钱够买某一款饮料时，代表这款饮料的按钮的灯就会亮。如果那个用户按了这个按钮，售货机就放一罐饮料到出口，然后找零钱给他。”</p>
<p>上面的话描述的是一件事情，一件用户通过系统完成他一个有价值的目标（买一罐饮料）的事。这样的过程就叫“用户案例(user case)”或者“用户故事(user story)”。也就是说，上面我们的客户所说的话，就是在描述一个用户故事（user story）。<br />
(我解释一下为什么用故事这个词，没兴趣也可以忽略。在一个系统面前，每个用户要完成同样的目标，都要做这个系统设定的例行的事，这件事情不是一个例子，所以不叫事例，这也不是故事，也不能算一段历程，而是一个例行的事。)</p>
<p>如果我们想要记下这段用户故事，我们可能会用这样的格式：</p>
<p>名称：卖饮料</p>
<p>事件：</p>
<p>1. 用户投入一些钱。</p>
<p>2. 售货机显示用户已经投了多少钱。</p>
<p>3. 如果投入的钱足够买某种饮料，这种饮料对应的按钮的灯就会亮。</p>
<p>4. 用户按了某个亮了的按钮。</p>
<p>5. 售货机卖出一罐饮料给他。</p>
<p>6. 售货机找零钱给他。</p>
<p>注意到，一个用户故事里面的事件可以这样描述：</p>
<p>1. 用户做XX。</p>
<p>2. 系统做YY。 </p>
<p>3. 用户做ZZ。</p>
<p>4. 系统做TT。</p>
<p>5&#8230;. </p>
<p><strong>用户故事只是描述系统的外在行为</strong></p>
<p>一个用户故事只是以客户能够明白的方式，描述了一个系统的外在行为，它完全忽略了系统的内部动作。比如，下面有下划线的那些文字，就属于不应该出现在用户故事中的系统内部动作：</p>
<p>1. 用户投入一些钱。</p>
<p>2. 售货机将塞进来的钱存在钱箱里，然后发送一条命令给屏幕，屏幕显示目前已经投入的金额。</p>
<p>3. 售货机查询数据库里面所有饮料的价格，判定钱足够买哪些饮料，对于钱足够买的那些饮料，对应的按钮的灯就会亮起来。</p>
<p>4. 用户按下一个亮起来的按钮。</p>
<p>5. 售货机卖出一罐饮料给用户，然后将数据库里面该饮料的存货数量减1。</p>
<p>6. 售货机找零钱给用户。</p>
<p>不管是口头描述的，还是书面形式，这样的内容是描述用户故事时一个很常见的错误。特别的，千万不要提及任何有关数据库，记录，字段之类的对客户一点意义都没有的东西。</p>
<p><strong>评估发布时间</strong></p>
<p>用户故事是用来干嘛的？假定客户希望在50天内递交这个系统。我们做得了吗？为了解答这个问题，我们就要在项目开始的阶段，试着找出所有的用户故事，然后评估一下，每一项历程需要多长的开发时间。可是，怎么评估呢？<br />
比如，我们现在收集了下面这些用户故事：</p>
<p>卖饮料：如上面所说的。<br />
取消购买：在投入了一些钱后，用户可以取消购买。<br />
输入管理密码：授权的人可以输入管理密码，然后增加存货，设定价格，拿走里面的钱等等。<br />
补充饮料：授权的人可以在输入管理密码后增加存货。<br />
取出钱箱里的钱：授权的人在输入管理密码后，可以取出钱箱里的钱箱里面的钱。<br />
安全警报：有些事情经常发生的话，系统会自动打开安全警报。<br />
打印月销售报表：授权的人可以打印出月销售报表。</p>
<p>然后找出里面最简单的用户故事（这里的“简单”，意思是说实现周期最短）。我们不一定非常精准的判断哪个最简单。只要挑出你觉得最简单的就行了。比如，我们觉得“输入管理密码”是最简单的用户故事。然后我们判断说，这个用户故事算1个“故事点（story point）”。</p>
<p>用户故事故事点<br />
卖饮料<br />
取消购买<br />
输入管理密码 1<br />
补充饮料<br />
取出钱箱里的钱<br />
安全警报<br />
打印月销售报表</p>
<p>不过一般我们不会列出清单，而是做出一堆卡片贴在墙上，每张卡片记录一个用户故事，然后将故事点写在卡片上面：</p>
<p><img alt="image" src="http://www.matrix.org.cn/resource/upload/forum/2007_02_12_080153_UjvyxmkyOn.jpg" border="0"></p>
<p>这样的一张卡片就叫“故事卡（story card）”。</p>
<p>然后开始考虑其他用户故事。比如，对于“取出钱箱里的钱”这个故事，我们认为它跟“输入管理密码”这个故事一样简单，所以它应该也是算1个故事点。我们在列表里面标上。当然，实际操作的时候，我们是在“取出钱箱里的钱”的故事卡上填上故事点。<br />
用户故事故事点<br />
卖饮料<br />
取消购买<br />
输入管理密码 1<br />
补充饮料<br />
取出钱箱里的钱 1<br />
安全警报<br />
打印月销售报表</p>
<p>对于“取消购买”，我们认为它应该是“取出钱箱里的钱”的两倍的工作量，所以它算2个故事点。 <br />
用户故事 故事点<br />
卖饮料<br />
取消购买 2<br />
输入管理密码1<br />
补充饮料<br />
取出钱箱里的钱1<br />
安全警报<br />
打印月销售报表</p>
<p>对于“卖饮料”，我们认为它应该是“取消购买”两倍的复杂度，所以它应该算4个故事点。<br />
用户故事 故事点<br />
卖饮料4<br />
取消购买2<br />
输入管理密码 1<br />
补充饮料<br />
取出钱箱里的钱 1<br />
安全警报<br />
打印月销售报表</p>
<p>对于“补充饮料”，我们又认为它比“取出钱箱里的钱”复杂，但又比“卖饮料”简单，然后它又应该比“取消购买（２个故事点）”复杂，所以我们认为它应该是3个故事点：<br />
用户故事故事点<br />
卖饮料4<br />
取消购买2<br />
输入管理密码 1<br />
补充饮料3<br />
取出钱箱里的钱 1<br />
安全警报<br />
打印月销售报表</p>
<p>类似的，我们认为“安全警报”应该比“补充饮料”简单一些，所以应该是2个故事点： <br />
用户故事故事点<br />
卖饮料4<br />
取消购买2<br />
输入管理密码 1<br />
补充饮料3<br />
取出钱箱里的钱1<br />
安全警报2<br />
打印月销售报表</p>
<p>“打印月销售报表”应该跟“卖饮料”一样复杂，所以应该是算4个故事点，这样的话，我们总共有17个故事点： <br />
用户故事故事点<br />
卖饮料4<br />
取消购买2<br />
输入管理密码1<br />
补充饮料3<br />
取出钱箱里的钱1<br />
安全警报2<br />
打印月销售报表4<br />
总计17</p>
<p>现在挑出任意一个用户故事，估计一下它要花（你一个人）多少时间来完成。假设我们之前做过跟“取出钱箱里的钱”类似的功能，所以我们就挑这个来计算，估计它要花5天的时间。也就是说，一个故事点要花5天的时间来完成。现在我们有17个故事点，也就是说，我们需要17×5＝85天来完成这个项目（如果只是一个开发人员来做的话）。假设现在我们的团队里面有两个开发人员，所以我们就需要85÷2＝43天来完成。</p>
<p>从目前的估计来看，我们可以在50天的期限里面做完这个项目。但现在说这个还太早了！这样的评估，更多的是猜测。通常开发人员在工作量上的估算都很差。事实上，人们经常会低估了工作量。那我们应该怎么估计得更准？ </p>
<p><strong>我们实际的开发速度是多少</strong></p>
<p>为了做到更准的估计，我们就让客户先给我们两周的时间做一些实际的开发，来测量一下我们在这两周里面可以做多少的用户故事。我们叫这两周的时间为“迭代周期”。</p>
<p>哪些用户故事应该放在第一个迭代周期里面做？这可能完全是客户决定的，也可能是大家讨论以后决定的。不过挑出的这些用户故事的故事点不应该超过这两周的承受能力。因为一个迭代周期有10天（假设我们周末不工作），然后我们估计一个开发人员5天可以完成一个故事点。现在我们有两个开发人员，所以我们应该可以在这个迭代周期中完成（10÷5）×2＝4个故事点。</p>
<p>然后客户可以在总故事点不超过４的前提下，挑出一些用户故事在这个迭代周期里实现。他们可能会尽量挑选他们觉得最重要的故事。比如，对他们来说，销售报表跟找零钱最重要，所以他们就挑出这两个：<br />
用户故事故事点<br />
取出钱箱里的钱1<br />
打印月销售报表2<br />
总计3</p>
<p>假设两周的迭代周期过去了，我们完成了“取出钱箱里的钱”，不过“打印月销售报表”没有完成，还剩0.5个故事点没有完成。也就是说，我们在这个迭代周期内完成了3-0.5＝2.5个故事点（比原来的预计要差一些）。 </p>
<p>2.5个故事点这样的数值，就是我们现在的参考值。也就是说，这个团队每个迭代周期可以完成2.5个故事点。这个参考值对我们很有用。它有两个用处： <br />
首先，我们可以假定我们在下个迭代周期也可以完成2.5个故事点，然后客户选择的用户故事的故事点总和不能超过2.5。<br />
其次，在从第一个迭代周期取得参考值以后，我们可以重新估算我们的发布时间了。本来我们估算我们每个开发人员完成1个故事点的时间是5天，现在我们有了实践后的数据了，我们的团队（两个开发人员）可以在一个迭代周期（10天）内完成2.5个故事点。现在总的故事点是17，计算一下，我们需要17÷2.5＝7个迭代周期来完成。14周，也就是70天。也就是说，我们满足不了客户提出的期限（50天内）！怎么办？</p>
<p><strong>预计不能如期完成时怎么办？</strong></p>
<p>很明显，现在我们完成不了全部的用户故事。在这50天里面，我们只能完成50÷10×2.5＝12.5个用户故事。因为现在有17个故事点，我们应该让客户挑出总计4.5个故事点的用户故事，推迟到下一个发布周期去。客户应该选择那些比较次要的用户故事。比如，客户可以推迟“打印月销售报表”这个用户故事。 </p>
<p>（这只是开发不能如期完成时的解决方法之一，这种方法应该是在客户比较有诚意合作的前提下使用。）<br />
用户故事故事点<br />
卖饮料4<br />
取消购买2<br />
输入管理密码1<br />
补充饮料3<br />
取出钱箱里的钱1<br />
安全警报2<br />
总计13</p>
<p>然后他还要挑出总和至少为0.5个故事点的故事。</p>
<p>但是如果“月销售报表”这个用户故事对客户来说也是非常重要，而且其他的故事也不能推迟，这时怎么办？这时我们就要试着简化这些用户故事。比如，原来“月销售报表”是要用一个第三方报表库来实现的，而且还要画出饼状统计图，如果我们只是生成简单的文本格式的报表的话（这格式应该可以被Excel导入，以便日后的处理）。那么这个用户故事的故事点就会从4减少到2，节省了2个故事点。如果客户同意的话，我们就可以将“打印月销售报表”分成两个用户故事“生成月销售文本报表”和“生成图形报表”，然后将后者推迟到下一个发布。</p>
<p>现在新的故事卡片出来了：<br />
用户故事故事点<br />
卖饮料4<br />
取消购买2<br />
输入管理密码 1<br />
补充饮料3<br />
取出钱箱里的钱1<br />
安全警报2<br />
打印月销售文本报表2<br />
总计15</p>
<p>还有其他的2.5个故事点要推迟，我们可以简化“卖饮料”。假设本来我们可以卖不同价格不同类别的饮料。如果现在我们只是简单支持一种价格一种类别的饮料的话，那这个用户故事的故事点可以从4减到2了。客户如果同意的话，我们就可以将“卖饮料”分割为“卖单一饮料”和“卖多种饮料”，然后将后者推迟到下个周期发布： <br />
用户故事故事点<br />
卖单一饮料2<br />
取消购买2<br />
输入管理密码1<br />
补充饮料3<br />
取出钱箱里的钱1<br />
安全警报2<br />
打印月销售文本报表2<br />
总计13</p>
<p>现在还剩0.5个故事点，我们再考虑一下“安全警报”。假设本来这个故事是要同时触发本机上的警报，跟通知附近的一个警察局的。如果现在我们只是触发本机的警报，那所花的故事点就可以从2减到1了。于是在客户同意的情况下，我们将“安全警报”分割为“本机安全警报”和“通知警察”，然后将后者推迟到下个发布： <br />
用户故事故事点<br />
卖单一饮料2<br />
取消购买2<br />
输入管理密码 1<br />
补充饮料3<br />
取出钱箱里的钱1<br />
本机安全警报1<br />
打印月销售文本报表2<br />
总计12</p>
<p>现在我们总计有12个故事点要做了（lt;=12.5）。上面这个筛选在本次发布的用户故事的过程，叫“发布计划编制”。 </p>
<p><strong>增加开发人员来满足发布期限</strong></p>
<p>在上面的例子中，我们以推迟部分用户故事到下个发布周期的办法来解决问题。这种“控制开发范围”通常是最好的解决办法。不过，这种解决办法实施不了的情况下，那你就只好保留所有的用户故事，然后增加更多的开发人员了。在这个例子中，假定我们需要“n”个开发人员，才能在50天内完成17个故事点。50÷10×2.5×n÷2.算出来，n＝2.7，我们需要3个开发人员，也就是多加一个开发人员进来。不过注意： <br />
团队人数加倍并不等于开发周期的减半。它可能只会缩短1/3。如果团队超过10个人的话，增加更多的人员可能反而会延缓项目的进度。<br />
而且项目开发周期越长，团队内的成员对整个项目代码的熟悉度就越少，加上不确定的人员流动，新来人员的业务不熟等其他可能性，这项目会越来越复杂。<br />
总的意思就是，项目人数不能太多，周期不能太长。</p>
<p><strong>根据参考值来掌控项目</strong></p>
<p>每个迭代周期2.5个故事点的这个参考值，只是第一个迭代周期的数据，第二个迭代周期可能会变成2或者3（一般是不会变动得太大）。假设是2的情况下，那对于第三个迭代周期，我们就要将参考值设为2，然后让客户以2为故事点总数来挑选用户故事。 <br />
对于大多数项目，参考值很快就会稳定下来（比如在几个迭代周期后）。当这个值稳定下来后，我们就要重新估计开发周期，重新进行“发布计划编制”了。如果这个参考值告诉我们，我们每个迭代周期可以做3个故事点的话，我们就要让客户挑选更多的用户故事放在这次的发布计划中。相反如果这个参考值是2的话，我们就要让客户减少用户故事（需要的话可以分割一些用户故事），如果团队人员还不多的话，可以增加更多的开发人员。</p>
<p>这是项目的初始阶段绝对要注意的。</p>
<p>发布计划编制，估算每个用户故事时要考虑哪些细节，忽略哪些细节？</p>
<p>在项目初始，我们要找出这个发布周期内所有主要的用户故事，评估每个故事的故事点。可是要怎么评估里面的细节呢？比如对于“卖饮料”， “卖饮料”这个简单的标题，省略了很多的细节：用户会投入什么样的钱？纸币可以吗？人民币可以吗？按钮的灯的亮度要多少？可不可以多个按钮对应一种饮料？按钮被按下以后，要不要变暗？找零钱是不是全部找10分的面额？ <br />
我们是不是要考虑上面所有的细节？对于按钮灯的亮度，我们就不用考虑了，它对我们的工作量没影响。不过，零钱的面额就对我们的工作量很有影响，我们要认真考虑一下（找一堆10分的零钱就很容易实现；如果要尽量减少零钱的个数就比较麻烦了）。处理不同币种也要考虑。 <br />
一般情况，我们不用太担心会漏过什么细节。对于每个用户故事，只要考虑一些“重要”问题就行了。当然，这里面的“重要”，就要根据经验以及客户的观点来决定了。</p>
<p><strong>如果我们不好估算的话怎么做</strong></p>
<p>如果我们觉得，这个用户故事不好估算，那可能的原因就是： </p>
<p>1.这个用户故事太大。这种情况我们就可以将这个用户故事分割出若干个新的用户故事，比如：<br />
将“卖饮料”分割出：<br />
1：显示总投入金额。<br />
2：金额够买的饮料对应的按钮灯亮起来。<br />
3：按下亮灯的按钮，可以买到对应的饮料。<br />
2.我们之前从没开发过自动售货机的程序。因此，我们不知道开发这样的程序有多复杂。这样的话，我们就要做一些实验了，比如做一个让售货机找钱的小程序。这种试验就叫“spike”（翻译不出来）。 </p>
<p><strong>迭代周期内的计划编制</strong></p>
<p>对于这个迭代周期内选择的所有用户故事，不像在发布计划编制那样，只是考虑一些重要的细节，现在我们要从客户那里调查到所有的细节。比如对于“卖饮料”，我们可能会在白板上画出用户交互的草图，然后跟客户一起讨论： <br />
这是一台自动售货机……<br />
用户投入硬币……<br />
假设他投入的是50分，而价格是40分，那么按钮就会亮起（别忘了我们现在做的只卖单一饮料）<br />
用户按一个亮起的按钮，一罐饮料会掉到售货机的出口<br />
找零钱……</p>
<p>在跟客户详细讨论完，了解了足够的细节以后，我们才发现，事实上这个用户故事“卖饮料（只卖单一的）”的故事点远远比我们预计的要麻烦，这时候应该类似前面的发布计划编制那样，1、分割出小的用户故事，挑出一些放在下一个迭代周期内；或者2、挑出这个迭代周期内的一些用户故事放在下一个迭代周期。反之，如果发现这个用户故事比我们想像的还要简单，那我们就要增加更多的用户故事到这个迭代周期内。 </p>
<p><strong>用户故事只是跟用户交流的开始，而不是全部</strong></p>
<p>假想现在已经从客户那边得到足够详细的需求了，我们可以开始实现了。注意，我们不用把所有用户提供的细节都记录下来。为什么呢？假设以后，你有点忘记用户故事，而客户又在你旁边，你是直接问客户，还是去找看需求文档找到你要的东西？当然是直接问客户了。客户可以提示更准确，更完整的需求给你。特别要注意的是，以后只要你一完成一个用户故事，你就要让客户看一下，或者实际的操作一下，因为客户对已经做的的东西了解得越多，那他就可以提供越准确越完整的需求。 <br />
用户挑选完用户故事以后，在之后的两个星期内，我们就要将这些用户故事逐个完成。每个用户故事我们都会设计结构，编码，测试等等。每做完一个用户故事，我们都要让客户验证一下系统是不是他所想的那样。 </p>
<p>在这两个星期内，如果我们提早完成了用户故事，我们就要让客户挑更多的用户故事。相反的，如果我们不能及时完成，我们就要让客户知道当前的进度。 </p>
<p><strong>总结</strong></p>
]]></content:encoded>
			<wfw:commentRss>http://adam.ofeva.com/blog/44.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

