再谈Yahoo关于性能优化的N条军规

18/07/2011瓜瓜

本来这是个老生常谈的问题,上周自成又分享了一些性能优化的建议,我这里再做一个全面的Tips整理,谨作为查阅型的文档,不妥之处,还请指正;
如果你已经对yahoo这些优化建议烂熟于心,果断点这里

一、 Yahoo的军规条例:

谨记:80%-90%的终端响应时间是花费在下载页面中的图片,样式表,脚本,flash等;
详细的解释来这里查:http://developer.yahoo.com/performance/rules.html
也可以直接firebug上一项项比对,如下图:

简单翻译解释下:

1、尽量减少HTTP请求个数——须权衡

合并图片(如css sprites,内置图片使用数据)、合并CSS、JS,这一点很重要,但是要考虑合并后的文件体积。

2、使用CDN(内容分发网络)

这里可以关注CDN的三类实现:镜像、高速缓存、专线,以及智能路由器和负载均衡;

3、为文件头指定Expires或Cache-Control,使内容具有缓存性。

区分静态内容和动态内容,避免以后页面访问中不必要的HTTP请求。

4、避免空的src和href

留意具有这两个属性的标签如link,script,img,iframe等;

5、使用gzip压缩内容

Gzip压缩所有可能的文件类型以来减少文件体积

6、把CSS放到顶部

实现页面有秩序地加载,这对于拥有较多内容的页面和网速较慢的用户来说更为重要,同时,HTML规范清楚指出样式表要放包含在页面的<head />区域内;

7、把JS放到底部

HTTP/1.1 规范建议,浏览器每个主机名的并行下载内容不超过两个,而问题在于脚本阻止了页面的平行下载,即便是主机名不相同

8、避免使用CSS表达式

页面显示和缩放,滚动、乃至移动鼠标时,CSS表达式的计算频率是我们要关注的。可以考虑一次性的表达式或者使用事件句柄来代替CSS表达式。

9、将CSS和JS放到外部文件中

我们需要权衡内置代码带来的HTTP请求减少与通过使用外部文件进行缓存带来的好处的折中点。

10、减少DNS查找次数

我们需要权衡减少 DNS查找次数和保持较高程度并行下载两者之间的关系。

11、精简CSS和JS

目的就是减少下载的文件体积,可考虑压缩工具JSMin和YUI Compressor。

12、避免跳转

为了确保“后退”按钮可以正确地使用,使用标准的 3XXHTTP状态代码;同域中注意避免反斜杠 “/” 的跳转;
跨域使用 Alias或者 mod_rewirte建立 CNAME(保存一个域名和另外一个域名之间关系的DNS记录)

13、剔除重复的JS和CSS

重复调用脚本,除了增加额外的HTTP请求外,多次运算也会浪费时间。在IE和Firefox中不管脚本是否可缓存,它们都存在重复运算JavaScript的问题。

14、配置ETags

Entity tags(ETags)(实体标签)是web服务器和浏览器用于判断浏览器缓存中的内容和服务器中的原始内容是否匹配的一种机制(“实体”就是所说的“内 容”,包括图片、脚本、样式表等),是比last-modified date更更加灵活的机制,单位时间内文件被修过多次,Etag可以综合Inode(文件的索引节点(inode)数),MTime(修改时间)和Size来精准的进行判断,避开UNIX记录MTime只能精确到秒的问题。 服务器集群使用,可取后两个参数。使用ETags减少Web应用带宽和负载。

15、使AJAX可缓存

利用时间戳,更精巧的实现响应可缓存与服务器数据同步更新。

16、尽早刷新输出缓冲

尤其对于css,js文件的并行下载更有意义

17、使用GET来完成AJAX请求

当使用XMLHttpRequest时,浏览器中的POST方法是一个“两步走”的过程:首先发送文件头,然后才发送数据。在url小于2K时使用GET获取数据时更加有意义。

18、延迟加载

确定页面运行正常后,再加载脚本来实现如拖放和动画,或者是隐藏部分的内容以及折叠内容等。

19、预加载

关注下无条件加载,有条件加载和有预期的加载。

20、减少DOM元素个数

使用更适合或者在语意是更贴切的标签,要考虑大量DOM元素中循环的性能开销。

21、根据域名划分页面内容

很显然, 是最大限度地实现平行下载

22、尽量减少iframe的个数

考虑即使内容为空,加载也需要时间,会阻止页面加载,没有语意,注意iframe相对于其他DOM元素高出1-2个数量级的开销,它会在典型方式下阻塞onload事件,IE和Firefox中主页面样式表会阻塞它的下载。

23、避免404

HTTP请求时间消耗是很大的,有些站点把404错误响应页面改为“你是不是要找***”,这虽然改进了用户体验但是同样也会浪费服务器资源(如数据库等)。最糟糕的情况是指向外部 JavaScript的链接出现问题并返回404代码。首先,这种加载会破坏并行加载;其次浏览器会把试图在返回的404响应内容中找到可能有用的部分当作JavaScript代码来执行。

24、减少Cookie的大小

去除不必要的coockie
使coockie体积尽量小以减少对用户响应的影响
注意在适应级别的域名上设置coockie以便使子域名不受影响
设置合理的过期时间。较早地Expire时间和不要过早去清除coockie,都会改善用户的响应时间。

25、使用无cookie的域

确定对于静态内容的请求是无coockie的请求。创建一个子域名并用他来存放所有静态内容。

26、减少DOM访问

缓存已经访问过的有关元素
线下更新完节点之后再将它们添加到文档树中
避免使用JavaScript来修改页面布局

27、开发智能事件处理程序

有时候我们会感觉到页面反应迟钝,这是因为DOM树元素中附加了过多的事件句柄并且些事件句病被频繁地触发。这就是为什么说使用event delegation(事件代理)是一种好方法了。如果你在一个div中有10个按钮,你只需要在div上附加一次事件句柄就可以了,而不用去为每一个按 钮增加一个句柄。事件冒泡时你可以捕捉到事件并判断出是哪个事件发出的。
你同样也不用为了操作DOM树而等待onload事件的发生。你需要做的就是等待树结构中你要访问的元素出现。你也不用等待所有图像都加载完毕。
你可能会希望用DOMContentLoaded事件来代替 事件应用程序中的onAvailable方法。

28、用<link>代替@import

在IE中,页面底部@import和使用<link>作用是一样的,因此最好不要使用它。

29、避免使用滤镜

完全避免使用AlphaImageLoader的最好方法就是使用PNG8格式来代替,这种格式能在IE中很好地工作。如果你确实需要使用 AlphaImageLoader,请使用下划线_filter又使之对IE7以上版本的用户无效。

30、优化图像

尝试把GIF格式转换成PNG格式,看看是否节省空间。在所有的PNG图片上运行pngcrush(或者其它PNG优化工具)

31、优化CSS Spirite

在Spirite中水平排列你的图片,垂直排列会稍稍增加文件大小;
Spirite中把颜色较近的组合在一起可以降低颜色数,理想状况是低于256色以便适用PNG8格式;
便于移动,不要在Spirite的图像中间留有较大空隙。这虽然不大会增加文件大小但对于用户代理来说它需要更少的内存来把图片解压为像素地图。 100×100的图片为1万像素,而1000×1000就是100万像素。

32、不要在HTML中缩放图像——须权衡

不要为了在HTML中设置长宽而使用比实际需要大的图片。如果你需要:

<img width=”100″ height=”100″ src=”mycat.jpg” alt=”My Cat” />

那么你的图片(mycat.jpg)就应该是100×100像素而不是把一个500×500像素的图片缩小使用。这里在下文有更有趣的分析。

33、favicon.ico要小而且可缓存

favicon.ico是位于服务器根目录下的一个图片文件。它是必定存在的,因为即使你不关心它是否有用,浏览器也会对它发出请求,因此最好不要返回一 个404 Not Found的响应。由于是在同一台服务器上,它每被请求一次coockie就会被发送一次。这个图片文件还会影响下载顺序,例如在IE中当你在 onload中请求额外的文件时,favicon会在这些额外内容被加载前下载。

因此,为了减少favicon.ico带来的弊端,要做到:
文件尽量地小,最好小于1K
在适当的时候(也就是你不要打算再换favicon.ico的时候,因为更换新文件时不能对它进行重命名)为它设置Expires文件头。你可以很安全地 把Expires文件头设置为未来的几个月。你可以通过核对当前favicon.ico的上次编辑时间来作出判断。
Imagemagick可以帮你创建小巧的favicon。

34、保持单个内容小于25K

因为iPhone不能缓存大于25K的文件。注意这里指的是解压缩后的大小。由于单纯gizp压缩可能达不要求,因此精简文件就显得十分重 要。

35、打包组件成复合文本

页面内容打包成复合文本就如同带有多附件的Email,它能够使你在一个HTTP请求中取得多个组件(切记:HTTP请求是很奢侈的)。当你使用这条规 则时,首先要确定用户代理是否支持(iPhone就不支持)。

二、Yahoo军规之外的场景?

1、 使用json作为数据的交换格式
Json在浏览器解析的效率至少高于XML一个数量级,高级浏览器中内置的有生成和解析json的方法,IE6中要用额外的方法(http://json.org),不要用eval,容易引发性能和安全问题。
2、 尽可能对images和table设定宽高值

针对Yslow的不要在HTML中缩放图像——第33条,有人会误解为不要对图片加宽高值,其实这条建议本身的意思是不要为了获取一个特定大小的图片,而去强行通过设置宽高值拉伸或者压缩一个既有的图片。建议是另存一张符合尺寸的图片替代。
对图片和table是设定宽高,是考虑到如果浏览器能立刻知道图片或者tables的宽高,它就能够直接呈现页面而不需要通过计算元素大小后重绘,而且即便是图片损毁而没有展现,也不会进而破坏了页面本来的布局。
有一些应用场景需要注意:

  • a、批量图片,图片源可控同时页面图片宽高值不可变,比如数据库有100张100*100的图片要在页面中全部展示,那么建议是都写上

    <img width=”100″ height=”120″ src=”" alt=”" />

  • b、批量图片,图片源不可控同时页面图片宽高值不可变,比如数据库有100张图片,而已知图片有的尺寸是97*100,有的100*105,而又不可能去一张张修改另存。这里视情况而定,根据图片尺寸与要求尺寸的偏离度,在保证图片不拉伸变形同时不影响页面布局的情况下,可以对图片单独设定宽度100,同时对其包裹的容器设定100*100的宽高来隐藏多出来的部分,注意不能同时设置宽高以防止变形。
  • c、批量图片,图片源不可控,页面图片宽高值不定,比如数据库有100张各种尺寸偏差较大的,此时可不对图片设置宽高;
    其他情况不一一罗列,原则是在最大程度保证图片不变形与图片最大面积展现的前提下,尽可能为图片设置宽高值,总之就是权衡。
    Tables的宽高值同图片,尽可能设置。
  • 3、 拆离内容块

    尽量用div取代tables,或者将tables打破成嵌套层次深的结构;

    避免用这样的嵌套

    <table>
    	<table>
    		<table>
    			...
    		</table>
    	</table>
    </table>
    

    采用下面的或者div重构:

    <table></table>
    <table></table>
    <table></table>
    

    4、 高效的CSS书写规则

    众所周知,CSS选择符是从右向左进行匹配的。
    通常一个图片列表的的小模块

    <div id="box">
    	<div class="hd">
    		<h3>我的旅途</h3>
    	</div>
    	<div class="bd">
    		<h4>旅途1</h4>
    		<ul id="pics">
    			<li>
    				<a href="#pic" title=""><img src="" alt="" /> </a>
    				<p>这是在<strong>图片1</strong></p>
    			</li>
    		</ul>
    	</div>
    </div>
    

    为了代码上缩进后内层的整洁性,我们html有可能这样写之外,更喜欢看这样的css写法:

    .box{border:1px solid #ccc }
    .box .hd{border-bottom:1px solid #ccc }
    .box .hd h3{color:#515151}
    .box .bd{color:#404040 }
    .box .bd ul{margin-left:10px}
    .box .bd ul li{border-bottom:1px dashed #f1f1f1}
    .box .bd ul li a{text-decoration:none}
    .box .bd ul li a:hover{text-decoration:underline}
    .box .bd ul li a img{border:1px solid #ccc}
    .box .bd ul li p{text-align:left;}
    .box .bd ul li p strong{color:#ff6600}
    

    其实写到这里,问题已经显而易见了。深达五层抑或六层的嵌套,同时右边的选择符都是采用标签,在满足我们视觉平整与代码结构系统化的时候,付出的是性能的代价。

    不做进一步的代码书写方式的探讨,受个人习惯与应用场景影响。这里对css选择符按照开销从小到大的顺序梳理一下:

  • ID选择符 #box
  • 类选择符 .box
  • 类型选择符 div
  • 相邻兄弟选择符 h4 + #pics
  • 子选择符 #pics li
  • 后代选择符 .box a{}
  • 通配选择符 *
  • 属性选择符 [href=”#pic”]
  • 伪类和伪元素 a:hover

参考《高性能网站建设-进阶指南》,有如下建议:

  • 避免使用统配规则;
  • 不要限定ID选择符;
  • 不要限定类选择符;
  • 让规则越具体越好;
  • 避免使用后代选择符;
  • 避免使用标签-子选择符;
  • 质疑子选择符的所有用途;
  • 依靠继承;

还要注意到,即便是页面加载后,当页面被触发引起回流(reflow)的时候,低效的选择符依然会引发更高的开销,显然这对于用户是不佳的体验。

4、Javascript 的性能优化点

  • a、慎用Eval

    谨记:有“eval”的代码比没有“eval”的代码要慢上 100 倍以上。主要原因是:JavaScript 代码在执行前会进行类似“预编译”的操作:首先会创建一个当前执行环境下的活动对象,并将那些用 var 申明的变量设置为活动对象的属性,但是此时这些变量的赋值都是 undefined,并将那些以 function 定义的函数也添加为活动对象的属性,而且它们的值正是函数的定义。但是,如果你使用了“eval”,则“eval”中的代码(实际上为字符串)无法预先识别其上下文,无法被提前解析和优化,即无法进行预编译的操作。所以,其性能也会大幅度降低。

  • b、推荐尽量使用局部变量

    JavaScript 代码解释执行,在进入函数内部时,它会预先分析当前的变量,并将这些变量归入不同的层级(level),一般情况下:
    局部变量放入层级 1(浅),全局变量放入层级 2(深)。如果进入“with”或“try – catch”代码块,则会增加新的层级,即将“with”或“catch”里的变量放入最浅层(层 1),并将之前的层级依次加深。变量所在的层越浅,访问(读取或修改)速度越快,尤其是对于大量使用全局变量的函数里面。

  • c、字符串数组方式拼接避免在IE6下的开销

    var tips = 'tip1'+'tip2';
    

    这是我们拼接字符串常用的方式,但是这种方式会有一些临时变量的创建和销毁,影响性能,尤其是在IE6下,所以推荐使用如下方式拼接:

    var tip_array = [],tips;
    tip_array.push('tip1');
    tip_array.push('tip2');
    tips = tip_array.join('');
    

    当然,最新的浏览器(如火狐 Firefox3+,IE8+ 等等)对字符串的拼接做了优化,性能略快于数组的“join”方法。

  • 以上仅列出三种常见的优化方法,仅抛砖以引玉石,更多的javascript优化点,比如避免隐式类型转换, 缩小对象访问层级,利用变量优化字符串匹配等大家可以继续深入挖掘;

5、DOM 操作优化

首先澄清两个概念——Repaint 和 Reflow:Repaint 也叫 Redraw,它指的是一种不会影响当前 DOM 的结构和布局的一种重绘动作。如下动作会产生 Repaint 动作:

  • 不可见到可见(visibility 样式属性);
  • 颜色或图片变化(background, border-color, color 样式属性);
  • 不改变页面元素大小,形状和位置,但改变其外观的变化

Reflow 比起 Repaint 来讲就是一种更加显著的变化了。它主要发生在 DOM 树被操作的时候,任何改变 DOM 的结构和布局都会产生 Reflow。但一个元素的 Reflow 操作发生时,它的所有父元素和子元素都会放生 Reflow,最后 Reflow 必然会导致 Repaint 的产生。举例说明,如下动作会产生 Reflow 动作:

  • 浏览器窗口的变化;
  • DOM 节点的添加删除操作
  • 一些改变页面元素大小,形状和位置的操作的触发
  • 通过 Reflow 和 Repaint 的介绍可知,每次 Reflow 比其 Repaint 会带来更多的资源消耗,因此,我们应该尽量减少 Reflow 的发生,或者将其转化为只会触发 Repaint 操作的代码。

    var tipBox = document.createElement('div');
    document.body.appendChild('tipBox');//reflow
    var tip1 = document.createElement('div');
    var tip2 = document.createElement('div');
    tipBox.appendChild(tip1);//reflow
    tipBox.appendChild(tip2);//reflow
    

    如上的代码,会产生三次reflow,优化后的代码如下:

    var tipBox = document.createElement('div');
    	  tip1 = document.createElement('div');
    	  tip2 = document.createElement('div');
    tipBox.appendChild(tip1);
    tipBox.appendChild(tip2);
    document.body.appendChild('tipBox');//reflow
    

    当然还可以利用 display 来减少reflow次数

    var tipBox = document.getElementById('tipBox');
    tipBox.style.display = 'none';//reflow
    tipBox.appendChild(tip1);
    tipBox.appendChild(tip2);
    tipBox.appendChild(tip3);
    tipBox.appendChild(tip4);
    tipBox.appendChild(tip5);
    tipBox.style.width = 120;
    tipBox.style.height = 60;
    tipBox.style.display = 'block';//reflow
    

    DOM元素测量属性和方法也会触发reflow,如下:

    var tipWidth = tipBox.offsetWidth;//reflow
    	  tipScrollLeft = tipBox.scrollLeft;//reflow
    	  display = window.getComputedStyle(div,'').getPropertyValue('display');//reflow
    

    触发reflow的属性和方法大概有这些:

    • offsetLeft
    • offsetTop
    • offsetHeight
    • offsetWidth
    • scrollTop/Left/Width/Height
    • clientTop/Left/Width/Height
    • getComputedStyle()
    • currentStyle(in IE))

    我们可以用临时变量将“offsetWidth”的值缓存起来,这样就不用每次访问“offsetWidth”属性。这种方式在循环里面非常适用,可以极大地提高性能。

    如果有批量的样式属性需要修改,建议通过替换className的方式来降低reflow的次数,曾经有这样一个场景:有三个intput,分别对应下面三个图片和三个内容区域,第二input选中的时候,第二图片显示,其他图片隐藏,第二块内容显示,其他内容隐藏,直接操作DOM节点的代码如下

    var input = [];
    	  pics = [];
    	  contents = [];
    ......
    inputFrame.onclick =function(e){
        var _e,_target;
        _e = e ? window.event : null;
        if(!_e){
          return;
       }else{
         _target = _e.srcElement || _e.target ;
         _index = getIndex(_target);//reflow两次
        show(_target,_index);//reflow两次
       }
    
    }
    function show(target,j){
    	for(var i = 0,i<3;i++){
    		target[i].style.display = 'none';//reflow
    	}
    	target[j].style.display = 'block';//reflow
    }
    function getIndex(targer){
        if(target){
        .....//获取当前的元素索引
        return index;
        }
    }
    

    如果是通过css预先定义元素的隐藏和显示,通过对父级的className进行操纵,将会把reflow的次数减少到1次

    .pbox .pic,.pbox content{display:none}
    .J_pbox_0 .pic0,.J_pbox_0 .content0{diplay:block}
    .J_pbox_1 .pic1,.J_pbox_1 .content1{diplay:block}
    .J_pbox_2 .pic2,.J_pbox_2 .content2{diplay:block}
    
    var input = [],
    	  parentBox = document.getELementById('J_Pbox');
    ......
    inputFrame.onclick =function(e){
        var _e,_target;
        if(){
         ...
        }else{
         ...
          parentBox.className = 'pbox J_pbox_'+_infex;//reflow一次
        }
    }
    

    三、Yahoo军规再度挖掘会怎样?

    在网站性能优化的路上,是不会有终点的,这也是前端工程师永不会妥协的地方。
    想看到更牛P的优化建议么,请移步这里来关注李牧童鞋的分享:

    • 使用combo合并静态资源
    • Bigpipe技术合并动态数据
    • Comet:基于http的服务端推技术
    • 使用DataURI减少图片请求
    • 使用良好的JS,CSS版本管理方案
    • 尝试仅作必要的JS更新
    • 利用本地存储做缓存
    • 关于最小化HTML
    • 进一步讨论Gzip
    • 进一步讨论域名划分
    • 打开keep-alive,重用HTTP连接
    • 使用JSON进行数据交换
    • 保障页面可交互性
    • 缩短最快可交互时间
    • 异步无阻脚本下载
    • 优化内存使用,防止内存泄露
    • 高效的JavaScript
    • 第三方代码性能问题
    • Inline脚本不要与CSS穿插使用
    • 使用高效的CSS选择器
    • 进一步讨论及早Flush
    • 关于视觉和心理学

重温Javascript继承机制

07/06/2011瓜瓜

上段时间,团队内部有过好几次几次给力的分享,这里对西风师傅分享的继承机制稍作整理一下,适当加了写口语化的描述,留作备案。

一、讲个故事吧

澄清在先,Java 和Javascript是雷锋和雷峰塔的关系。Javascript原名Mocha,当时还叫做LiveScript,创造者是Brendan Eich,现任Mozilla公司首席技术官。

1994年,历史上第一个比较成熟的网络浏览器——Navigator0.9版诞生在网景公司(Netscape),极为轰动。
但是,Navigator0.9只能用来浏览,不具备与访问者交互的能力,比如,用户提交一个数据表单,如果表单为空,浏览器是无法判断的,只能直接提交给服务器端,再把空值的错误返回,让用户重新填写,这样显然是低效率和浪费资源的。

这个时候,对于正处于技术革新最前沿的 Netscape,开发一种实用的客户端脚本语言来处理这些问题变得必要起来,于是,这个任务落到了工程师Brendan Eich身上。他觉得吧,木必要设计得很复杂,只要能搞定一些简单操作就够了,比如判断用户有没有填写表单。

1994年正是面向对象编程(object-oriented programming)的兴盛时代,C++是最流行的语言,而Java语言的1.0版即将于次年推出,Brendan Eich难免受其影响, 他想将Javascript里面所有的数据类型看做是对象(object),这一点与Java非常相似。但是,他马上就遇到了一个难题,到底要不要设计”继承”机制呢?

二、继承的演变

1、采用new关键字生成实例

处理表单验证这样简单功能脚本语言显然是不需要”继承”机制的,然而如果Javascript里面都是对象,就需要有一种办法来把所有对象联系起来。最后,Brendan Eich还是设计了”继承”。只是,他并没有引入”类”(class)的概念,因为一旦有了”类”,Javascript就是一种完整的面向对象编程语言了,
这好像有点太正式了,与设计初衷也远了,同时增加了初学者的入门难度。
参照到C++和Java语言都使用new命令来生成实例:

C++这样写:

ClassName *object = new ClassName(param);

Java这样写:

Foo foo = new Foo();

那么,也可以把new命令引入了Javascript,用来从原型对象生成一个实例对象。但是,Javascript中没有”类”的话,怎样表示原型对象呢?
依然是参照C++和Java使用new命令时,都会调用”类”的构造函数(constructor)。Brendan Eich简化了设计,在Javascript语言中,new命令后面跟的是构造函数,不再是类。
我们举个例子来说,现在有一个叫做WD构造函数,表示前端开发(web-developper)对象的原型。

function WD(skill){
	this.skill = skill;
}

对这个构造函数使用new关键字,就会生成一个前端开发对象的实例。

var WD1 = new WD('html');
console.log(WD1.skill); // html

在构造函数中的this关键字,它其实代表的是新创建的实例对象。

2、new 出来对象的缺陷

采用new关键字,用构造函数生成实例对象无法共享属性和方法。
比如,在WD对象的构造函数中,设置一个实例对象的共有属性skill。

function WD(skill){
	this.skill = skill;
	this.sex = '男';
}

然后,生成两个实例对象:

var WD1 = new WD('html');
var WD2 = new WD('css');

这两个对象的skill属性是独立的,修改其中一个,不会影响到另一个。

WD1.skill= 'Javascript';
console.log(WD2.skill);//“css”,不受WD1的影响

每一个实例对象,都有自己的属性和方法的副本。这不仅无法做到数据共享,也是极大的资源浪费。

3、引入prototype属性

为了实现属性和方法的共享,Brendan Eich决定为构造函数设置一个prototype属性。
这个属性包含一个对象(以下简称”prototype对象”),所有实例对象需要共享的属性和方法,都放在这个对象里面;那些不需要共享的属性和方法,就放在构造函数里面。
实例对象一旦创建,将自动引用prototype对象的属性和方法。也就是说,实例对象的属性和方法,分成两种,一种是本地的,另一种是引用的。
还是以WD构造函数为例,现在用prototype属性进行改写:

function WD(skill){
	this.skill = skill;
}

WD.prototype = { sex : '男' };

var WD1 = new WD('html');
var WD2 = new WD('css');

console.log(WD1.sex); // 男
console.log(WD2.sex); // 男

现在,sex属性放在prototype对象里,是两个实例对象共享的。只要修改了prototype对象,就会同时影响到两个实例对象。

WD.prototype.sex = '女';
console.log(WD1.sex); //女
console.log(WD2.sex); // 女

由于所有的实例对象共享同一个prototype对象,那么从外界看起来,prototype对象就好像是实例对象的原型,而实例对象则好像”继承”了prototype对象一样。这就是Javascript继承机制的设计思想。

三、构造函数如何实现继承

现在有一个”MED”对象的构造函数(MED:Marketing Experience Design,营销体验设计)

function MED(){
	this.aim = "营销体验设计";
}

依然是”WD”对象的构造函数,

function WD(skill,sex){
	this.skill = skill;
	this.sex = sex;
}

怎样才能使”WD”继承”MED”呢?

1. apply绑定构造函数实现

最简单的方法,大概就是使用call或apply方法,将父对象的构造函数绑定在子对象上,也就是在子对象构造函数中加一行:

function WD(skill,sex){

	MED.apply(this, arguments);

	this.skill = skill;
	this.sex = sex;
}

var WD1 = new WD("Html","男");
console.log(WD1.aim); // "营销体验设计"

2. prototype模式实现

我们通常的做法是使用prototype属性。如果”WD”的prototype对象,指向一个MED的实例,那么所有”WD”的实例,就能继承MED了。

WD.prototype = new MED();//我们将WD的prototype对象指向一个MED的实例。
WD.prototype.constructor = WD;
var WD1 = new WD("Html","男");
console.log(WD1.aim); // 营销体验设计

这句

WD.prototype = new MED();

相当于完全删除了prototype 对象原先的值,然后赋予一个新值。那么第二行又是什么意思呢?

WD.prototype.constructor = WD;

原来,任何一个prototype对象都有一个constructor属性,指向它的构造函数。也就是说,WD.prototype 这个对象的constructor属性,是指向WD的。
我们在前一步已经删除了这个prototype对象原来的值,所以新的prototype对象没有constructor属性,需要我们手动加上去,否则后面的”继承链”会出问题。这就是第二行的意思。
注意,这是很重要的一点,编程时务必要遵守,下文都遵循这一点,即如果替换了prototype对象,

o.prototype = {};

那么,下一步必然是为新的prototype对象加上constructor属性,并将这个属性指回原来的构造函数。

o.prototype.constructor = o;

3. 从prototype直接继承实现

由于MED对象中,不变的属性都可以直接写入MED.prototype。所以,我们也可以让WD()跳过 MED(),直接继承MED.prototype。
现在,我们先将MED对象改写:

function MED(){ }

MED.prototype.skill = "MED";

然后,将WD的prototype对象指向MED的prototype对象,这样就完成了继承。

WD.prototype = MED.prototype;
WD.prototype.constructor = WD;

var WD1 = new WD("Html","男");

console.log(WD1.skill); // MED

与前一种方法相比,这样做的优点是效率比较高(不用执行和建立MED的实例了),比较省内存。缺点是 WD.prototype和MED.prototype现在指向了同一个对象,那么任何对WD.prototype的修改,都会反映到MED.prototype。
所以,上面这一段代码其实是有问题的。请看第二行

WD.prototype.constructor = WD;

这一句实际上把MED.prototype对象的constructor属性也改掉了!

console.log(MED.prototype.constructor); // WD

4. 利用一个空对象作为中介来实现

由于”直接继承prototype”存在上述的缺点,所以可以利用一个空对象作为中介。

var F = function(){};
F.prototype = MED.prototype;

WD.prototype = new F();
WD.prototype.constructor = WD;

F是空对象,所以几乎不占内存。这时,修改WD的prototype对象,就不会影响到MED的prototype对象。

console.log(MED.prototype.constructor); // MED

5.利用 prototype模式的封装函数

我们将上面的方法,封装成一个函数,便于使用。

function extend(Child, Parent) {

	var F = function(){};

	F.prototype = Parent.prototype;
	Child.prototype = new F();
	Child.prototype.constructor = Child;
}

使用的时候,方法如下

extend(WD,MED);
var WD1 = new WD("Html","男");
console.log(WD1.aim); // 营销体验设计

这个extend函数就是YUI库如何实现继承的方法。

6. 拷贝继承实现

上面是采用prototype方式来实现继承。其实既然子对象会拥有父对象的属性和方法,我们直接采用”拷贝”方法也可以达到效果。简单说,如果把父对象的所有属性和方法,拷贝进子对象,不也能够实现继承吗?
首先,还是把MED的所有不变属性,都放到它的prototype对象上。

function MED(){}

MED.prototype.aim = "营销体验设计";

然后,再写一个函数,实现属性拷贝的目的。

function extendCopy(Child, Parent) {
	var p = Parent.prototype;
	var c = Child.prototype;
	for (var i in p) {
		c[i] = p[i];
	}
}

这个函数的作用,就是将父对象的prototype对象中的属性,一一拷贝给Child对象的prototype对象。
使用的时候,这样写:

extendCopy(WD, MED);
var WD1 = new WD("Html","男");
console.log(WD1.aim); // 营销体验设计

四、”非构造函数”的如何实现继承

比如,现在有一个对象,叫做”MED”–营销体验设计。

var MED = {
	aim:'营销体验设计'
}

还有一个对象,叫做”前端开发”。

var WD ={
	skill:'html'
}

请问怎样才能让”前端开发”去继承”营销体验设计”,就是说,我怎样才能生成一个”营销体验设计的前端开发”对象?
这里要注意,这两个对象都是普通对象,不是构造函数,无法使用构造函数方法实现”继承”。

1、object()方法

Json格式的发明者Douglas Crockford,提出了一个object()函数,可以做到这一点。

function object(o) {

	function F() {}

	F.prototype = o;

	return new F();
}

这个object()函数,其实只做一件事,就是把子对象的prototype属性,指向父对象,从而使得子对象与父对象连在一起。
使用的时候,第一步先在父对象的基础上,生成子对象:

var WD = object(MED);

然后,再加上子对象本身的属性:

WD.skill = 'html';

这时,子对象已经继承了父对象的属性了。

console.log(WD.aim); //营销体验设计

2、浅拷贝

除了使用”prototype链”以外,还有另一种思路:把父对象的属性,全部拷贝给子对象,也能实现继承。
下面这个函数,就是在做拷贝:

function LightCopy(p) {
	var c = {};
	for (var i in p) {
		c[i] = p[i];
	}
	//c.uber = p;
	return c;
}

使用的时候,这样写:

var WD = LightCopy(MED);
WD.aim = '前端开发';

但是,这样的拷贝有一个问题。那就是,如果父对象的属性等于数组或另一个对象,那么实际上,子对象获得的只是一个内存地址,而不是真正拷贝,因此存在父对象被篡改的可能。
请看,现在给MED添加一个”技能”属性,它的值是一个数组。

MED.skills = ['‘html’','css','Javascript'];

通过LightCopy()函数,WD继承了MED。

var WD = LightCopy(MED);

然后,我们为WD的”技能”添加一个属性:

WD.skills.push('teamwork');

发生了什么事?MED的”技能”也被篡改了!

console.log(WD.skills); //‘html’,'Javascript','css','teamwork'
console.log(MED.skills); //‘html’,'Javascript','css','teamwork'

所以,LightCopy()只是拷贝基本类型的数据,我们把这种拷贝叫做”浅拷贝”。这是早期jQuery实现继承的方式。

3、深拷贝

所谓”深拷贝”,就是能够实现真正意义上的数组和对象的拷贝。它的实现并不难,只要递归调用”浅拷贝”就行了。

function deepCopy(p, c) {
	var c = c || {};
	for (var i in p) {
		if (typeof p[i] === 'object') {
			c[i] = (p[i].constructor === Array) ? [] : {};
			deepCopy(p[i], c[i]);
		} else {
			c[i] = p[i];
		}
	}
	return c;
}

使用的时候这样写:

var WD = deepCopy(MED);

现在,给父对象加一个属性,值为数组。然后,在子对象上修改这个属性:

MED.skills = ['‘html’','css','Javascript'];
WD.skills.push('teamwork');

这时,父对象就不会受到影响了。

console.log(WD.skills); //‘html’,'css','Javascript','teamwork'
console.log(MED.skills); //‘html’,'css','Javascript'

目前,jQuery库使用的就是这种继承方法。


小结下dom节点操作

16/02/2011风月

一、节点的定义

dom节点树

dom节点树

图中可见节点HTML文档中的每个成分都是一个节点:

  • 整个文档是一个文档节点
  • 每个HTML标签是一个元素节点
  • 包含在HTML元素中的文本是文本节点
  • 每个HTML属性是一个属性节点
  • 注释属于注释节点

备注:通过DOM,可以访问HTML文档中的每个节点。

二、节点引用

节点的绝对引用:

  • document.documentElement返回文档的根节点
  • document.activeElement返回当前文档中被击活的标签节点
  • event.fromElement返回鼠标移出的源节点
  • event.toElement返回鼠标移入的源节点
  • event.srcElement返回激活事件的源节点

节点的相对引用:(设当前对节点为node)

  • node.parentNode node.parentElement 返回父节点,document.parentNode()返回null
  • node.childNodes[1] 符合标准,返回子节点集合(包含文本节点及标签节点),文本和属性节点的childNodes永远是null.先获取长度node.childNodes.length,然后可以通过循环或者索引找到需要的节点.
    //对与文本节点的处理:
    eg:

    var myTextNodes = document.getElementById("test").childNodes;
    var count = myTextNodes.length;
    for(var i = 0; i < count; i++) {
      if(myTextNodes[i].nodeType=="3" && myTextNodes[i].nodeName!="#text"){//排除IE空白文本的节点
      alert(myTextNodes[i]);
    }
    }
    
  • node.children 不符合标准,不推荐使用,它只返回html节点,甚至不返回文本节点
  • node.firstChild返回第一个子节点,firstChild=childNodes[0]
  • node.lastChild返回最后一个子节点,lastChide=childNodes[childNodes.length-1]
  • node.nextSibling()返回同属下一个节点
  • node.previousSibling()返回同属上一个节点

三、节点操作

  • 节点定位
    getElementById(elementId)
    //寻找一个有着给定id属性值的元素,返回一个元素节点 ,document.getElementById(IDvalue)
    
    getElementsByTagName(tagName)
    //用于寻找有着给定标签名的所有元素,document.getElementsByTagName(tagName)
    
    getElementsByName(elementName)
    //在HTML中checkbox和radio都是通过相同的name属性值,来标识一个组内的元素。如果我们现在要获取被选中的元素,首先获取改组元素,然后循环判断是节点的checked属性值是否为true即可
  • 创建节点:
    document.createElement(element)
    //参数为要新添的节点标签名,egnewnode=document.createElement("div"); 
    
    document.createTextNode(string)
    //创建一个包含着给定文本的新文本节点,eg:document.createTextNode("hello");
    

    eg:

    var a =document.createElement("span");
    var b =document.createTextNode("cssrain");
    a.appendChild(b);
    
  • 添加节点:
    //添加子节点:
    node.appendChild(newChild) //newChild为生新增的节点.eg: document.body.appendChildNode(o) document.forms[0].appendChildNode(o)
    
    //插入节点
    node.insertBefore(newNode,targetNode)
    node.insertAfter(newNode,targetNode);
  • 修改节点:
    //删除节点
    node.remove()[2] //当某个节点被remove方法删除时,这个节点所包含的所有子节点将同时被删除。
    node.removeChild(node) //eg:document.body.removeChild(node)
    node.removeNode()//IE支持,但FF不支持,推荐用removeChild代替实现
    
    //替换节点
    node.replaceChild(newChild,oldChild) //oldChild节点必须是node元素的一个子节点。
    node.replaceNode() node.swapNode()//只有IE支持replaceNode与swapNode方法,其他浏览器则不支持。
    
  • 复制节点:
    //返回复制节点引用
    node.cloneNode(bool)//bool为布尔值,true / false 是否克隆该节点所有子节点 ,eg:node.cloneNode(true)
  • 获取节点信息:
    .nodeName//只读,返回节点名称,相当于tagName.
    .nodeValue//可读可写,但对元素节点不能写。返回一个字符串,指示这个节点的值。元素节点返回null,属性节点返回属性值,文本节点返回文本。一般只用于设置文本节点的值。
    .nodeType//只读,返回节点类型:1,元素节点;2,属性节点;3,文本节点。 
    
    node.contains() //是否包含某节点,返回boolean值,IE支持,FF不支持contains(),但支持W3C标准compareDocumentPosition() .
    node.hasChildNodes()//是否有子节点,返回boolean值
  • 属性节点:
    setAttribute(key,value)//element.setAttribute(attributeName,attributeValue),setAttribute()方法只能用在属性节点上。
    getAttribute(key)//返回一个给定元素的一个给定属性节点的值

备注:

[1]childNodes兼容性问题说明:

用IE的调试工具会发现IE中把空格解析成“#text”,即IE会把2个标签之间只要有回车或空格的地方解析成空白文本节点,就多了个节点nodeName="#text"。而FF中却不会。

  • 测试代码:

    //节点之间留有空格和回车
    <div id="test1">
    <div>first</div>
    <div>second</div>
    <div>third</div>
    </div>
    
    //除注释外,节点间无空格回车
    <div id="test2"><div>first</div><div>second</div><div>third</div></div>
    
    var val1=document.getElementById("test1").childNodes.length;
    var val2=document.getElementById("test2").childNodes.length;
    alert("val1="+val1+":"+"val2="+val2)
  • 测试结果:

    IE中是val1=7:val2=3
    FF中是val1=3:val2=3

  • 兼容性解决办法:

    在for循环里不妨加上:
    if(childNode.nodeName=="#text") continue;
    或者nodeType == 1。

[2]add(),remove()兼容性问题:

注意的是add,remove方法仅用于area,controlRange,options等集合对象.

<select>
<option value="1">option1</option>
<option value="2">option2</option>
</select>
<script type="text/javascript">
function fnAdd(){//兼容IE,FF,Opera,Chrome
var oOption=document.createElement("option");
document.getElementById("#oList").options.add(oOption);
oOption.text="option3";
oOption.value="3";
}
function fnRemoveChild(){//兼容IE,FF,Opera,Chrome
document.getElementById("#oList")).removeChild(document.getElementById("#oList").lastChild);
}
function fnRemove(){
//兼容IE,FF,Opera,Chrome
document.getElementById("#oList").remove(0);
}
</script>

扩展知识:

innerHTML、outerHTML、innerText、outerText

  • 定义:

    • innerHTML设置或获取标签内的HTML,eg:获取node.innerHTML 设置node.innerHTML="hello"
    • outerHTML设置或获取标签及标签内的HTML
    • innerText设置或获取标签内的文本
    • outerText设置(包括标签)或获取(不包括标签)对象的文本
  • 不同之处:

    innerHTML与outerHTML在设置对象的内容时包含的HTML会被解析,而innerText与outerText则不会。

    在设置时,innerHTML与innerText仅设置标签内的文本,而outerHTML与outerText设置包括标签在内的文本。

  • 注意:

    W3C 只支持innerHTML. 其他都是微软的规定.(outerHTML,outerText,innerText只有微软的IE 好使, 其他浏览器不好用(firefox, mozilla等),必须用其他方法实现)

原文地址:http://www.heiniuhaha.cn/blog/?p=1388


浅谈web标准、可用性、可访问性

14/02/2011风月

前言:大家不难发现,只要是招聘UED相关的岗位,如前端开发工程师、交互设计师、用户研究员甚至视觉设计师,一般都对web标准、可用性和可访问性的理解有要求。那么到底什么是web标准、可用性、可访问性呢?

一、web标准

简单的说,就是HTML、CSS、JavaScript这三者分离。WEB标准不是某一个标准,而是一系列标准的集合。网页主要由三部分组成:结构(Structure)、表现(Presentation)和行为(Behavior)。对应的标准也分三方面:结构化标准语言主要包括XHTML和XML,表现标准语言主要包括CSS,行为标准主要包括对象模型(如 W3C DOM)、ECMAScript等。

web标准的优点:

  • 代码的效率:在HTML文件中使用最精简的代码,而把样式和页面布局信息包含进CSS文件中。则放在服务器上的文件越小,下载文件需要的时间就越短。
  • 易于维护:页面的样式和布局信息保存在单独的CSS文件中,如果你想改变站点的外观时,仅需要在单独的CSS文件中做出更改即可。整站统一css则可带来巨大的便利。
  • 可访问性:上网用户中那些视力受损的人,通过屏幕阅读器使用键盘命令将网页的内容读给他们听。以语义化的HTML(结构和表现相分离的HTML)编写的网页文件,就可以让此类用户更容易导航,且网页文件中的重要信息也更有可能被这些用户找到。
  • 设备兼容性:纯HTML,无附加样式信息,可以针对具有不同特点(如屏幕尺寸等)的设备而被重新格式化,只需要引用一套另外的样式表即可。同时,CSS本身也可以让你为不同的呈现方式和媒体类型(如在屏幕上阅读网页,打印网页,在移动设备上阅读网页等)规定不同的样式表。
  • 网络爬虫/搜索引擎:搜索引擎使用“爬虫”,解析你的网页。语义化的HTML能更准确更快速的被解析,从而知道哪些才是重要的内容,那么你的网页在搜索结果中的排名就会大受影响。

二、可用性、可访问性

可访问性就是对所有人一视同仁,无论他们是否有残障。

网站的用户类型:

  • 身体健康的用户;
  • 盲人或严重视觉障碍者,他们使用屏幕阅读器来听取网站,或者通过点字显示器来感知网页;
  • 近视者,需要将字体大小放大到200%;
  • 患有运动性残疾,因此无法用手操作鼠标,而用点击棒来操作键盘,或通过视线点击器来操作网站的用户;
  • 使用移动设备如常用的手机,或使用跟踪球等不常见的计算机控制设备的用户。

实现可用性、可访问性的方法

  • 逐步强化你的网站功能,同时对支持性进行测试。运用“渐进增强”和“平稳退化”原则开发网站。
  • 允许用户关闭有问题的增强功能。
  • 提供相同内容或功能的替代版本。
  • 就客户端需要支持的技术向你的客户提出建议,并举例说明哪些公司的产品支持这些技术。

可访问性良好网页的特征

  • HTML语义化、结构化:HTML语义结构提供了网页的整体框架,提示他们在文件层级中所处的位置,还有他们可以如何与各种页面元素进行交互,以及在适当的地方对文本内容进行强调,帮助用户获得大量重要信息。如导航菜单例子:
    <ul>
    	<li>Menu Item 1</li>
    	<li>Menu Item 2</li>
    	<li>Menu Item 3</li>
    </ul>
    

    说明:通过将导航菜单构造为列表,就能很容易地让那些使用屏幕阅读器、同时无法看到列表的人知道这是个列表。因为他们的屏幕阅读器会告诉他们这是一张列表。如果你没有使用列表标记,屏幕阅读器就没办法知道这是列表,因此也就不能告诉使用者了。

  • 替代内容:文本可以作为页面内容的通用替代内容,如img标签的alt属性值、a标签的title属性值。
    <a href="http://www.alimama.com" title="淘宝联盟大促销">
    	<img alt="淘宝联盟大促销" src="images/app/sale.jpg"/>
    </a>
    

    说明:文本内容可以很方便地由屏幕阅读器朗读出来,也可以放大或缩小,还可以方便地改变其对比度,或者进行其他许多变形操作。alt 属性包含了对该图片的简短描述,以便无法准确看到该图片的用户(或搜索引擎)使用,title属性负责对链接地址的详细文本描述。

  • HTML定义基本交互:实现tab选项卡搜索功能
    <form action="search.html" method="GET">
    	<fieldset>
    		<legend>Search within:</legend>
    		 <ul>
    			<li><label for="dogs">Dogs</label><input id="dogs" type="radio" name="animal" value="dog" checked></li>
    			<li><label for="cats">Cats</label><input id="cats" type="radio" name="animal" value="cat"></li>
    			<li><label for="fish">Fish</label><input id="fish" type="radio" name="animal" value="fish"></li>
    		</ul>
    	</fieldset>
    	<input type="text" id="searchfield" name="search"/>
    	<input type="submit" value="Search"/>
    </form>
    

    说明:先考虑基本交互(而不是仅仅只加载视觉效果的部分)的话,你就可以简化实现tab搜索效果。现在我们可以只用一个表单来进行所有的搜索,而同时仍然能实现tab选项卡效果(虽然这需要一点样式和脚本)。通过 AJAX 来插入页面内容,那禁用javascript的用户将无法使用。

四个可访问性标准(WCAG 2.0):

  • 可感知:人们可以通过适合自己的媒体来获知网页内容。比如应当让盲人得以收听页面内容。例如,图像应该有文本对应体。
  • 可操作:人们可以与 web 应用程序或内容进行交互。例如,用户应该可以不用鼠标也能与某个网站进行交互,并且可以通过屏幕阅读器来进行导航。
  • 可理解:使用者可以弄懂页面内容和用户界面。例如,正文不应该比它需要的更加复杂,且网站应以可预测的方式来运行。
  • 健壮性:所提供的一切服务都应当不受平台或操作系统的限制。这样就可以避免人们提供一些不太完善的服务,这些服务会因为硬件/软件的限制而导致大多数人都无法使用。例如,不同设备上的浏览器能够一起使用网站,且导航应该是一致的。

说明:网站并不是必须满足全部这些要求,要视网站用户类型而定,但为了实现可访问性,网站应当确保其页面可以用一般的屏幕阅读技术读取。

总结:

可访问性是网站开发质量的一个衡量标准。如果你在开发网站的时候(以及开始开发前)顾及你的使用用户的话,你就能创建可用性、可访问性更好、更符合web标准的网页,并且享受它所带来的一切好处。

参考网页:
opera web标准课程
百度百科 web标准

原文地址:http://www.heiniuhaha.cn/blog/?p=1378


拥抱HTML5,《HTML5设计原理》读后随记

13/01/2011风月

前言:
HTML5和CSS3的时代到来了,新版2011版淘宝网首页已全部使用HTML5,拥抱变化才是王道。为之漫笔翻译的很好,看了一遍后,感觉理解了很多,强烈推荐其他做开发的童鞋尤其前端也来看看。
不仅让我摸清了html4,xhtml1.0, xhtml2.0, html5之间的关系,也理解了为什么会出现HTML5,同时,加紧推进在项目中应用HTML5。


著名的阿西莫夫机器人三大法则:

机器人不得伤害人类,或袖手旁观人类受伤害。
机器人必须服从人类命令,除非命令违反第一法则。
机器人必须自卫,只要不违背第一和第二法则。


xhtml1.0与html4.0相同点:

两个规范的内容是一样的;
词汇表是一样的;
所有的元素是一样;
所有的属性也都是一样的;

xhtml1.0与html4.0唯一不同点:

XHTML 1.0要求使用XML语法(严格的编码风格)
//所有属性都必须使用小写字母;
//所有元素也必须使用小写字母;
//所有属性值都必须加引号;
//你还得记着使用结束标签,记着对img和br要使用自结束标签。

XHTML 1.1与xhtml1.0唯一的变化:

把文档标记为XML文档
//XML的错误处理模型:解析器如果遇到错误,停止解析。
//不能理解XML的浏览器,用户直接看不到这个网页了。

XHTML 2特点(这个规范没有完成):

仍然使用XML错误处理模型,你必须保证以XML文档类型发送文档;
有意不再向后兼容已有的HTML的各个版本,开发人员和浏览器厂商永远不会支持它。

真正广泛地应用的设计原理:

发送时要保守;接收时要开放。
//作为专业人士,在发送文档的时候,我们会尽量保守一些,尽量采用最佳实践,尽量确保文档格式良好。
//但从浏览器的角度说,它们必须以开放的姿态去接收任何文档。


HTML5

1、2004成立了Web Hypertext Applications Technology Working Group(Web超文本应用技术工作组,WHATWG),完全脱离W3C。
2、W3C在2007年组建了HTML5工作组,在WHATWG工作成果的基础上继续开展工作。


HTML5设计原理一:避免不必要的复杂性

一、DOCTYPE的写法:

//HTML 4.01:
<!DOCTYPE html PUBLIC "-//W3C/DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">

//XHTML 1.0:
<!DOCTYPE html PUBLIC "-//W3C/DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

//HTML5:
<!DOCTYPE html>//这种写法会触发浏览器的标准模式。

备注:doctype它不是写给浏览器看的,Doctype是写给验证器看的。让验证器按照该doctype来验证我的文档。

二、指定文档的字符编码的写法:

//HTML 4.01:
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>

//XHTML 1.0:
<?xml version="1.0" encoding="UTF-8" ?>

//HTML5:
<meta charset="utf-8"/>

备注:此简短写法,它不仅适用于最新版本的浏览器,只要是今天还有人在用的浏览器都同样有效。

HTML5其他简洁写法:

<link href="#" rel="stylesheet"/>
//无需再写type="text/css",否则那就是重复自己了

<script></script>
//无需再写使用的脚本语言 type="text/javascript"

HTML5设计原理二:支持已有的内容

<img src="foo" alt="bar" />
<p class="foo">Hello world</p>

<img src="foo" alt="bar">
<p class="foo">Hello world

<IMG SRC="foo" ALT="bar">
<P CLASS="foo">Hello world</p>

<img src=foo alt=bar>
<p class=foo>Hello world</p>

备注:HTML5支持已存在的各种不严谨的写法。
在JavaScript,你可以在每条语句末尾加上分号,但不是必需的,因为JavaScript会自动插入分号……JSlint确实是个非常棒的工具,规范统一JavaScript编码风格,在团队项目非常有用。


HTML5设计原理三:解决现实的问题

给整块内容(含多个块级元素)加个链接

//HTML 4.01 XHTML 1.0:
<h2><a href="/path/to/resource">Headline text</a></h2>
<p><a href="/path/to/resource">Paragraph text.</a></p>

//HTML5:
<a href="/path/to/resource">
<h2>Headline text</h2>
<p>Paragraph text.</p>
</a>

备注:这种写法其实早就已经存在于浏览器中了,但以前这样写是不合乎规范的,现在我们把标准改了,允许你这样写了。


HTML5设计原理四:求真务实

新增语义元素涉及头部(header)、脚部(footer)、分区(section)、文章(article)……

//HTML 4.01 XHTML 1.0:

<div id="header">...</div>
<div id="navigation">...</div>
<div id="main">...</div>
<div id="sidebar">...</div>
<div id="footer">...</div>
</body>

//HTML5:
<body>
<header>...</header>
<nav>...</nav>
<div id="main">...</div>
<aside>...</aside>
<footer>...</footer>
</body>

备注:新元素section、article、aside和nav代表了一种新的内容模型,一种HTML中前所未有的内容模型——给内容分区。
将新元素作为类的替代品更有价值,因为这些元素在一个页面中不止可以使用一次,而是可以使用多次,可嵌套使用。
其中最为通用的section,可以说是与内容最相关的一个。而article则是一种特殊的section。Aside呢,是一种特殊的section。最后,Nav也是一种特殊的section。

//HTML 4.01 XHTML 1.0:
<div class="item">
<h2>...</h2>
<div class="meta">...</div>
<div class="content">
...
</div>
<div class="links">...</div>
</div>

//HTML5:
<section class="item">
<header><h1>...</h1></header>
<footer class="meta">...</footer>
<div class="content">
...
</div>
<nav class="links">...</nav>
</section>

备注:在HTML5中,只要你建立一个新的内容块,不管用section、article、aside、nav,还是别的元素,都可以在其中使用H1,而不必担心这个块里的标题在整个页面中应该排在什么级别;H2、H3,都没有问题。


HTML5设计原理五:平稳退化

渐进增强的另一面就是平稳退化。
使用type属性增强表单:

input type="number"
input type="search"
input type="range"
input type="email"
input type="date"
input type="url"

备注:
现有的浏览器无法理解这些新type值的,但在它们看到自己不理解的type值时,会将type的值解释为text。
HTML5还为输入元素增加了新的属性,比如placeholder(占位符),就是用于在文本框中预先放一些文本。无需JavaScript去实现,太完美了。


HTML5视频对Flash视频(video元素):

<video>
<source src="movie.mp4">
<source src="movie.ogv">
<object data="movie.swf">
<a href="movie.mp4">download</a>
</object>
</video>

备注:两者要兼顾,无论是HTML5,还是Flash:

  1. 如果浏览器支持video元素,也支持H264,没什么好说的,用第一个视频。
  2. 如果浏览器支持video元素,支持Ogg,那么用第二个视频。
  3. 如果浏览器不支持video元素,那么就要试试Flash影片了。
  4. 如果浏览器不支持video元素,也不支持Flash,我还给出了下载链接。

遵循另一个设计原理,即梅特卡夫定律(Metcalfe’s Law):

网络价值同网络用户数量的平方成正比。

HTML5设计原理六:最终用户优先

本质上是一种解决冲突的机制

一旦遇到冲突,最终用户优先,其次是作者,其次是实现者,其次标准制定者,最后才是理论上的完满。

Web的设计原理:

大多数人的意见和运行的代码。

参考:

《HTML5设计原理》

原文地址:http://www.heiniuhaha.cn/blog/?p=1273&preview=true