<?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/"
	>

<channel>
	<title>Miller</title>
	<atom:link href="http://varnow.org/?feed=rss2" rel="self" type="application/rss+xml" />
	<link>http://varnow.org</link>
	<description>记录前端-记录成长</description>
	<pubDate>Sat, 03 Jul 2010 07:14:09 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.7.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>HTML5文件API之图片预览</title>
		<link>http://varnow.org/?p=293</link>
		<comments>http://varnow.org/?p=293#comments</comments>
		<pubDate>Sat, 03 Jul 2010 07:07:56 +0000</pubDate>
		<dc:creator>admin</dc:creator>
		
		<category><![CDATA[XHTML]]></category>

		<category><![CDATA[html5]]></category>

		<guid isPermaLink="false">http://varnow.org/?p=293</guid>
		<description><![CDATA[图片上传在当今的Web应用中是一个非常常用的功能，如果不需要在上传前进行图片预览则可以简单的使用HTML+Javascript来实现，但如果一定要在上传之前提供图片预览功能则需要求助于Flash来实现。不过，随着HTML5 File API的诞生这一状况终于有了改观，本文将介绍如何使用HTML5 File API快速的实现图片预览功能。

浏览器支持情况
本文实现的功能在以下浏览器中经过测试：IE8、Firefox3.6、Chrome6.0、Opera10、Safari4。其中Firefox3.6与Chrome6.0已经实现了该标准（虽然并没有完全遵循标准），其他浏览器均未实现。关于具体的兼容性问题，后文将详细说明。
文件选择与获取
如今最常见的文件选择方式是使用File Input元素，用户通过该元素打开本机文件对话框寻找并选择相应的文件，不过随着HTML5 Drag Drop API的出现又增加了一种新的方式-用户可以直接将本机的文件拖拽到Web页面中。
方式一 &#60;input type=&#8221;file&#8221;&#62;
&#60;input id="fileSel" type="file" onchange="handleFiles(this.files)"&#62;
&#60;script type="text/javascript"&#62;

//获取用户选择的文件

function handleFiles(files){

    //遍历files并处理

}

&#60;/script&#62;
方式二 Drag &#38; Drop
&#60;div id="fileDropRegion"&#62;将文件拖拽到此&#60;/div&#62;
&#60;script type="text/javascript"&#62;

//获取用户选择的文件

var dr = document.getElementById('fileDropRegion');

dr.addEventListener("drop",function(e){
     e.stopPropagation(); 
     e.preventDefault();  

     var dt = e.dataTransfer;  

     //获取文件数组
     var files = dt.files;
     handleFiles(files); 
},false);

function handleFiles(files){

    //遍历files并处理

}

&#60;/script&#62;
文件读入与展示
通过上文中的方法我们获取到了用户选择的文件数组，接下来就该操作其中的每一个文件了，如HTML5 File API描述的那样，每个文件对象应该包含以下属性：
readonly attribute DOMString [...]]]></description>
			<content:encoded><![CDATA[<p>图片上传在当今的Web应用中是一个非常常用的功能，如果不需要在上传前进行图片预览则可以简单的使用HTML+Javascript来实现，但如果一定要在上传之前提供图片预览功能则需要求助于Flash来实现。不过，随着<a href="http://dev.w3.org/2006/webapi/FileAPI" target="_blank">HTML5 File API</a>的诞生这一状况终于有了改观，本文将介绍如何使用HTML5 File API快速的实现图片预览功能。</p>
<p><span id="more-293"></span></p>
<h4>浏览器支持情况</h4>
<p>本文实现的功能在以下浏览器中经过测试：IE8、Firefox3.6、Chrome6.0、Opera10、Safari4。其中Firefox3.6与Chrome6.0已经实现了该标准（虽然并没有完全遵循标准），其他浏览器均未实现。关于具体的兼容性问题，后文将详细说明。</p>
<h4>文件选择与获取</h4>
<p>如今最常见的文件选择方式是使用File Input元素，用户通过该元素打开本机文件对话框寻找并选择相应的文件，不过随着HTML5 Drag Drop API的出现又增加了一种新的方式-用户可以直接将本机的文件拖拽到Web页面中。</p>
<p>方式一 &lt;input type=&#8221;file&#8221;&gt;</p>
<pre name="code" class="html">&lt;input id="fileSel" type="file" onchange="handleFiles(this.files)"&gt;
&lt;script type="text/javascript"&gt;

//获取用户选择的文件

function handleFiles(files){

    //遍历files并处理

}

&lt;/script&gt;</pre>
<p>方式二 Drag &amp; Drop</p>
<pre name="code" class="html">&lt;div id="fileDropRegion"&gt;将文件拖拽到此&lt;/div&gt;
&lt;script type="text/javascript"&gt;

//获取用户选择的文件

var dr = document.getElementById('fileDropRegion');

dr.addEventListener("drop",function(e){
     e.stopPropagation(); 
     e.preventDefault();  

     var dt = e.dataTransfer;  

     //获取文件数组
     var files = dt.files;
     handleFiles(files); 
},false);

function handleFiles(files){

    //遍历files并处理

}

&lt;/script&gt;</pre>
<h4>文件读入与展示</h4>
<p>通过上文中的方法我们获取到了用户选择的文件数组，接下来就该操作其中的每一个文件了，如HTML5 File API描述的那样，每个文件对象应该包含以下属性：</p>
<blockquote><p>readonly attribute DOMString name;           //The name of the file.</p>
<p>readonly attribute unsigned long long size; //Represents the size of the Blob object in bytes.</p>
<p>readonly attribute DOMString type;            //The media type of the Blob</p>
<p>readonly attribute DOMString url;              //The URL representing the Blob object.</p></blockquote>
<p>如果是上传图片则可以通过type属性来进行图片格式过滤，并可以通过size属性来控制图片大小。对于这些属性，Firefox与Chrome的实现情况是出奇的一致，均只支持name、size和type属性。</p>
<pre name="code" class="javascript">function handleFiles(files){

    //遍历files并处理

    for (var i = 0; i &lt; files.length; i++) { 
    var file = files[i]; 
    var imageType = /image.*/;  

        //通过type属性进行图片格式过滤
    if (!file.type.match(imageType)) { 
      continue; 
    }  

        //读入文件

    } 

}

&lt;/script&gt;</pre>
<p>此外，HTML5 File API中还定义了一个BlobReader，通过它可以使用各种不同的方式读入文件并且可以监听和处理读取文件过程中的各个状态。不过在Firefox与Chrome的实现中，并没有这样一个BlobReader，取而代之的是FileReader，拥有与BlobReader相同的方法和事件。</p>
<blockquote><p>// async read methods</p>
<p>void readAsArrayBuffer(in Blob blob);</p>
<p>void readAsBinaryString(in Blob blob);</p>
<p>void readAsText(in Blob blob, [Optional] in DOMString encoding);</p>
<p>void readAsDataURL(in Blob blob);</p>
<p>// event handler attributes</p>
<p>attribute Function onloadstart;</p>
<p>attribute Function onprogress;</p>
<p>attribute Function onload;</p>
<p>attribute Function onabort;</p>
<p>attribute Function onerror;</p>
<p>attribute Function onloadend;</p></blockquote>
<p>以上4个读取文件的方式均是异步的，需要注册监听对应的事件来处理文件，而与图片预览相关的方法是readAsDataURL，该方法会将图片文件内容转换成dataURI的格式，而该格式是可以直接在浏览器中显示的。</p>
<pre name="code" class="javascript">function handleFiles(files){

    //遍历files并处理

    for (var i = 0; i &lt; files.length; i++) { 
    var file = files[i]; 
    var imageType = /image.*/;  

        //通过type属性进行图片格式过滤
    if (!file.type.match(imageType)) { 
      continue; 
    }  

        //读入文件

    var reader = new FileReader(); 
    reader.onload = function(e){

            //e.target.result返回的即是图片的dataURI格式的内容

            var imgData = e.target.result,

                img = document.createElement('img');

            img.src = imgData;

            //展示img

    }
    reader.readAsDataURL(file); 
    }  

}

&lt;/script&gt;</pre>
<p>通过以上几步便简单的实现了图片的上传前的预览功能，具体的效果请在Firefox或Chrome下<a href="http://varnow.org/pages/html5/file_api/image_preview.html" target="_blank">打开此页面</a>。</p>
<h4>参考资料</h4>
<p><a href="http://dev.w3.org/2006/webapi/FileAPI" target="_blank">HTML5 File API</a></p>
<p><a href="https://developer.mozilla.org/en/using_files_from_web_applications" target="_blank">Using files from web applications</a></p>
]]></content:encoded>
			<wfw:commentRss>http://varnow.org/?feed=rss2&amp;p=293</wfw:commentRss>
		</item>
		<item>
		<title>DOM Storage全解析</title>
		<link>http://varnow.org/?p=288</link>
		<comments>http://varnow.org/?p=288#comments</comments>
		<pubDate>Sun, 16 May 2010 06:31:49 +0000</pubDate>
		<dc:creator>admin</dc:creator>
		
		<category><![CDATA[DOM]]></category>

		<category><![CDATA[Javascript]]></category>

		<category><![CDATA[html5]]></category>

		<guid isPermaLink="false">http://varnow.org/?p=288</guid>
		<description><![CDATA[Web应用的发展，使得客户端存储使用得也越来越多，而实现客户端存储的方式则是多种多样。最简单而且兼容性最佳的方案是Cookie，但是作为真正的客户端存储，Cookie则存在很多致命伤。此外，在IE6及以上版本中还可以使用userData Behavior、在Firefox下可以使用globalStorage、在有Flash插件的环境中可以使用Flash Local Storage，但是这几种方式都存在兼容性方面的局限性，因此真正使用起来并不理想。针对以上情况，HTML5中给出了更加理想的解决方案：假如你需要存储复杂的数据则可以使用Web Database，可以像客户端程序一样使用SQL（不过Web Database标准当前正陷于僵局之中，而且目前已经实现的浏览器很有限）；假如你需要存储的只是简单的用key/value对即可解决的数据则可以使用Web Storage。 本文主要从各个方面介绍一下Web Storage的具体情况。
 
sessionStorage与localStorage
Web Storage实际上由两部分组成：sessionStorage与localStorage。
sessionStorage用于本地存储一个会话（session）中的数据，这些数据只有在同一个会话中的页面才能访问并且当会话结束后数据也随之销毁。因此sessionStorage不是一种持久化的本地存储，仅仅是会话级别的存储。
localStorage用于持久化的本地存储，除非主动删除数据，否则数据是永远不会过期的。
为什么选择Web Storage而不是Cookie？
与Cookie相比，Web Storage存在不少的优势，概括为以下几点：
1. 存储空间更大：IE8下每个独立的存储空间为10M，其他浏览器实现略有不同，但都比Cookie要大很多。
2. 存储内容不会发送到服务器：当设置了Cookie后，Cookie的内容会随着请求一并发送的服务器，这对于本地存储的数据是一种带宽浪费。而Web Storage中的数据则仅仅是存在本地，不会与服务器发生任何交互。
3. 更多丰富易用的接口：Web Storage提供了一套更为丰富的接口，使得数据操作更为简便。
4. 独立的存储空间：每个域（包括子域）有独立的存储空间，各个存储空间是完全独立的，因此不会造成数据混乱。
兼容性如何？
接下来的各种测试是在以下浏览器中进行的：IE8、Firefox3.6、Chrome5、Safari4、Opera10，事实证明各个浏览器在API方面的实现基本上一致，存在一定的兼容性问题，但不影响正常的使用。
sessionStorage测试
本节主要针对sessionStorage的一些特性进行了测试，测试的重点在于各个浏览器对于session的定义以及跨域情况。测试方法很简单：打开页面A，在页面A中写入当前的session数据，然后通过页面A中的链接或按钮使用不同的方式进入下页面B，如果页面B中能够访问到页面A中的数据则说明浏览器将当前情况的页面A、B视为同一个session。测试的具体结果如表1：
表1 sessionStorage兼容性测试



&#160;
原窗口
target=&#34;_blank&#34;
window.open
ctrl + click
跨域访问


IE8
是
是
是
是
否


Firefox3.6
是
是
是
否（null)
否


Chrome5
是
是
是
否（undefined)
否


Safari4
是
否
是
否（undefined)
否


Opera10
是
否
否
否（undefined)
否



从表1中可以看出，处于安全性考虑所有浏览器下session数据都是不允许跨域访问的，包括跨子域也是不允许的。其他方面主流浏览器中的实现较为一致。
API测试
方法包括以下几个：
setItem(key,value)、removeItem(key)、getItem(key)、clear()、key(index)；
属性包括length、remainingSpace(非标准)。不过存储数据时可以简单的使用localStorage.key=value的方式。
测试地址为：http://varnow.org/pages/html5/web_storage/local/localStorage.html
测试结果另人满意，标准中定义的接口在各个浏览器中都已实现，此外IE8下新增了一个非标准的remainingSpace属性，用于获取存储空间中剩余的空间。结果如表2：
表2 API测试



&#160;
setItem
removeItem
getItem
clear
key
length
remainingSpace


IE8
 是
是
是
是
是
是
是


Firefox3.6
是
是
是
是
是
是
否


Chrome5
是
是
是
是
是
是
否


Safari4
是
是
是
是
是
是
否


Opera10
是
是
是
是
是
是
否



此外关于setItem(key,value)方法中的value类型，理论上可以是任意类型，不过实际上浏览器会调用value的toString方法来获取其字符串值并存储到本地，因此如果是自定义的类型则需要自己定义有意义的toString方法。
事件
标准的事件为onstorage，当存储空间中的数据发生变化时触发。此外，IE8中新增了一个onstoragecommit事件，当数据写入的时候触发。onstorage事件中的事件对象应该支持以下属性：
The key attribute represents the key being changed.      The oldValue attribute represents the old value of the key being changed.       The [...]]]></description>
			<content:encoded><![CDATA[<p>Web应用的发展，使得客户端存储使用得也越来越多，而实现客户端存储的方式则是多种多样。最简单而且兼容性最佳的方案是Cookie，但是作为真正的客户端存储，Cookie则存在很多致命伤。此外，在IE6及以上版本中还可以使用<a href="http://msdn.microsoft.com/en-us/library/ms531424(v=VS.85).aspx" target="_blank">userData Behavior</a>、在Firefox下可以使用<a href="https://developer.mozilla.org/en/dom/storage" target="_blank">globalStorage</a>、在有Flash插件的环境中可以使用<a href="http://www.macromedia.com/support/documentation/en/flashplayer/help/help02.html">Flash Local Storage</a>，但是这几种方式都存在兼容性方面的局限性，因此真正使用起来并不理想。针对以上情况，HTML5中给出了更加理想的解决方案：假如你需要存储复杂的数据则可以使用<a href="http://dev.w3.org/html5/webdatabase/" target="_blank">Web Database</a>，可以像客户端程序一样使用SQL（不过Web Database标准当前正陷于僵局之中，而且目前已经实现的浏览器很有限）；假如你需要存储的只是简单的用key/value对即可解决的数据则可以使用<a href="http://dev.w3.org/html5/webstorage/" target="_blank">Web Storage</a>。 本文主要从各个方面介绍一下Web Storage的具体情况。</p>
<p> <span id="more-288"></span><br />
<h4>sessionStorage与localStorage</h4>
<p>Web Storage实际上由两部分组成：sessionStorage与localStorage。</p>
<p>sessionStorage用于本地存储一个会话（session）中的数据，这些数据只有在同一个会话中的页面才能访问并且当会话结束后数据也随之销毁。因此sessionStorage不是一种持久化的本地存储，仅仅是会话级别的存储。</p>
<p>localStorage用于持久化的本地存储，除非主动删除数据，否则数据是永远不会过期的。</p>
<h4>为什么选择Web Storage而不是Cookie？</h4>
<p>与Cookie相比，Web Storage存在不少的优势，概括为以下几点：</p>
<p>1. 存储空间更大：IE8下每个独立的存储空间为10M，其他浏览器实现略有不同，但都比Cookie要大很多。</p>
<p>2. 存储内容不会发送到服务器：当设置了Cookie后，Cookie的内容会随着请求一并发送的服务器，这对于本地存储的数据是一种带宽浪费。而Web Storage中的数据则仅仅是存在本地，不会与服务器发生任何交互。</p>
<p>3. 更多丰富易用的接口：Web Storage提供了一套更为丰富的接口，使得数据操作更为简便。</p>
<p>4. 独立的存储空间：每个域（包括子域）有独立的存储空间，各个存储空间是完全独立的，因此不会造成数据混乱。</p>
<h4>兼容性如何？</h4>
<p>接下来的各种测试是在以下浏览器中进行的：IE8、Firefox3.6、Chrome5、Safari4、Opera10，事实证明各个浏览器在API方面的实现基本上一致，存在一定的兼容性问题，但不影响正常的使用。</p>
<h4>sessionStorage测试</h4>
<p>本节主要针对sessionStorage的一些特性进行了测试，测试的重点在于各个浏览器对于session的定义以及跨域情况。测试方法很简单：打开<a href="http://varnow.org/pages/html5/web_storage/session/sessionStorage_A.html" target="_blank">页面A</a>，在页面A中写入当前的session数据，然后通过页面A中的链接或按钮使用不同的方式进入下页面B，如果页面B中能够访问到页面A中的数据则说明浏览器将当前情况的页面A、B视为同一个session。测试的具体结果如表1：</p>
<p align="center">表1 sessionStorage兼容性测试</p>
<table border="0" cellspacing="0" cellpadding="2" width="600">
<tbody>
<tr>
<td valign="top" width="60">&#160;</td>
<td valign="top" width="140">原窗口</td>
<td valign="top" width="100">target=&quot;_blank&quot;</td>
<td valign="top" width="100">window.open</td>
<td valign="top" width="100">ctrl + click</td>
<td valign="top" width="100">跨域访问</td>
</tr>
<tr>
<td valign="top" width="60">IE8</td>
<td valign="top" width="140">是</td>
<td valign="top" width="100">是</td>
<td valign="top" width="100">是</td>
<td valign="top" width="100">是</td>
<td valign="top" width="100">否</td>
</tr>
<tr>
<td valign="top" width="60">Firefox3.6</td>
<td valign="top" width="140">是</td>
<td valign="top" width="100">是</td>
<td valign="top" width="100">是</td>
<td valign="top" width="100">否（null)</td>
<td valign="top" width="100">否</td>
</tr>
<tr>
<td valign="top" width="60">Chrome5</td>
<td valign="top" width="140">是</td>
<td valign="top" width="100">是</td>
<td valign="top" width="100">是</td>
<td valign="top" width="100">否（undefined)</td>
<td valign="top" width="100">否</td>
</tr>
<tr>
<td valign="top" width="60">Safari4</td>
<td valign="top" width="140">是</td>
<td valign="top" width="100">否</td>
<td valign="top" width="100">是</td>
<td valign="top" width="100">否（undefined)</td>
<td valign="top" width="100">否</td>
</tr>
<tr>
<td valign="top" width="60">Opera10</td>
<td valign="top" width="140">是</td>
<td valign="top" width="100">否</td>
<td valign="top" width="100">否</td>
<td valign="top" width="100">否（undefined)</td>
<td valign="top" width="100">否</td>
</tr>
</tbody>
</table>
<p>从表1中可以看出，处于安全性考虑所有浏览器下session数据都是不允许跨域访问的，包括跨子域也是不允许的。其他方面主流浏览器中的实现较为一致。</p>
<h4>API测试</h4>
<p>方法包括以下几个：</p>
<p>setItem(key,value)、removeItem(key)、getItem(key)、clear()、key(index)；</p>
<p>属性包括length、remainingSpace(非标准)。不过存储数据时可以简单的使用localStorage.key=value的方式。</p>
<p>测试地址为：<a title="http://varnow.org/pages/html5/web_storage/local/localStorage.html" href="http://varnow.org/pages/html5/web_storage/local/localStorage.html">http://varnow.org/pages/html5/web_storage/local/localStorage.html</a></p>
<p>测试结果另人满意，标准中定义的接口在各个浏览器中都已实现，此外IE8下新增了一个非标准的remainingSpace属性，用于获取存储空间中剩余的空间。结果如表2：</p>
<p align="center">表2 API测试</p>
<table border="0" cellspacing="0" cellpadding="2" width="600">
<tbody>
<tr>
<td valign="top" width="48">&#160;</td>
<td valign="top" width="60">setItem</td>
<td valign="top" width="74">removeItem</td>
<td valign="top" width="65">getItem</td>
<td valign="top" width="57">clear</td>
<td valign="top" width="70">key</td>
<td valign="top" width="81">length</td>
<td valign="top" width="145">remainingSpace</td>
</tr>
<tr>
<td valign="top" width="48">IE8</td>
<td valign="top" width="60"> 是</td>
<td valign="top" width="74">是</td>
<td valign="top" width="65">是</td>
<td valign="top" width="57">是</td>
<td valign="top" width="70">是</td>
<td valign="top" width="81">是</td>
<td valign="top" width="145">是</td>
</tr>
<tr>
<td valign="top" width="48">Firefox3.6</td>
<td valign="top" width="60">是</td>
<td valign="top" width="74">是</td>
<td valign="top" width="65">是</td>
<td valign="top" width="57">是</td>
<td valign="top" width="70">是</td>
<td valign="top" width="81">是</td>
<td valign="top" width="145">否</td>
</tr>
<tr>
<td valign="top" width="48">Chrome5</td>
<td valign="top" width="60">是</td>
<td valign="top" width="74">是</td>
<td valign="top" width="65">是</td>
<td valign="top" width="57">是</td>
<td valign="top" width="70">是</td>
<td valign="top" width="81">是</td>
<td valign="top" width="145">否</td>
</tr>
<tr>
<td valign="top" width="48">Safari4</td>
<td valign="top" width="60">是</td>
<td valign="top" width="74">是</td>
<td valign="top" width="65">是</td>
<td valign="top" width="57">是</td>
<td valign="top" width="70">是</td>
<td valign="top" width="81">是</td>
<td valign="top" width="145">否</td>
</tr>
<tr>
<td valign="top" width="48">Opera10</td>
<td valign="top" width="60">是</td>
<td valign="top" width="74">是</td>
<td valign="top" width="65">是</td>
<td valign="top" width="57">是</td>
<td valign="top" width="70">是</td>
<td valign="top" width="81">是</td>
<td valign="top" width="145">否</td>
</tr>
</tbody>
</table>
<p>此外关于setItem(key,value)方法中的value类型，理论上可以是任意类型，不过实际上浏览器会调用value的toString方法来获取其字符串值并存储到本地，因此如果是自定义的类型则需要自己定义有意义的toString方法。</p>
<h4>事件</h4>
<p>标准的事件为onstorage，当存储空间中的数据发生变化时触发。此外，IE8中新增了一个onstoragecommit事件，当数据写入的时候触发。onstorage事件中的事件对象应该支持以下属性：</p>
<blockquote><p>The key attribute represents the key being changed.      <br />The oldValue attribute represents the old value of the key being changed.       <br />The newValue attribute represents the new value of the key being changed.       <br />The url attribute represents the address of the document whose key changed.       <br />The storageArea attribute represents the Storage object that was affected. </p>
</blockquote>
<p>对于这一标准的实现，webkit内核的浏览器（Chrome、Safari）以及Opera是完全遵循标准的，IE8则只实现了url，Firefox下则均未实现。</p>
<p>测试地址为：<a href="http://varnow.org/pages/html5/web_storage/local/event.html">http://varnow.org/pages/html5/web_storage/local/event.html</a></p>
<p>具体结果见表3。</p>
<p align="center">表3 onStorage事件对象属性测试</p>
<table border="0" cellspacing="0" cellpadding="2" width="600">
<tbody>
<tr>
<td valign="top" width="60">&#160;</td>
<td valign="top" width="140">key</td>
<td valign="top" width="100">oldValue</td>
<td valign="top" width="100">newValue</td>
<td valign="top" width="100">url</td>
<td valign="top" width="100">storageArea</td>
</tr>
<tr>
<td valign="top" width="60">IE8</td>
<td valign="top" width="140">无</td>
<td valign="top" width="100">无</td>
<td valign="top" width="100">无</td>
<td valign="top" width="100">有</td>
<td valign="top" width="100">无</td>
</tr>
<tr>
<td valign="top" width="60">Firefox3.6</td>
<td valign="top" width="140">无</td>
<td valign="top" width="100">无</td>
<td valign="top" width="100">无</td>
<td valign="top" width="100">无</td>
<td valign="top" width="100">无</td>
</tr>
<tr>
<td valign="top" width="60">Chrome5</td>
<td valign="top" width="140">有</td>
<td valign="top" width="100">有</td>
<td valign="top" width="100">有</td>
<td valign="top" width="100">有</td>
<td valign="top" width="100">有</td>
</tr>
<tr>
<td valign="top" width="60">Safari4</td>
<td valign="top" width="140">有</td>
<td valign="top" width="100">有</td>
<td valign="top" width="100">有</td>
<td valign="top" width="100">有</td>
<td valign="top" width="100">有</td>
</tr>
<tr>
<td valign="top" width="60">Opera10</td>
<td valign="top" width="140">有</td>
<td valign="top" width="100">有</td>
<td valign="top" width="100">有</td>
<td valign="top" width="100">有</td>
<td valign="top" width="100">有</td>
</tr>
</tbody>
</table>
<p>此外，不同的浏览器事件注册的方式以及对象也不一致，具体如表4。</p>
<p align="center">表4 onStorage事件注册对象</p>
<table border="0" cellspacing="0" cellpadding="2" width="600">
<tbody>
<tr>
<td valign="top" width="67">&#160;</td>
<td valign="top" width="111">事件注册对象</td>
<td valign="top" width="422">备注</td>
</tr>
<tr>
<td valign="top" width="67">IE8</td>
<td valign="top" width="111">document</td>
<td valign="top" width="422">&#160;</td>
</tr>
<tr>
<td valign="top" width="67">Firefox3.6</td>
<td valign="top" width="111">document</td>
<td valign="top" width="422">必须使用document.addEventListener注册，否则无效。</td>
</tr>
<tr>
<td valign="top" width="67">Chrome5</td>
<td valign="top" width="111">window</td>
<td valign="top" width="422">&#160;</td>
</tr>
<tr>
<td valign="top" width="67">Safari4</td>
<td valign="top" width="111">body</td>
<td valign="top" width="422">&#160;</td>
</tr>
<tr>
<td valign="top" width="67">Opera10</td>
<td valign="top" width="111">window</td>
<td valign="top" width="422">&#160;</td>
</tr>
</tbody>
</table>
<h4>&#160;</h4>
<h4>缺陷与不足</h4>
<p>Web Storage的缺陷主要集中在其安全性方面，具体体现在以下两点：</p>
<p>1. 浏览器会为每个域分配独立的存储空间，即脚本在域A中是无法访问到域B中的存储空间的，但是浏览器却不会检查脚本所在的域与当前域是否相同。即在域B中嵌入域A中的脚本依然可以访问域B中的数据。测试地址：<a title="http://varnow.org/pages/html5/web_storage/local/corss_domain_js_access.html" href="http://varnow.org/pages/html5/web_storage/local/corss_domain_js_access.html">http://varnow.org/pages/html5/web_storage/local/corss_domain_js_access.html</a></p>
<p>2. 存储在本地的数据未加密而且永远不会过期，极易造成隐私泄漏。也许需要像保存密码一样询问用户是在用私人电脑还是公共电脑来决定是否将数据保存在本地。</p>
<h4>跨页面通讯示例</h4>
<p>示例地址：<a href="http://varnow.org/pages/html5/web_storage/app/play.html">http://varnow.org/pages/html5/web_storage/app/play.html</a></p>
<p align="center"><a href="http://varnow.org/wordpress/wp-content/uploads/2010/05/ndimension.jpg" rel="lightbox[288]"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="N-Dimension" border="0" alt="N-Dimension" src="http://varnow.org/wordpress/wp-content/uploads/2010/05/ndimension-thumb.jpg" width="244" height="235" /></a> </p>
<p>玩法很简单：选择 打开多个窗口，页面会自动打开并定位4个新窗口，小球会在多个窗口间运动，在小球运动的过程中可以拖动窗口或者打开新的窗口来控制小球进入新的区域。</p>
<p>实现原理：每个新打开的窗口会将自己的坐标以及大小写入localStorage，小球在运动到窗口边界时会检测小球将要进入的下一个窗口，如果找到该窗口则将小球坐标写入并通知新窗口接收小球。</p>
]]></content:encoded>
			<wfw:commentRss>http://varnow.org/?feed=rss2&amp;p=288</wfw:commentRss>
		</item>
		<item>
		<title>HTML5中的FORM2.0</title>
		<link>http://varnow.org/?p=287</link>
		<comments>http://varnow.org/?p=287#comments</comments>
		<pubDate>Thu, 18 Mar 2010 04:27:06 +0000</pubDate>
		<dc:creator>admin</dc:creator>
		
		<category><![CDATA[XHTML]]></category>

		<category><![CDATA[html5]]></category>

		<guid isPermaLink="false">http://varnow.org/?p=287</guid>
		<description><![CDATA[FORM在HTML5中的变化算是比较大的，一方面体现在新增了大量的2.0时代的控件，另一方面则是整个FORM结构的变化。与新增的控件相比，新的FORM结构一样为将来的开发工作提供了很大的便利性。
 
一、2.0的FORM结构
&#160;&#160;&#160; 1. 可以随便放置的表单控件
&#160;&#160;&#160;&#160;&#160;&#160;&#160; 在HTML4的表单实现中，如果不借助脚本则必须将表单控件放置在&#60;form&#62;&#60;/form&#62;标签之中才能顺利的提交到服务器，这就要求将提交到同一服务器的数据必须集中到一个DOM块中，这在form元素和表单控件较多的情况下对设计以及实现带来一定程度的限制。例如在某个注册模块中，有一部分信息需要提交到地址A，而另一部分则需要提交到地址B，然而在展现上这些控件又是混在一块的。这一场景在HTML4中处理起来是比较麻烦的，但是在HTML5中则可以轻松处理，因为在HTML5中，所有的表单控件都增加了一个新属性-form,以元素ID为值，表示该控件属于某个元素。通过这个属性则彻底突破了必须将控件写在&#60;form&#62;&#60;/form&#62;之中的限制。例如：


&#60;form id=&#34;testform&#34; action=&#34;serve.php&#34;&#62;

    &#60;button type=&#34;submit&#34;&#62;Submit&#60;/button&#62;

&#60;/form&#62;

&#60;input form=&#34;testform&#34; name=&#34;var1&#34; value=&#34;val1&#34;&#62;

在提交表单testform时会将var1提交到服务器。不过，目前仅有Opera10支持这一特性。
&#160;&#160;&#160; 2. 一个表单，多处提交
&#160;&#160;&#160;&#160;&#160;&#160;&#160; 在HTML4中，一个form指定了action后便只能将&#60;form&#62;&#60;/form&#62;中的表单元素提交到一个地址。而在HTML5中则给所有的提交按钮，诸如&#60;input type=&#34;submit&#34;&#62;、&#60;input type=&#34;image&#34;&#62;、&#60;button type=&#34;submit&#34;&#62;都增加了form*属性，具体的如下：
&#160;&#160;&#160;&#160;&#160;&#160;&#160; formaction - 指定表单提交地址，如果指定则覆盖&#60;form&#62;中的action;
&#160;&#160;&#160;&#160;&#160;&#160;&#160; formenctype - 如果指定则覆盖&#60;form&#62;中的enctype; 
&#160;&#160;&#160;&#160;&#160;&#160;&#160; formmethod - 如果指定则覆盖&#60;form&#62;中的method;
&#160;&#160;&#160;&#160;&#160;&#160;&#160; formtarget - 如果指定则覆盖&#60;form&#62;中的target;
&#160;&#160;&#160;&#160;&#160;&#160;&#160; formnovalidate - 如果指定则覆盖&#60;form&#62;中的novalidate(新增form属性，表示不对表单进行有效性检查);
&#160;&#160;&#160;&#160;&#160;&#160;&#160; 有了这些属性使得一个表单中的数据可以根据提交按钮的不同提交到不同的地址，例如：


&#60;form id=&#34;testform&#34; action=&#34;serve.php&#34;&#62;

    &#60;input type=&#34;submit&#34; name=&#34;s1&#34; value=&#34;v1&#34; formaction=&#34;s1.php&#34;&#62;Submit to S1

    &#60;input type=&#34;submit&#34; [...]]]></description>
			<content:encoded><![CDATA[<p>FORM在HTML5中的变化算是比较大的，一方面体现在新增了大量的2.0时代的控件，另一方面则是整个FORM结构的变化。与新增的控件相比，新的FORM结构一样为将来的开发工作提供了很大的便利性。</p>
<p> <span id="more-287"></span><br />
<h4>一、2.0的FORM结构</h4>
<p>&#160;&#160;&#160; 1. 可以随便放置的表单控件</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; 在HTML4的表单实现中，如果不借助脚本则必须将表单控件放置在&lt;form&gt;&lt;/form&gt;标签之中才能顺利的提交到服务器，这就要求将提交到同一服务器的数据必须集中到一个DOM块中，这在form元素和表单控件较多的情况下对设计以及实现带来一定程度的限制。例如在某个注册模块中，有一部分信息需要提交到地址A，而另一部分则需要提交到地址B，然而在展现上这些控件又是混在一块的。这一场景在HTML4中处理起来是比较麻烦的，但是在HTML5中则可以轻松处理，因为在HTML5中，所有的表单控件都增加了一个新属性-<em>form</em>,以元素ID为值，表示该控件属于某个元素。通过这个属性则彻底突破了必须将控件写在&lt;form&gt;&lt;/form&gt;之中的限制。例如：</p>
<pre class="html" name="code">

&lt;form id=&quot;testform&quot; action=&quot;serve.php&quot;&gt;

    &lt;button type=&quot;submit&quot;&gt;Submit&lt;/button&gt;

&lt;/form&gt;

&lt;input form=&quot;testform&quot; name=&quot;var1&quot; value=&quot;val1&quot;&gt;
</pre>
<p>在提交表单testform时会将var1提交到服务器。不过，目前仅有Opera10支持这一特性。</p>
<p>&#160;&#160;&#160; 2. 一个表单，多处提交</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; 在HTML4中，一个form指定了action后便只能将&lt;form&gt;&lt;/form&gt;中的表单元素提交到一个地址。而在HTML5中则给所有的提交按钮，诸如&lt;input type=&quot;submit&quot;&gt;、&lt;input type=&quot;image&quot;&gt;、&lt;button type=&quot;submit&quot;&gt;都增加了form*属性，具体的如下：</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; formaction - 指定表单提交地址，如果指定则覆盖&lt;form&gt;中的action;</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; formenctype - 如果指定则覆盖&lt;form&gt;中的enctype; </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; formmethod - 如果指定则覆盖&lt;form&gt;中的method;</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; formtarget - 如果指定则覆盖&lt;form&gt;中的target;</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; formnovalidate - 如果指定则覆盖&lt;form&gt;中的novalidate(新增form属性，表示不对表单进行有效性检查);</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; 有了这些属性使得一个表单中的数据可以根据提交按钮的不同提交到不同的地址，例如：</p>
<pre class="html" name="code">

&lt;form id=&quot;testform&quot; action=&quot;serve.php&quot;&gt;

    &lt;input type=&quot;submit&quot; name=&quot;s1&quot; value=&quot;v1&quot; formaction=&quot;s1.php&quot;&gt;Submit to S1

    &lt;input type=&quot;submit&quot; name=&quot;s2&quot; value=&quot;v2&quot; formaction=&quot;s2.php&quot;&gt;Submit to S2

&lt;/form&gt;

&lt;input form=&quot;testform&quot; name=&quot;var1&quot; value=&quot;val1&quot;&gt;

&lt;input form=&quot;testform&quot; name=&quot;var2&quot; value=&quot;val2&quot;&gt;
</pre>
<p>与第一点相比，这一特性的浏览器支持则更糟糕，目前尚无浏览器支持。测试的浏览器包括：IE8、IE9、Firefox3.6、Chrome5.0、Opera10、Safari4。</p>
<h4>二、2.0的新控件新属性</h4>
<p>&#160;&#160;&#160; 1.新控件</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; FORM2.0的新控件很大一部分是对&lt;input&gt;的type属性进行了扩展，新的type包括：</p>
<table style="margin: auto" border="0" cellspacing="0" cellpadding="2" width="565">
<tbody>
<tr>
<td style="text-align: center; background-color: #eee" valign="top" width="100">TYPE</td>
<td style="text-align: center; background-color: #eee" valign="top" width="166">描述</td>
<td style="text-align: center; background-color: #eee" valign="top" width="297">浏览器支持</td>
</tr>
<tr>
<td valign="top" width="100">search</td>
<td valign="top" width="166">检索类型，如：<a href="http://varnow.org/wordpress/wp-content/uploads/2010/03/image4.png" rel="lightbox[287]"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://varnow.org/wordpress/wp-content/uploads/2010/03/image-thumb4.png" width="191" height="99" /></a> </td>
<td valign="top" width="297">Opera10、Safari4、Chrome5</td>
</tr>
<tr>
<td valign="top" width="100">tel</td>
<td valign="top" width="166">电话号码</td>
<td valign="top" width="297">NONE</td>
</tr>
<tr>
<td valign="top" width="100">url</td>
<td valign="top" width="166">绝对URL，有效性验证，如：<br />
        <br /><a href="http://varnow.org/wordpress/wp-content/uploads/2010/03/image5.png" rel="lightbox[287]"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://varnow.org/wordpress/wp-content/uploads/2010/03/image-thumb5.png" width="203" height="68" /></a> </td>
<td valign="top" width="297">Opera10</td>
</tr>
<tr>
<td valign="top" width="100">email</td>
<td valign="top" width="166"><a href="http://varnow.org/wordpress/wp-content/uploads/2010/03/image6.png" rel="lightbox[287]"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://varnow.org/wordpress/wp-content/uploads/2010/03/image-thumb6.png" width="244" height="30" /></a> </td>
<td valign="top" width="297">Opera10</td>
</tr>
<tr>
<td valign="top" width="100">date &amp;<br />
        <br />datetime &amp; </p>
<p>month &amp; </p>
<p>week &amp; </p>
<p>time &amp; </p>
<p>datetime-local</td>
<td valign="top" width="166"><a href="http://varnow.org/wordpress/wp-content/uploads/2010/03/image7.png" rel="lightbox[287]"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://varnow.org/wordpress/wp-content/uploads/2010/03/image-thumb7.png" width="244" height="147" /></a> </td>
<td valign="top" width="297">Opera10</td>
</tr>
<tr>
<td valign="top" width="100">number</td>
<td valign="top" width="166"><a href="http://varnow.org/wordpress/wp-content/uploads/2010/03/image8.png" rel="lightbox[287]"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://varnow.org/wordpress/wp-content/uploads/2010/03/image-thumb8.png" width="165" height="32" /></a> </td>
<td valign="top" width="297">Opera10</td>
</tr>
<tr>
<td valign="top" width="100">range</td>
<td valign="top" width="166"><a href="http://varnow.org/wordpress/wp-content/uploads/2010/03/image9.png" rel="lightbox[287]"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://varnow.org/wordpress/wp-content/uploads/2010/03/image-thumb9.png" width="187" height="27" /></a> </td>
<td valign="top" width="297">Safari4、Opera10、Chrome5</td>
</tr>
<tr>
<td valign="top" width="100">color</td>
<td valign="top" width="166">颜色值</td>
<td valign="top" width="297">NONE</td>
</tr>
</tbody>
</table>
</p>
</p>
</p>
<p>&#160;&#160;&#160; 2. 新属性</p>
<table style="margin: auto" border="0" cellspacing="0" cellpadding="2" width="565">
<tbody>
<tr>
<td style="text-align: center; background-color: #eee" valign="top" width="100">ATTRIBUTE</td>
<td style="text-align: center; background-color: #eee" valign="top" width="250">描述</td>
<td style="text-align: center; background-color: #eee" valign="top" width="213">浏览器支持</td>
</tr>
<tr>
<td valign="top" width="100">pattern</td>
<td valign="top" width="250">属于input元素，设置正则表达式来给input的值设定特定的格式</td>
<td valign="top" width="213">Opera10</td>
</tr>
<tr>
<td valign="top" width="100">required</td>
<td valign="top" width="250">标识表单控件是必填的（不能用于hidden、range、color和submit）</td>
<td valign="top" width="213">Opera10</td>
</tr>
<tr>
<td valign="top" width="100">list</td>
<td valign="top" width="250">当用户输入时，可以给与选择的列表，如：<br />
        <br /><a href="http://varnow.org/wordpress/wp-content/uploads/2010/03/image10.png" rel="lightbox[287]"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://varnow.org/wordpress/wp-content/uploads/2010/03/image-thumb10.png" width="204" height="70" /></a> </td>
<td valign="top" width="213">Opera10</td>
</tr>
<tr>
<td valign="top" width="100">placeholder</td>
<td valign="top" width="250">在未获得焦点并且无输入时的提示文字，如：<a href="http://varnow.org/wordpress/wp-content/uploads/2010/03/image11.png" rel="lightbox[287]"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://varnow.org/wordpress/wp-content/uploads/2010/03/image-thumb11.png" width="239" height="34" /></a> </td>
<td valign="top" width="213">Safari4、Chrome5</td>
</tr>
<tr>
<td valign="top" width="100">autofocus</td>
<td valign="top" width="250">页面加载后是否自动获得焦点</td>
<td valign="top" width="213">Safari4、Chrome5</td>
</tr>
</tbody>
</table>
</p>
<p>以上结果在以下浏览器中测试：</p>
<p>Firefox3.6 Chrome5、Safari4、Opera10、IE9 Platform Prevew</p>
<p><a href="http://varnow.org/pages/html5/form-test1.html" target="_blank">测试页面在此</a></p>
<p>此外，更详细的FORM标准请<a href="http://www.w3.org/TR/html5/forms.html" target="_blank">参考文档</a>。</p>
]]></content:encoded>
			<wfw:commentRss>http://varnow.org/?feed=rss2&amp;p=287</wfw:commentRss>
		</item>
		<item>
		<title>HTML5中的自定义属性</title>
		<link>http://varnow.org/?p=265</link>
		<comments>http://varnow.org/?p=265#comments</comments>
		<pubDate>Tue, 16 Mar 2010 06:40:05 +0000</pubDate>
		<dc:creator>admin</dc:creator>
		
		<category><![CDATA[XHTML]]></category>

		<category><![CDATA[html5]]></category>

		<guid isPermaLink="false">http://varnow.org/?p=265</guid>
		<description><![CDATA[在WEB页面中，为了配合Javascript的交互功能，很多时候都需要给HTML标签添加自定义属性（虽然标准不支持这么做），而关于自定义属性的名字则大都也是各自为战的自定义的，或根据功能、或根据项目等等，例如：


&#60;!--来自贴吧—&#62;

&#60;div cid=&#34;count_ayjpb&#34; st_type=&#34;toupiao&#34;&#62;&#60;/div&#62;

&#60;!--来自有啊—&#62;

&#60;a href=&#34;#&#34; favo_type=&#34;x&#34; favo_obj_id=&#34;x&#34; favo_title=&#34;x&#34;&#62;&#60;/a&#62;

这样的会造成自定义属性名混乱的问题，而且更为重要的是假如新的HTML标准中需要使用这个自定义属性，而且其含义与你的自定义属性中含义并不相同，这是一个大家都不希望看到的情况。于是在HTML5中给出了解决方案：只要是自定义属性都需要带上&#34;data-&#34;前缀。

The
data-*
 collection of author-defined attributes. Authors can define any attribute they want as long as they prefix it with
data-
 to avoid clashes with future versions of HTML.

因此，按照这个规范可以改写上面两个例子以获得更好的兼容性：


&#60;!--来自贴吧—&#62;

&#60;div data-cid=&#34;count_ayjpb&#34; data-st_type=&#34;toupiao&#34;&#62;&#60;/div&#62;

&#60;!--来自有啊—&#62;

&#60;a href=&#34;#&#34; data-favo_type=&#34;x&#34; data-favo_obj_id=&#34;x&#34; data-favo_title=&#34;x&#34;&#62;&#60;/a&#62;

实际上在淘宝的机票已经有看到这样的使用


&#60;span data-display=&#34;inline&#34; data-tnick=&#34;x&#34; data-nick=&#34;x&#34;&#62;&#60;/span&#62;

&#8211;本文到此结束&#8211;
]]></description>
			<content:encoded><![CDATA[<p>在WEB页面中，为了配合Javascript的交互功能，很多时候都需要给HTML标签添加自定义属性（虽然标准不支持这么做），而关于自定义属性的名字则大都也是各自为战的自定义的，或根据功能、或根据项目等等，例如：</p>
<pre class="html" name="code">

&lt;!--来自贴吧—&gt;

&lt;div cid=&quot;count_ayjpb&quot; st_type=&quot;toupiao&quot;&gt;&lt;/div&gt;

&lt;!--来自有啊—&gt;

&lt;a href=&quot;#&quot; favo_type=&quot;x&quot; favo_obj_id=&quot;x&quot; favo_title=&quot;x&quot;&gt;&lt;/a&gt;
</pre>
<p>这样的会造成自定义属性名混乱的问题，而且更为重要的是假如新的HTML标准中需要使用这个自定义属性，而且其含义与你的自定义属性中含义并不相同，这是一个大家都不希望看到的情况。于是在<a href="http://www.w3.org/TR/html5/" target="_blank">HTML5</a>中给出了解决方案：只要是自定义属性都需要带上&quot;data-&quot;前缀。</p>
<blockquote>
<p>The
<div class="hl-surround"><div class="hl-main">data-*</div></div>
<p> collection of author-defined attributes. Authors can define any attribute they want as long as they prefix it with
<div class="hl-surround"><div class="hl-main">data-</div></div>
<p> to avoid clashes with future versions of HTML.</p>
</blockquote>
<p>因此，按照这个规范可以改写上面两个例子以获得更好的兼容性：</p>
<pre class="html" name="code">

&lt;!--来自贴吧—&gt;

&lt;div data-cid=&quot;count_ayjpb&quot; data-st_type=&quot;toupiao&quot;&gt;&lt;/div&gt;

&lt;!--来自有啊—&gt;

&lt;a href=&quot;#&quot; data-favo_type=&quot;x&quot; data-favo_obj_id=&quot;x&quot; data-favo_title=&quot;x&quot;&gt;&lt;/a&gt;
</pre>
<p>实际上在<a href="http://jipiao.taobao.com/index.htm" target="_blank">淘宝的机票</a>已经有看到这样的使用</p>
<pre class="html" name="code">

&lt;span data-display=&quot;inline&quot; data-tnick=&quot;x&quot; data-nick=&quot;x&quot;&gt;&lt;/span&gt;
</pre>
<p>&#8211;本文到此结束&#8211;</p>
]]></content:encoded>
			<wfw:commentRss>http://varnow.org/?feed=rss2&amp;p=265</wfw:commentRss>
		</item>
		<item>
		<title>前端优化总结</title>
		<link>http://varnow.org/?p=260</link>
		<comments>http://varnow.org/?p=260#comments</comments>
		<pubDate>Fri, 12 Mar 2010 10:25:43 +0000</pubDate>
		<dc:creator>admin</dc:creator>
		
		<category><![CDATA[WEB优化]]></category>

		<guid isPermaLink="false">http://varnow.org/?p=260</guid>
		<description><![CDATA[&#160;&#160;&#160; 前段时间简单的研究了下前端优化相关的知识，本文算是一个阶段性的总结，或者当做一个优化的参考List。
前言
&#160;&#160;&#160; 前端是庞大的，包括HTML、CSS、Javascript、Image、Flash等等各种各样的资源。前端优化是复杂的，针对方方面面的资源都有不同的方式。那么，前端优化的目的是什么？
&#160;&#160;&#160; 1. 从用户角度而言，优化能够让页面加载得更快、对用户的操作响应得更及时，能够给用户提供更为友好的体验。
&#160;&#160;&#160; 2. 从服务商角度而言，优化能够减少页面请求数、或者减小请求所占带宽，能够节省可观的资源。
&#160;&#160;&#160; 总之，恰当的优化不仅能够改善站点的用户体验并且能够节省相当的资源利用。
&#160;&#160;&#160; 前端优化的途径有很多，按粒度大致可以分为两类，第一类是页面级别的优化，例如HTTP请求数、脚本的无阻塞加载、内联脚本的位置优化等；第二类则是代码级别的优化，例如Javascript中的DOM操作优化、CSS选择符优化、图片优化以及HTML结构优化等等。另外，本着提高投入产出比的目的，后文提到的各种优化策略大致按照投入产出比从大到小的顺序排列。
 
一、页面级优化
1. 减少HTTP请求数
&#160;&#160;&#160; 这条策略基本上所有前端人都知道，而且也是最重要最有效的。都说要减少HTTP请求，那请求多了到底会怎么样呢？首先，每个请求都是有成本的，既包含时间成本也包含资源成本。一个完整的请求都需要经过DNS寻址、与服务器建立连接、发送数据、等待服务器响应、接收数据这样一个“漫长”而复杂的过程。时间成本就是用户需要看到或者“感受”到这个资源是必须要等待这个过程结束的，资源上由于每个请求都需要携带数据，因此每个请求都需要占用带宽。另外，由于浏览器进行并发请求的请求数是有上限的(具体参见此处)，因此请求数多了以后，浏览器需要分批进行请求，因此会增加用户的等待时间，会给用户造成站点速度慢这样一个印象，即使可能用户能看到的第一屏的资源都已经请求完了，但是浏览器的进度条会一直存在。
&#160;&#160;&#160; 减少HTTP请求数的主要途径包括：
&#160;&#160;&#160; (1). 从设计实现层面简化页面
&#160;&#160;&#160;&#160;&#160;&#160;&#160; 如果你的页面像百度首页一样简单，那么接下来的规则基本上都用不着了。保持页面简洁、减少资源的使用时最直接的。如果不是这样，你的页面需要华丽的皮肤，则继续阅读下面的内容。
&#160;&#160;&#160; (2). 合理设置HTTP缓存
&#160;&#160;&#160;&#160;&#160;&#160;&#160; 缓存的力量是强大的，恰当的缓存设置可以大大的减少HTTP请求。以有啊首页为例，当浏览器没有缓存的时候访问一共会发出78个请求，共600多K数据（如图1.1），而当第二次访问即浏览器已缓存之后访问则仅有10个请求，共20多K数据（如图1.2）。（这里需要说明的是，如果直接F5刷新页面的话效果是不一样的，这种情况下请求数还是一样，不过被缓存资源的请求服务器是304响应，只有Header没有Body，可以节省带宽）
&#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;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 图1.1&#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;&#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;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 图1.2
&#160;&#160;&#160;&#160;&#160;&#160;&#160; 怎样才算合理设置？原则很简单，能缓存越多越好，能缓存越久越好。例如，很少变化的图片资源可以直接通过HTTP Header中的Expires设置一个很长的过期头；变化不频繁而又可能会变的资源可以使用Last-Modifed来做请求验证。尽可能的让资源能够在缓存中待得更久。关于HTTP缓存的具体设置和原理此处就不再详述了，有兴趣的可以参考下列文章：
&#160;&#160;&#160;&#160;&#160;&#160;&#160; HTTP1.1协议中关于缓存策略的描述
&#160;&#160;&#160;&#160;&#160;&#160;&#160; Fiddler HTTP Performance中关于缓存的介绍
&#160;&#160;&#160; (3). 资源合并与压缩
&#160;&#160;&#160;&#160;&#160;&#160;&#160; 如果可以的话，尽可能的将外部的脚本、样式进行合并，多个合为一个。另外，CSS、Javascript、Image都可以用相应的工具进行压缩，压缩后往往能省下不少空间。
&#160;&#160;&#160; (4). CSS Sprites
&#160;&#160;&#160;&#160;&#160;&#160;&#160; 合并CSS图片，减少请求数的又一个好办法。
&#160;&#160;&#160; (5). Inline Images
&#160;&#160;&#160;&#160;&#160;&#160;&#160; 使用data: URL scheme的方式将图片嵌入到页面或CSS中，如果不考虑资源管理上的问题的话，不失为一个好办法。如果是嵌入页面的话换来的是增大了页面的体积，而且无法利用浏览器缓存。使用在CSS中的图片则更为理想一些。
&#160;&#160;&#160; (6). Lazy Load Images
&#160;&#160;&#160;&#160;&#160;&#160;&#160; 这条策略实际上并不一定能减少HTTP请求数，但是却能在某些条件下或者页面刚加载时减少HTTP请求数。对于图片而言，在页面刚加载的时候可以只加载第一屏，当用户继续往后滚屏的时候才加载后续的图片。这样一来，假如用户只对第一屏的内容感兴趣时，那剩余的图片请求就都节省了。有啊首页曾经的做法是在加载的时候把第一屏之后的图片地址缓存在Textarea标签中，待用户往下滚屏的时候才“惰性”加载。
2. 将外部脚本置底
&#160;&#160;&#160;&#160;&#160;&#160; 前文有谈到，浏览器是可以并发请求的，这一特点使得其能够更快的加载资源，然而外链脚本在加载时却会阻塞其他资源，例如在脚本加载完成之前，它后面的图片、样式以及其他脚本都处于阻塞状态，直到脚本加载完成后才会开始加载。如果将脚本放在比较靠前的位置，则会影响整个页面的加载速度从而影响用户体验。解决这一问题的方法有很多，在这里有比较详细的介绍（这里是译文和更详细的例子），而最简单可依赖的方法就是将脚本尽可能的往后挪，减少对并发下载的影响。
3. 异步执行inline脚本
&#160;&#160;&#160;&#160;&#160;&#160;&#160; inline脚本对性能的影响与外部脚本相比，是有过之而无不及。首页，与外部脚本一样，inline脚本在执行的时候一样会阻塞并发请求，除此之外，由于浏览器在页面处理方面是单线程的，当inline脚本在页面渲染之前执行时，页面的渲染工作则会被推迟。简而言之，inline脚本在执行的时候，页面处于空白状态。鉴于以上两点原因，建议将执行时间较长的inline脚本异步执行，异步的方式有很多种，例如使用script元素的defer属性（存在兼容性问题和其他一些问题，例如不能使用document.write）、使用setTimeout，此外，在HTML5中引入了Web Workers的机制，恰恰可以解决此类问题。
4. Lazy Load Javascript
&#160;&#160;&#160;&#160;&#160;&#160;&#160; [...]]]></description>
			<content:encoded><![CDATA[<p>&#160;&#160;&#160; 前段时间简单的研究了下前端优化相关的知识，本文算是一个阶段性的总结，或者当做一个优化的参考List。</p>
<h4>前言</h4>
<p>&#160;&#160;&#160; 前端是庞大的，包括HTML、CSS、Javascript、Image、Flash等等各种各样的资源。前端优化是复杂的，针对方方面面的资源都有不同的方式。那么，前端优化的目的是什么？</p>
<p>&#160;&#160;&#160; 1. 从用户角度而言，优化能够让页面加载得更快、对用户的操作响应得更及时，能够给用户提供更为友好的体验。</p>
<p>&#160;&#160;&#160; 2. 从服务商角度而言，优化能够减少页面请求数、或者减小请求所占带宽，能够节省可观的资源。</p>
<p>&#160;&#160;&#160; 总之，恰当的优化不仅能够改善站点的用户体验并且能够节省相当的资源利用。</p>
<p>&#160;&#160;&#160; 前端优化的途径有很多，按粒度大致可以分为两类，第一类是页面级别的优化，例如HTTP请求数、脚本的无阻塞加载、内联脚本的位置优化等；第二类则是代码级别的优化，例如Javascript中的DOM操作优化、CSS选择符优化、图片优化以及HTML结构优化等等。另外，本着提高投入产出比的目的，后文提到的各种优化策略大致按照投入产出比从大到小的顺序排列。</p>
<p> <span id="more-260"></span><br />
<h4>一、页面级优化</h4>
<p><strong>1. 减少HTTP请求数</strong></p>
<p>&#160;&#160;&#160; 这条策略基本上所有前端人都知道，而且也是最重要最有效的。都说要减少HTTP请求，那请求多了到底会怎么样呢？首先，每个请求都是有成本的，既包含时间成本也包含资源成本。一个完整的请求都需要经过DNS寻址、与服务器建立连接、发送数据、等待服务器响应、接收数据这样一个“漫长”而复杂的过程。时间成本就是用户需要看到或者“感受”到这个资源是必须要等待这个过程结束的，资源上由于每个请求都需要携带数据，因此每个请求都需要占用带宽。另外，由于浏览器进行并发请求的请求数是有上限的(具体参见<a href="http://www.stevesouders.com/blog/2008/03/20/roundup-on-parallel-connections/">此处</a>)，因此请求数多了以后，浏览器需要分批进行请求，因此会增加用户的等待时间，会给用户造成站点速度慢这样一个印象，即使可能用户能看到的第一屏的资源都已经请求完了，但是浏览器的进度条会一直存在。</p>
<p>&#160;&#160;&#160; 减少HTTP请求数的主要途径包括：</p>
<p>&#160;&#160;&#160; (1). 从设计实现层面简化页面</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; 如果你的页面像百度首页一样简单，那么接下来的规则基本上都用不着了。保持页面简洁、减少资源的使用时最直接的。如果不是这样，你的页面需要华丽的皮肤，则继续阅读下面的内容。</p>
<p>&#160;&#160;&#160; (2). 合理设置HTTP缓存</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; 缓存的力量是强大的，恰当的缓存设置可以大大的减少HTTP请求。以<a href="http://youa.baidu.com" target="_blank">有啊首页</a>为例，当浏览器没有缓存的时候访问一共会发出78个请求，共600多K数据（如图1.1），而当第二次访问即浏览器已缓存之后访问则仅有10个请求，共20多K数据（如图1.2）。（这里需要说明的是，如果直接F5刷新页面的话效果是不一样的，这种情况下请求数还是一样，不过被缓存资源的请求服务器是304响应，只有Header没有Body，可以节省带宽）</p>
<p><a href="http://varnow.org/wordpress/wp-content/uploads/2010/03/image.png" rel="lightbox[260]"><img title="image" style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="112" alt="image" src="http://varnow.org/wordpress/wp-content/uploads/2010/03/image-thumb.png" width="244" border="0" /></a>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <a href="http://varnow.org/wordpress/wp-content/uploads/2010/03/image1.png" rel="lightbox[260]"><img title="image" style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="92" alt="image" src="http://varnow.org/wordpress/wp-content/uploads/2010/03/image-thumb1.png" width="244" border="0" /></a> </p>
<p>&#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; 图1.1&#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;&#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;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 图1.2</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; 怎样才算合理设置？原则很简单，能缓存越多越好，能缓存越久越好。例如，很少变化的图片资源可以直接通过HTTP Header中的Expires设置一个很长的过期头；变化不频繁而又可能会变的资源可以使用Last-Modifed来做请求验证。尽可能的让资源能够在缓存中待得更久。关于HTTP缓存的具体设置和原理此处就不再详述了，有兴趣的可以参考下列文章：</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html" target="_blank">HTTP1.1协议中关于缓存策略的描述</a></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; <a href="http://msdn.microsoft.com/en-us/library/bb250442(VS.85).aspx" target="_blank">Fiddler HTTP Performance中关于缓存的介绍</a></p>
<p>&#160;&#160;&#160; (3). 资源合并与压缩</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; 如果可以的话，尽可能的将外部的脚本、样式进行合并，多个合为一个。另外，CSS、Javascript、Image都可以用相应的工具进行压缩，压缩后往往能省下不少空间。</p>
<p>&#160;&#160;&#160; (4). CSS Sprites</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; 合并CSS图片，减少请求数的又一个好办法。</p>
<p>&#160;&#160;&#160; (5). Inline Images</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; 使用data: <a href="http://tools.ietf.org/html/rfc2397" target="_blank">URL scheme</a>的方式将图片嵌入到页面或CSS中，如果不考虑资源管理上的问题的话，不失为一个好办法。如果是嵌入页面的话换来的是增大了页面的体积，而且无法利用浏览器缓存。使用在CSS中的图片则更为理想一些。</p>
<p>&#160;&#160;&#160; (6). Lazy Load Images</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; 这条策略实际上并不一定能减少HTTP请求数，但是却能在某些条件下或者页面刚加载时减少HTTP请求数。对于图片而言，在页面刚加载的时候可以只加载第一屏，当用户继续往后滚屏的时候才加载后续的图片。这样一来，假如用户只对第一屏的内容感兴趣时，那剩余的图片请求就都节省了。<a href="http://youa.baidu.com" target="_blank">有啊首页</a>曾经的做法是在加载的时候把第一屏之后的图片地址缓存在Textarea标签中，待用户往下滚屏的时候才“惰性”加载。</p>
<p><strong>2. 将外部脚本置底</strong></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160; 前文有谈到，浏览器是可以并发请求的，这一特点使得其能够更快的加载资源，然而外链脚本在加载时却会阻塞其他资源，例如在脚本加载完成之前，它后面的图片、样式以及其他脚本都处于阻塞状态，直到脚本加载完成后才会开始加载。如果将脚本放在比较靠前的位置，则会影响整个页面的加载速度从而影响用户体验。解决这一问题的方法有很多，在<a href="http://www.stevesouders.com/blog/2009/04/27/loading-scripts-without-blocking/" target="_blank">这里有比较详细的介绍</a>（这里是<a href="http://varnow.org/?p=42" target="_blank">译文和更详细的例子</a>），而最简单可依赖的方法就是将脚本尽可能的往后挪，减少对并发下载的影响。</p>
<p><strong>3. 异步执行inline脚本</strong></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; inline脚本对性能的影响与外部脚本相比，是有过之而无不及。首页，与外部脚本一样，inline脚本在执行的时候一样会阻塞并发请求，除此之外，由于浏览器在页面处理方面是单线程的，当inline脚本在页面渲染之前执行时，页面的渲染工作则会被推迟。简而言之，inline脚本在执行的时候，页面处于空白状态。鉴于以上两点原因，建议将执行时间较长的inline脚本异步执行，异步的方式有很多种，例如使用script元素的defer属性（存在兼容性问题和其他一些问题，例如不能使用document.write）、使用setTimeout，此外，在HTML5中引入了<a href="https://developer.mozilla.org/En/Using_web_workers" target="_blank">Web Workers</a>的机制，恰恰可以解决此类问题。</p>
<p><strong>4. Lazy Load Javascript</strong></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; 随着Javascript框架的流行，越来越多的站点也使用起了框架。不过，一个框架往往包括了很多的功能实现，这些功能并不是每一个页面都需要的，如果下载了不需要的脚本则算得上是一种资源浪费-既浪费了带宽又浪费了执行花费的时间。目前的做法大概有两种，一种是为那些流量特别大的页面专门定制一个专用的mini版框架，另一种则是Lazy Load。<a href="http://developer.yahoo.com/yui/3/" target="_blank">YUI</a>则使用了第二种方式，在YUI的实现中，最初只加载核心模块，其他模块可以等到需要使用的时候才加载。</p>
<p><strong>5. 将CSS放在HEAD中</strong></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; 如果将CSS放在其他地方比如BODY中，则浏览器有可能还未下载和解析到CSS就已经开始渲染页面了，这就导致页面由无CSS状态跳转到CSS状态，用户体验比较糟糕。除此之外，有些浏览器会在CSS下载完成后才开始渲染页面，如果CSS放在靠下的位置则会导致浏览器将渲染时间推迟。</p>
<p><strong>6. 异步请求Callback</strong></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; 在某些页面中可能存在这样一种需求，需要使用script标签来异步的请求数据。类似：</p>
<p>Javascript:</p>
<pre class="javascript" name="code">

/*Callback函数*/

function myCallback(info){

//do something here

}
</pre>
<p>HTML:</p>
<pre class="html" name="code">

&lt;script type=&quot;text/javascript&quot; src=&quot;http://abc.com/cb&quot;&gt;&lt;/script&gt;
</pre>
<p>cb返回的内容:</p>
<pre class="html" name="code">

myCallback('Hello world!');
</pre>
<p>像以上这种方式直接在页面上写&lt;script&gt;对页面的性能也是有影响的，即增加了页面首次加载的负担，推迟了DOMLoaded和window.onload事件的触发时机。如果时效性允许的话，可以考虑在DOMLoaded事件触发的时候加载，或者使用setTimeout方式来灵活的控制加载的时机。 </p>
<p><strong>7. 减少不必要的HTTP跳转</strong></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; 对于以目录形式访问的HTTP链接，很多人都会忽略链接最后是否带&#8217;/'，假如你的服务器对此是区别对待的话，那么你也需要注意，这其中很可能隐藏了301跳转，增加了多余请求。具体参见下图，其中第一个链接是以无&#8217;/'结尾的方式访问的，于是服务器有了一次跳转。</p>
<p><a href="http://varnow.org/wordpress/wp-content/uploads/2010/03/image2.png" rel="lightbox[260]"><img title="image" style="border-top-width: 0px; display: block; border-left-width: 0px; float: none; border-bottom-width: 0px; margin-left: auto; margin-right: auto; border-right-width: 0px" height="35" alt="image" src="http://varnow.org/wordpress/wp-content/uploads/2010/03/image-thumb2.png" width="244" border="0" /></a> </p>
<p align="center">图1.3</p>
<p><strong>8. 避免重复的资源请求</strong></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; 这种情况主要是由于疏忽或页面由多个模块拼接而成，然后每个模块中请求了同样的资源时，会导致资源的重复请求。出现的几率不大，但是还是要注意排查，不然可能会出现如下局面，来自<a href="http://womai.com/" target="_blank">这里</a>。</p>
<p><a href="http://varnow.org/wordpress/wp-content/uploads/2010/03/image3.png" rel="lightbox[260]"><img title="image" style="border-top-width: 0px; display: block; border-left-width: 0px; float: none; border-bottom-width: 0px; margin-left: auto; margin-right: auto; border-right-width: 0px" height="159" alt="image" src="http://varnow.org/wordpress/wp-content/uploads/2010/03/image-thumb3.png" width="244" border="0" /></a></p>
<p align="center">图1.4 </p>
<p>图1.4中，同一个JS在一个页面中请求了9次，全是200请求，当然这个算比较极端的情况了。</p>
<h4>二、代码级优化</h4>
<p><strong>1. Javascript</strong></p>
<p>&#160;&#160;&#160; (1). DOM</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; DOM操作应该是脚本中最耗性能的一类操作，例如增加、修改、删除DOM元素或者对DOM集合进行操作。如果脚本中包含了大量的DOM操作则需要注意以下几点：</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; a. HTML Collection</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; 在脚本中document.images、document.forms、getElementsByTagName()返回的都是HTMLCollection类型的集合，在平时使用的时候大多将它作为数组来使用，因为它有length属性，也可以使用索引访问每一个元素。不过在访问性能上则比数组要差很多，原因是这个集合并不是一个静态的结果，它表示的仅仅是一个特定的查询，每次访问该集合时都会重新执行这个查询从而更新查询结果。所谓的“访问集合”包括读取集合的length属性、访问集合中的元素。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; 因此，当你需要遍历HTML Collection的时候，尽量将它转为数组后再访问，以提高性能。即使不转换为数组，也请尽可能少的访问它，例如在遍历的时候可以将length属性、成员保存到局部变量后再使用局部变量。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; b. Reflow &amp; Repaint</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; 除了上面一点之外，DOM操作还需要考虑浏览器的Reflow和Repaint，因为这些都是需要消耗资源的，具体的可以参加以下文章：</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; <a href="http://varnow.org/?p=232" target="_blank">如何减少浏览器的repaint和reflow?</a></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; <a href="http://blog.dynatrace.com/2009/12/12/understanding-internet-explorer-rendering-behaviour/">Understanding Internet Explorer Rendering Behaviour</a></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; <a href="http://www.mozilla.org/newlayout/doc/reflow.html">Notes on HTML Reflow</a></p>
<p>&#160;&#160;&#160; (2). 慎用with</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; <span style="border-right: #ccc 1px solid; padding-right: 1px; border-top: #ccc 1px solid; padding-left: 1px; padding-bottom: 1px; border-left: #ccc 1px solid; padding-top: 1px; border-bottom: #ccc 1px solid; background-color: #eee">with(obj){ p = 1}; </span>代码块的行为实际上是修改了代码块中的<a href="http://varnow.org/?p=171" target="_blank">执行环境</a>，将obj放在了其作用域链的最前端，在with代码块中访问非局部变量是都是先从obj上开始查找，如果没有再依次按作用域链向上查找，因此使用with相当于增加了作用域链长度。而每次查找作用域链都是要消耗时间的，过长的作用域链会导致查找性能下降。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160; 因此，除非你能肯定在with代码中只访问obj中的属性，否则慎用with，替代的可以使用局部变量缓存需要访问的属性。&#160;&#160;&#160;&#160;&#160;&#160; </p>
<p>&#160;&#160;&#160; (3). 避免使用eval和Function </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; 每次 eval 或 Function 构造函数作用于字符串表示的源代码时，脚本引擎都需要将源代码转换成可执行代码。这是很消耗资源的操作 —— 通常比简单的函数调用慢100倍以上。 </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160; eval 函数效率特别低，由于事先无法知晓传给 eval 的字符串中的内容，eval在其上下文中解释要处理的代码，也就是说编译器无法优化上下文，因此只能有浏览器在运行时解释代码。这对性能影响很大。 </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160; Function 构造函数比eval略好，因为使用此代码不会影响周围代码；但其速度仍很慢。 </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; 此外，使用eval和Function也不利于Javascript压缩工具执行压缩。</p>
<p>&#160;&#160;&#160; (4). 减少作用域链查找</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; 前文谈到了作用域链查找问题，这一点在循环中是尤其需要注意的问题。如果在循环中需要访问非本作用域下的变量时请在遍历之前用局部变量缓存该变量，并在遍历结束后再重写那个变量，这一点对全局变量尤其重要，因为全局变量处于作用域链的最顶端，访问时的查找次数是最多的。</p>
<p>低效率的写法：</p>
<pre class="javascript" name="code">

//全局变量

var globalVar = 1;

function myCallback(info){

   for( var i = 100000; i--;){

      //每次访问globalVar都需要查找到作用域链最顶端，本例中需要访问100000次

      globalVar += i;

   }

}
</pre>
<p>&#160;&#160; 更高效的写法：</p>
<pre class="javascript" name="code">

//全局变量

var globalVar = 1;

function myCallback(info){

   //局部变量缓存全局变量

   var localVar = globalVar;

   for( var i = 100000; i--;){

      //访问局部变量是最快的

      localVar += i;

   }

   //本例中只需要访问2次全局变量

   globalVar = localVar;

}
</pre>
<p>&#160;&#160;&#160; 此外，要减少作用域链查找还应该减少闭包的使用。</p>
<p>&#160;&#160;&#160; (5). 数据访问</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; Javascript中的数据访问包括直接量（字符串、正则表达式）、变量、对象属性以及数组，其中对直接量和局部变量的访问是最快的，对对象属性以及数组的访问需要更大的开销。当出现以下情况时，建议将数据放入局部变量：</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; a. 对任何对象属性的访问超过1次</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; b. 对任何数组成员的访问次数超过1次</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; 另外，还应当尽可能的减少对对象以及数组深度查找。</p>
<p>&#160;&#160;&#160; (6). 字符串拼接</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; 在Javascript中使用&quot;+&quot;号来拼接字符串效率是比较低的，因为每次运行都会开辟新的内存并生成新的字符串变量，然后将拼接结果赋值给新变量。与之相比更为高效的做法是使用数组的join方法，即将需要拼接的字符串放在数组中最后调用其join方法得到结果。不过由于使用数组也有一定的开销，因此当需要拼接的字符串较多的时候可以考虑用此方法。</p>
<p>&#160;&#160;&#160; 关于Javascript优化的更详细介绍请参考：</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; <a href="http://www.slideshare.net/nzakas/writing-efficient-javascript" target="_blank">Write Efficient Javascript(PPT)</a></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; <a href="http://dev.opera.com/articles/view/efficient-javascript/" target="_blank">Efficient JavaScript</a>&#160;&#160;&#160; </p>
<p><strong>2. CSS选择符</strong></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; 在大多数人的观念中，都觉得浏览器对CSS选择符的解析式从左往右进行的，例如</p>
<pre class="css" name="code">#toc A { color: #444; }</pre>
<p>这样一个选择符，如果是从右往左解析则效率会很高，因为第一个ID选择基本上就把查找的范围限定了，但实际上浏览器对选择符的解析是从右往左进行的。如上面的选择符，浏览器必须遍历查找每一个A标签的祖先节点，效率并不像之前想象的那样高。根据浏览器的这一行为特点，在写选择符的时候需要注意很多事项，有人已经一一列举了，<a href="https://developer.mozilla.org/en/Writing_Efficient_CSS" target="_blank">详情参考此处</a>。</p>
<p><strong>3. HTML</strong></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; 对HTML本身的优化现如今也越来越多的受人关注了，详情可以参见这篇<a href="http://varnow.org/?p=249" target="_blank">总结性文章</a>。</p>
<p><strong>4. Image压缩</strong></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; 图片压缩是个技术活，不过现如今这方面的工具也非常多，压缩之后往往能带来不错的效果，具体的压缩原理以及方法在《Even Faster Web Sites》第10章有很详细的介绍，有兴趣的可以去看看。</p>
<h4>总结</h4>
</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; 本文从页面级以及代码级两个粒度对前端优化的各种方式做了一个总结，这些方法基本上都是前端开发人员在开发的过程中可以借鉴和实践的，除此之外，完整的前端优化还应该包括很多其他的途径，例如CDN、Gzip、多域名、无Cookie服务器等等，由于对于开发人员的可操作性并不强大，在此也就不多叙述了，详细的可以参考<a href="http://developer.yahoo.com/performance/rules.html" target="_blank">Yahoo</a>和<a href="http://code.google.com/speed/page-speed/docs/rules_intro.html" target="_blank">Google</a>的这些“金科玉律”。</p>
]]></content:encoded>
			<wfw:commentRss>http://varnow.org/?feed=rss2&amp;p=260</wfw:commentRss>
		</item>
		<item>
		<title>HTML优化</title>
		<link>http://varnow.org/?p=249</link>
		<comments>http://varnow.org/?p=249#comments</comments>
		<pubDate>Fri, 05 Mar 2010 10:44:04 +0000</pubDate>
		<dc:creator>admin</dc:creator>
		
		<category><![CDATA[WEB优化]]></category>

		<guid isPermaLink="false">http://varnow.org/?p=249</guid>
		<description><![CDATA[在前段领域中，对Javascript和CSS的优化一直是大家关注的焦点，相应的压缩优化工具也非常丰富，相对而言对HTML优化的关注则显得有些冷淡。在Steve Souders的大作《Even Faster Web Sites》中谈到非常多有效的前端优化方法，例如Javascript的加载、CSS选择符、图片优化、gzip、iframe问题等，唯独没有细说HTML优化。在整个前端的构成中，HTML是必不可少的一部分，而且是真正的展示“前端”。虽然与动辄十几K的Javascript相比，HTML的大小在整个页面资源中一般不会占太多的比重，而且还有Gzip，但是事实表明，大多数页面都有较大的压缩余地，即使是Gzip过后仍然能减小可观的体积，这一事实在后续的文章中会给出，本文主要讨论HTML优化的一些可行和看上去不那么可行的途径。

对于各种优化途径，如果一定要我给一个分类的话，我会用颜色来区分。对于那些在各种页面中都适用而且无害的方法，我将它们归为绿色。相对而言，只有在某些具体的情况下才适用或者有违标准的方法归为橙色。
绿色方法
1. 使用相对URL
对于页面中的各种URL，例如链接、外链CSS的href、外链Javascript的src、图片src、iframe src等，如果能够确定它们与当前页面是处于同一域名下的话，则可以使用相对URL，这样每一个URL都能够节省至少一个域名的长度。
2. 删除HTTP或HTTPS
绝对URL都以HTTP或HTTPS等协议头开始，如果能确定URL的协议与当前页面URL的协议是一致的，或者说该URL在多种协议下均是可用的，则可以考虑删除这个协议头。这样做虽然有些非主流，但事实证明是可行的，而且也有理论依据(见http://www.ietf.org/rfc/rfc2396.txt 第5.2节描述)。Nexus One的这个页面中有些资源URL就删除了协议头，虽然并不是出于节省资源的目的，但至少证明删除协议头是可行的。
对于CSS，如果删除协议头在IE7、IE8下会造成CSS下载两次，这个是需要注意的问题。
3. 删除注释
与CSS、Javascript一样，也可以通过删除HTML中的注释来实现优化。然而，这一点对于HTML来说却要复杂一些，因为在HTML中存在某些特殊作用的注释是不能删除的。
(a). IE条件注释
&#60;!--[if expression]&#62; HTML &#60;![endif]—&#62;
诸如这样的语法，只有符合expression的浏览器才能识别其中的内容。
(b). CDATA注释
&#60;script type="text/javascript"&#62;
/* &#60;![CDATA[ */
// content of your Javascript goes here
/* ]]&#62; */
&#60;/script&#62;
CDATA本身是XHTML中才需要的，在CDATA中的内容不需要对特殊字符进行转义，不过为了兼容老版本的浏览器，需要加上注释，以免出错。不过，对于这个注释，如果你的页面并没有真正的使用XHTML标准则可以大胆的删除掉。使用XHTML标准不是简单的声明DTD就可以办到的，除此之外还需要服务器能够派application/xhtml+xml这样的内容格式，另外还需要浏览器支持，目前IE并不支持XHTML格式。因此，目前你的页面可能是按照XHTML规范来写的，也能通过HTML验证，但想要兼容IE则一定还是以HTML方式来展示的。这样一来，很多为了遵循标准而增加的内容则可以删除，具体的在后文的橙色部分详述。
(c). 自定义注释
产品中自定义的一些具有特定功能的注释，例如统计。
4. 压缩空白符
在HTML中的大多数标签中，多个空白符都会被压缩成一个来显示，于是可以通过删除多余的空白符来减小HTML体积。不过需要注意的是并不是所有的空白符都可以这样做，例如pre、textarea中的空白符是按原样显示的，标签属性中的属性值也是按原样显示的，例如title、value等。
5. 压缩inline css &#38; Javascript
不管inline还是external，都需要压缩，这是减小体积的最直接的方式。
6. CSS&#38;Javascript尽量外链
外链CSS和Javascript，不仅仅是减小HTML本身的体积，而且还可以充分利用浏览器的缓存机制，所以当CSS、Javascript的代码量超过一定体积时，尽量外链。除此之外，尽量避免在页面中使用onmouseover=&#8221;xxxx&#8221;的方式来注册事件，既不利于维护也容易产生不必要的冗余代码。
7. 删除元素默认属性
在HTML规范中，很多HTML元素的属性是有默认值的，对于这些默认值可以抹去不写，下面是不完全统计。



标签
属性
默认值


style
media
screen


link
media
screen


form
method
get


form




input
type
text



橙色方法
橙色方法是指那些有违标准，或者会导致页面出现无法预料的问题的一类方法，算是一类非常规的方法，因此也仅仅适用于一些非常规的页面，例如搜索引擎首页。
1. 删除或替换&#60;!DOCYTYPE&#62;
DOCTYPE对于页面展现非常重要，浏览器会根据具体的DOCTYPE来决定如何显示页面，除非你对删除DOCTYPE后的页面展示十分有把握，否则不要轻易删除。另外一种可行的方式是采用HTML5中的写法，即&#60;!doctype html&#62;，大多数浏览器能够将这种doctype写法解析为标准或准标准(Almost Standard)模式，不过依然会造成一些问题，具体见这里。目前百度、Google都是这么做的。
2. 删除标签属性值的引号
引号不是必须的，删之。此处也需要考虑一些特殊情况，例如属性值中包含引号等。
（提醒：不符合XHTML规范）
3. 省略某些属性值
例如disabled、checked、multiple等的值都可以省略。
（提醒：不符合XHTML规范）
4. 删除可选的闭合标签
例如body、p、tr等标签是允许没有闭合标签的，具体的参考此处。
（提醒：不符合XHTML规范）
5. 删除自动闭合标签的&#8221;/&#8221;
（提醒：不符合XHTML规范）
工具
HTML优化工具目前比较理想的是Absolute HTML Compressor，另外，PageSpeed1.6中也引入了HTML压缩功能，不过目前只有四个简单的策略，尚处于实验性阶段。随着大家对前端性能越来越高的关注度，HTML优化及压缩方面也一定会有所发展的。
总结
1. HTML优化及压缩的发展滞后于前端其他资源，不过目前也已经引起大家越来越多的关注。
2. 绿色规则中的很多都是在写码过程中就可以注意和遵循的，如果能把这个优化融入到编码中，效果应该更好。
3. 橙色规则由于违背某些规范，只适用于某些特定的页面，使用时务必谨慎。
参考资料
HTML minify – to be or not to be?
Optimizing [...]]]></description>
			<content:encoded><![CDATA[<p>在前段领域中，对Javascript和CSS的优化一直是大家关注的焦点，相应的压缩优化工具也非常丰富，相对而言对HTML优化的关注则显得有些冷淡。在<a href="http://www.stevesouders.com/blog/" target="_blank">Steve Souders</a>的大作《Even Faster Web Sites》中谈到非常多有效的前端优化方法，例如Javascript的加载、CSS选择符、图片优化、gzip、iframe问题等，唯独没有细说HTML优化。在整个前端的构成中，HTML是必不可少的一部分，而且是真正的展示“前端”。虽然与动辄十几K的Javascript相比，HTML的大小在整个页面资源中一般不会占太多的比重，而且还有Gzip，但是事实表明，大多数页面都有较大的压缩余地，即使是Gzip过后仍然能减小可观的体积，这一事实在后续的文章中会给出，本文主要讨论HTML优化的一些可行和看上去不那么可行的途径。</p>
<p><span id="more-249"></span></p>
<p>对于各种优化途径，如果一定要我给一个分类的话，我会用颜色来区分。对于那些在各种页面中都适用而且无害的方法，我将它们归为<strong><span style="color: #49ad44;">绿色</span></strong>。相对而言，只有在某些具体的情况下才适用或者有违标准的方法归为<strong><span style="color: #d2aa0b;">橙色</span></strong>。</p>
<h4><strong><span style="color: #49ad44;">绿色方法</span></strong></h4>
<p>1. 使用相对URL</p>
<p>对于页面中的各种URL，例如链接、外链CSS的href、外链Javascript的src、图片src、iframe src等，如果能够确定它们与当前页面是处于同一域名下的话，则可以使用相对URL，这样每一个URL都能够节省至少一个域名的长度。</p>
<p>2. 删除HTTP或HTTPS</p>
<p>绝对URL都以HTTP或HTTPS等协议头开始，如果能确定URL的协议与当前页面URL的协议是一致的，或者说该URL在多种协议下均是可用的，则可以考虑删除这个协议头。这样做虽然有些非主流，但事实证明是可行的，而且也有理论依据(见<a href="http://www.ietf.org/rfc/rfc2396.txt">http://www.ietf.org/rfc/rfc2396.txt</a> 第5.2节描述)。<a href="http://www.google.com/phone/" target="_blank">Nexus One</a>的这个页面中有些资源URL就删除了协议头，虽然并不是出于节省资源的<a href="http://blog.httpwatch.com/2010/02/10/using-protocol-relative-urls-to-switch-between-http-and-https/" target="_blank">目的</a>，但至少证明删除协议头是可行的。</p>
<p>对于CSS，如果删除协议头<a href="http://www.stevesouders.com/blog/2010/02/10/5a-missing-schema-double-download/" target="_blank">在IE7、IE8下会造成CSS下载两次</a>，这个是需要注意的问题。</p>
<p>3. 删除注释</p>
<p>与CSS、Javascript一样，也可以通过删除HTML中的注释来实现优化。然而，这一点对于HTML来说却要复杂一些，因为在HTML中存在某些特殊作用的注释是不能删除的。</p>
<p>(a). <a href="http://msdn.microsoft.com/en-us/library/ms537512%28VS.85%29.aspx" target="_blank">IE条件注释</a></p>
<pre>&lt;!--[if <em>expression</em>]&gt; <em>HTML</em> &lt;![endif]—&gt;</pre>
<p>诸如这样的语法，只有符合expression的浏览器才能识别其中的内容。</p>
<p>(b). <a href="http://javascript.about.com/library/blxhtml.htm" target="_blank">CDATA注释</a></p>
<pre>&lt;script type="text/javascript"&gt;
/* &lt;![CDATA[ */
// content of your Javascript goes here
/* ]]&gt; */
&lt;/script&gt;</pre>
<p>CDATA本身是XHTML中才需要的，在CDATA中的内容不需要对特殊字符进行转义，不过为了兼容老版本的浏览器，需要加上注释，以免出错。不过，对于这个注释，如果你的页面并没有真正的使用XHTML标准则可以大胆的删除掉。使用XHTML标准不是简单的声明DTD就可以办到的，除此之外还需要服务器能够派application/xhtml+xml这样的内容格式，另外还需要浏览器支持，目前IE并不支持XHTML格式。因此，目前你的页面可能是按照XHTML规范来写的，也能通过HTML验证，但想要兼容IE则一定还是以HTML方式来展示的。这样一来，很多为了遵循标准而增加的内容则可以删除，具体的在后文的橙色部分详述。</p>
<p>(c). 自定义注释</p>
<p>产品中自定义的一些具有特定功能的注释，例如统计。</p>
<p>4. 压缩空白符</p>
<p>在HTML中的大多数标签中，多个空白符都会被压缩成一个来显示，于是可以通过删除多余的空白符来减小HTML体积。不过需要注意的是并不是所有的空白符都可以这样做，例如pre、textarea中的空白符是按原样显示的，标签属性中的属性值也是按原样显示的，例如title、value等。</p>
<p>5. 压缩inline css &amp; Javascript</p>
<p>不管inline还是external，都需要压缩，这是减小体积的最直接的方式。</p>
<p>6. CSS&amp;Javascript尽量外链</p>
<p>外链CSS和Javascript，不仅仅是减小HTML本身的体积，而且还可以充分利用浏览器的缓存机制，所以当CSS、Javascript的代码量超过一定体积时，尽量外链。除此之外，尽量避免在页面中使用<span style="border-right: #ccc 1px solid; border-top: #ccc 1px solid; border-left: #ccc 1px solid; border-bottom: #ccc 1px solid; background-color: #eee">onmouseover=&#8221;xxxx&#8221;</span>的方式来注册事件，既不利于维护也容易产生不必要的冗余代码。</p>
<p>7. 删除元素默认属性</p>
<p>在<a href="http://www.w3.org/TR/html401/" target="_blank">HTML规范</a>中，很多HTML元素的属性是有默认值的，对于这些默认值可以抹去不写，下面是不完全统计。</p>
<table style="margin: auto; width: 400px;" border="0" cellspacing="0" cellpadding="2">
<tbody>
<tr>
<td width="133" valign="top"><strong>标签</strong></td>
<td width="133" valign="top"><strong>属性</strong></td>
<td width="133" valign="top"><strong>默认值</strong></td>
</tr>
<tr>
<td width="133" valign="top">style</td>
<td width="133" valign="top">media</td>
<td width="133" valign="top">screen</td>
</tr>
<tr>
<td width="133" valign="top">link</td>
<td width="133" valign="top">media</td>
<td width="133" valign="top">screen</td>
</tr>
<tr>
<td width="133" valign="top">form</td>
<td width="133" valign="top">method</td>
<td width="133" valign="top">get</td>
</tr>
<tr>
<td width="133" valign="top">form</td>
<td width="133" valign="top"><a name="adef-enctype"></a></td>
<td width="133" valign="top"><a name="idx-applicationx-www-form-urlencoded-1"></a></td>
</tr>
<tr>
<td width="133" valign="top">input</td>
<td width="133" valign="top">type</td>
<td width="133" valign="top">text</td>
</tr>
</tbody>
</table>
<h4><strong><span style="color: #bf930d;">橙色方法</span></strong></h4>
<p>橙色方法是指那些有违标准，或者会导致页面出现无法预料的问题的一类方法，算是一类非常规的方法，因此也仅仅适用于一些非常规的页面，例如搜索引擎首页。</p>
<p>1. 删除或替换&lt;!DOCYTYPE&gt;</p>
<p>DOCTYPE对于页面展现非常重要，浏览器会根据具体的DOCTYPE来<a href="http://hsivonen.iki.fi/doctype/#handling" target="_blank">决定如何显示页面</a>，除非你对删除DOCTYPE后的页面展示十分有把握，否则不要轻易删除。另外一种可行的方式是采用HTML5中的写法，即<span style="background-color: #eeeeee; border: 1px solid #cccccc;">&lt;!doctype html&gt;</span>，大多数浏览器能够将这种doctype写法解析为标准或准标准(Almost Standard)模式，不过依然会造成一些问题，具体见<a href="http://blog.silentash.com/2010/01/html5-doctype-and-img-space/" target="_blank">这里</a>。目前<a href="http://www.baidu.com" target="_blank">百度</a>、<a href="http://www.google.com" target="_blank">Google</a>都是这么做的。</p>
<p>2. 删除标签属性值的引号</p>
<p>引号不是必须的，删之。此处也需要考虑一些特殊情况，例如属性值中包含引号等。</p>
<p>（提醒：不符合XHTML规范）</p>
<p>3. 省略某些属性值</p>
<p>例如disabled、checked、multiple等的值都可以省略。</p>
<p>（提醒：不符合XHTML规范）</p>
<p>4. 删除可选的闭合标签</p>
<p>例如body、p、tr等标签是允许没有闭合标签的，具体的参考<a href="http://hikejun.com/blog/?p=525" target="_blank">此处</a>。</p>
<p>（提醒：不符合XHTML规范）</p>
<p>5. 删除自动闭合标签的&#8221;/&#8221;</p>
<p>（提醒：不符合XHTML规范）</p>
<h4>工具</h4>
<p>HTML优化工具目前比较理想的是<a href="http://www.alentum.com/ahc/">Absolute HTML Compressor</a>，另外，<a href="http://code.google.com/speed/page-speed/download.html" target="_blank">PageSpeed1.6</a>中也引入了HTML压缩功能，不过目前只有<a href="http://code.google.com/p/page-speed/wiki/MinifyHtml" target="_blank">四个简单的策略</a>，尚处于实验性阶段。随着大家对前端性能越来越高的关注度，HTML优化及压缩方面也一定会有所发展的。</p>
<h4>总结</h4>
<p>1. HTML优化及压缩的发展滞后于前端其他资源，不过目前也已经引起大家越来越多的关注。</p>
<p>2. 绿色规则中的很多都是在写码过程中就可以注意和遵循的，如果能把这个优化融入到编码中，效果应该更好。</p>
<p>3. 橙色规则由于违背某些规范，只适用于某些特定的页面，使用时务必谨慎。</p>
<p>参考资料</p>
<h4><a href="http://www.speedingupwebsite.com/2010/02/02/html-minify-to-be-or-not-to-be/">HTML minify – to be or not to be?</a></h4>
<h4><a href="http://perfectionkills.com/optimizing-html/" target="_blank">Optimizing HTML</a></h4>
<h4><a href="http://annevankesteren.nl/2010/01/optimizing-html" target="_blank">Optimizing Optimizing HTML</a></h4>
]]></content:encoded>
			<wfw:commentRss>http://varnow.org/?feed=rss2&amp;p=249</wfw:commentRss>
		</item>
		<item>
		<title>从另外两道题说起</title>
		<link>http://varnow.org/?p=245</link>
		<comments>http://varnow.org/?p=245#comments</comments>
		<pubDate>Fri, 26 Feb 2010 03:25:25 +0000</pubDate>
		<dc:creator>admin</dc:creator>
		
		<category><![CDATA[Javascript]]></category>

		<category><![CDATA[quiz]]></category>

		<guid isPermaLink="false">http://varnow.org/?p=245</guid>
		<description><![CDATA[最近貌似流行做Javascript题，Dmitry A. Soshnikov这又出了几道题-The quiz，还可以自我评分，有兴趣的可以去试试看自己可以得几分。本文主要挑了2道比较绕的题目来说几个知识点：
1. Javascript中的&#8221;,&#8221;（逗号）操作符
2. Javascript中&#8221;;&#8221;（分号）的重要性
3. Javascript中with块中的作用域
下面请看题目。

题目1
var
  b = 10,
  c = (
    20,
    function (x) { return x + 100},
    function () { return arguments[0]}
  );

a = b + c
({x: 10}).x;
要说这道题先来说说Javascript中的逗号，Javascript中的逗号大概在以下几处会用到：

//1. 变量申明
var v1, v2;

//2. 数组定义
var arr = [1,2,3];

//3. 函数参数
function func(arg1,arg2){};
func(1,2);

//4. 操作符(for语句中的循环执行表达式)
for( var i = 0, j = 0; i &#60; len; i++,j++){}
其中用法1-3是比较常见的，而用法4中很多人并没有注意，for语句最后的&#8221;i++,j++&#8221;正式一个包含逗号操作符的表达式，在MDC中对逗号操作符做了如下描述：
You can use [...]]]></description>
			<content:encoded><![CDATA[<p>最近貌似流行做Javascript题，<a href="http://javascript.ru/person/Dmitry-A.-Soshnikov">Dmitry A. Soshnikov</a>这又出了几道题-<a href="http://javascript.ru/blog/Dmitry-A.-Soshnikov/The-quiz" target="_blank">The quiz</a>，还可以自我评分，有兴趣的可以去试试看自己可以得几分。本文主要挑了2道比较绕的题目来说几个知识点：</p>
<p>1. Javascript中的&#8221;,&#8221;（逗号）操作符</p>
<p>2. Javascript中&#8221;;&#8221;（分号）的重要性</p>
<p>3. Javascript中with块中的作用域</p>
<p>下面请看题目。</p>
<p><span id="more-245"></span></p>
<p>题目1</p>
<pre name="code" class="javascript">var
  b = 10,
  c = (
    20,
    function (x) { return x + 100},
    function () { return arguments[0]}
  );

a = b + c
({x: 10}).x;</pre>
<p>要说这道题先来说说Javascript中的逗号，Javascript中的逗号大概在以下几处会用到：</p>
<pre name="code" class="javascript">
//1. 变量申明
var v1, v2;

//2. 数组定义
var arr = [1,2,3];

//3. 函数参数
function func(arg1,arg2){};
func(1,2);

//4. 操作符(for语句中的循环执行表达式)
for( var i = 0, j = 0; i &lt; len; i++,j++){}</pre>
<p>其中用法1-3是比较常见的，而用法4中很多人并没有注意，for语句最后的&#8221;i++,j++&#8221;正式一个包含逗号操作符的表达式，在<a href="https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Operators/Special_Operators/Comma_Operator" target="_blank">MDC</a>中对逗号操作符做了如下描述：</p>
<p><cite>You can use the comma operator when you want to include multiple expressions in a location that requires a single expression. </cite></p>
<p>实际上，逗号操作符会从左往右依次计算每个表达式的值并返回最后一个表达式的计算结果。会到题中，变量c的赋值正是一个逗号操作符运算，因此c的值为function(){return arguments[0]}。</p>
<p>之后看a的赋值&#8221;b+c&#8221;，表面看貌似一个整数加一个函数，实际上因为c之后没有分号，因此c会与第二行的&#8221;({x:10})&#8221;结合，相当于&#8221;c({x:10})&#8221;，因此最后a的值为&#8221;10+10&#8243;。关于分号问题，下面有一段更常见的代码：</p>
<pre name="code" class="javascript">
var a = function(){ return arguments[0];}
(function(x){
    return x;
})(100);</pre>
<p>最后a的值为100，因此在给变量申明时务必记得加分号。</p>
<p>题目2</p>
<pre name="code" class="javascript">({
  x: 10,
  foo: function () {
    function bar() {
      console.log(x);
      console.log(y);
      console.log(this.x);
    }
    with (this) {
      var x = 20;
      var y = 30;
      bar.call(this);
    }
  }
}).foo();</pre>
<p>这里主要是要搞清楚with块的作用，with(obj)使用时只是将obj放置在作用域链的最前端，而并不会像函数那样形成一个完整的活动对象（包括局部变量、arguments、函数等），因此在with块中使用var声明变量时，而实际上还是在with块之外创建了局部变量，并不是with块的局部变量。同样，在with块中定义的函数实际上也是属于with块外层。另外，在with块中给变量赋值时则会先从obj对象中查找变量，如果找不到则依次按照作用域链查找。 因此，以上的with块的代码可以这样分解：</p>
<pre name="code" class="javascript">
({
  x: 10,
  foo: function () {
    function bar() {
      console.log(x);
      console.log(y);
      console.log(this.x);
    }

    var x,y;
    with (this) {
      x = 20;
      y = 30;
      bar.call(this);
    }
  }
}).foo();</pre>
<p>在代码解析的时候，会在foo函数中创建两个局部变量x,y(初始值都为undefined)。而在运行时的with块中，给x赋值时因为首先找到的变量是this(也就是最外层的那个对象)中的x属性，因此this.x=20；这也造成了作用域链中位于this上一级的foo的上下文(context)中的局部变量x没有被赋值。同理，with块中给y赋值时，按作用域链查找到得是foo中的局部变量，因此局部变量y=30。最后bar在执行的时候，x/y均是访问foo中的局部变量，因此一个为undefined，一个为30,而this.x为20。</p>
<p>呃…要说清楚还真绕……!</p>
]]></content:encoded>
			<wfw:commentRss>http://varnow.org/?feed=rss2&amp;p=245</wfw:commentRss>
		</item>
		<item>
		<title>Firefox3.5 CSS Sprites Bug</title>
		<link>http://varnow.org/?p=241</link>
		<comments>http://varnow.org/?p=241#comments</comments>
		<pubDate>Fri, 05 Feb 2010 06:08:14 +0000</pubDate>
		<dc:creator>admin</dc:creator>
		
		<category><![CDATA[WEB优化]]></category>

		<category><![CDATA[Firefox3.5]]></category>

		<guid isPermaLink="false">http://varnow.org/?p=241</guid>
		<description><![CDATA[今天猛然发现在Firefox3.5下，如果CSS中对同一个背景图片使用了多次，则对同一个图片会发送多次请求，也就是说没有缓存背景图片。因此，这也就造成了CSS Sprite在Firefox3.5中失效了，具体的例子可以围观这个测试页面。

HTML代码如下
&#60;div class="test1"&#62;Test1&#60;/div&#62;
&#60;div class="test2"&#62;Test2&#60;/div&#62;
CSS代码如下
.test1{
    background: url(http://co.youa.baidu.com/picture/r/mall/xmas09/images/sprites.png) 0 -207px no-repeat;
    width: 600px;
    height: 30px;
}
.test2{
    background: url(http://co.youa.baidu.com/picture/r/mall/xmas09/images/sprites.png) 0 -238px no-repeat;
    width: 270px;
    height: 60px;
}
通过Firebug查看具体请求如下：
 经查证，这是一个已知Bug，仅存在于Firefox3.5中，不过在Firefox3.6版本中已经修复了。
如果无视Firefox3.5则可以到此为止，如果想要Hack这个问题则可以用以下的方式解决：
给每个使用背景图的元素添加一个相同的class name，并在这个class name中定义背景图片并在特定的类名中定义背景图片位置。这个方式也许并不算一个hack，仅仅是设置背景图片的另一个途径罢了，而且在一些情况下还可以节省字节。
修改后HTML代码如下

&#60;div class="test test1"&#62;Test1&#60;/div&#62;
&#60;div class="test test2"&#62;Test2&#60;/div&#62;
修改后CSS代码如下

.test{
    background: url(http://co.youa.baidu.com/picture/r/mall/xmas09/images/sprites.png) no-repeat;
}
.test1{
    background-position:0 -207px;
    width: 600px;
    height: 30px;
}
.test2{
    background-position:0 -238px;
    width: 270px;
    height: 60px;
}
通过Firebug查看具体请求如下：

修改后的测试页面在此。
]]></description>
			<content:encoded><![CDATA[<p>今天猛然发现在Firefox3.5下，如果CSS中对同一个背景图片使用了多次，则对同一个图片会发送多次请求，也就是说没有缓存背景图片。因此，这也就造成了CSS Sprite在Firefox3.5中失效了，具体的例子可以围观这个<a href="http://varnow.org/pages/firefox-3-5-css-image-bug.html" target="_blank">测试页面</a>。</p>
<p><span id="more-241"></span></p>
<p>HTML代码如下</p>
<pre name="code" class="html">&lt;div class="test1"&gt;Test1&lt;/div&gt;
&lt;div class="test2"&gt;Test2&lt;/div&gt;</pre>
<p>CSS代码如下</p>
<pre name="code" class="css">.test1{
    background: url(<a href="http://co.youa.baidu.com/picture/r/mall/xmas09/images/sprites.png)">http://co.youa.baidu.com/picture/r/mall/xmas09/images/sprites.png)</a> 0 -207px no-repeat;
    width: 600px;
    height: 30px;
}
.test2{
    background: url(<a href="http://co.youa.baidu.com/picture/r/mall/xmas09/images/sprites.png)">http://co.youa.baidu.com/picture/r/mall/xmas09/images/sprites.png)</a> 0 -238px no-repeat;
    width: 270px;
    height: 60px;
}</pre>
<p>通过Firebug查看具体请求如下：</p>
<p><a href="http://varnow.org/wordpress/wp-content/uploads/2010/02/image14.png" rel="lightbox[241]"><img style="border-top-width: 0px; display: block; border-left-width: 0px; float: none; border-bottom-width: 0px; margin-left: auto; margin-right: auto; border-right-width: 0px" title="image" src="http://varnow.org/wordpress/wp-content/uploads/2010/02/image-thumb14.png" border="0" alt="image" width="244" height="54" /></a> 经查证，这是一个<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=497665" target="_blank">已知Bug</a>，仅存在于Firefox3.5中，不过在Firefox3.6版本中已经修复了。</p>
<p>如果无视Firefox3.5则可以到此为止，如果想要Hack这个问题则可以用以下的方式解决：</p>
<p>给每个使用背景图的元素添加一个相同的class name，并在这个class name中定义背景图片并在特定的类名中定义背景图片位置。这个方式也许并不算一个hack，仅仅是设置背景图片的另一个途径罢了，而且在一些情况下还可以节省字节。</p>
<p>修改后HTML代码如下</p>
<pre name="code" class="html">
&lt;div class="test test1"&gt;Test1&lt;/div&gt;
&lt;div class="test test2"&gt;Test2&lt;/div&gt;</pre>
<p>修改后CSS代码如下</p>
<pre name="code" class="css">
.test{
    background: url(<a href="http://co.youa.baidu.com/picture/r/mall/xmas09/images/sprites.png)">http://co.youa.baidu.com/picture/r/mall/xmas09/images/sprites.png)</a> no-repeat;
}
.test1{
    background-position:0 -207px;
    width: 600px;
    height: 30px;
}
.test2{
    background-position:0 -238px;
    width: 270px;
    height: 60px;
}</pre>
<p>通过Firebug查看具体请求如下：</p>
<p align="center"><a href="http://varnow.org/wordpress/wp-content/uploads/2010/02/image15.png" rel="lightbox[241]"><img style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" title="image" src="http://varnow.org/wordpress/wp-content/uploads/2010/02/image-thumb15.png" border="0" alt="image" width="244" height="30" /></a></p>
<p>修改后的<a href="http://varnow.org/pages/firefox-3-5-css-image-bug-fixed.html" target="_blank">测试页面</a>在此。</p>
]]></content:encoded>
			<wfw:commentRss>http://varnow.org/?feed=rss2&amp;p=241</wfw:commentRss>
		</item>
		<item>
		<title>如何减少浏览器的repaint和reflow?</title>
		<link>http://varnow.org/?p=232</link>
		<comments>http://varnow.org/?p=232#comments</comments>
		<pubDate>Mon, 01 Feb 2010 07:24:51 +0000</pubDate>
		<dc:creator>admin</dc:creator>
		
		<category><![CDATA[WEB优化]]></category>

		<category><![CDATA[reflow]]></category>

		<category><![CDATA[repaint]]></category>

		<category><![CDATA[性能优化]]></category>

		<guid isPermaLink="false">http://varnow.org/?p=232</guid>
		<description><![CDATA[文本内容主要包括以下几点：

什么是repaint/reflow?
什么情况下会触发浏览器的repaint/reflow?
浏览器自身所作的优化
如何优化你的脚本来减少repaint/reflow?


一、什么是repaint/reflow?
页面在加载的过程中，需要对文档结构进行解析，同时需要结合各种各样的样式来计算这个页面长什么样子，最后再经过浏览器的渲染页面就出现了。这整个过程细说起来还是比较复杂，其中充满了repaint和reflow。对于DOM结构中的各个元素都有自己的盒子（模型），这些都需要浏览器根据各种样式（浏览器的、开发人员定义的等）来计算并根据计算结果将元素放到它该出现的位置，这个过程称之为reflow；当各种盒子的位置、大小以及其他属性，例如颜色、字体大小等都确定下来后，浏览器于是便把这些元素都按照各自的特性绘制了一遍，于是页面的内容出现了，这个过程称之为repaint。
以上提到的只是在页面加载时必然会出现的repaint和reflow，除此之外，在页面加载完成后，用户的一些操作、脚本的一些操作都会导致浏览器发生这种行为，具体在后文阐述。
另外，关于浏览器渲染的更为详细的资料可以参考以下，涵盖了IE以及Firefox：
Understanding Internet Explorer Rendering Behaviour
Notes on HTML Reflow
二、什么情况下会触发浏览器的repaint/reflow?
除了页面在首次加载时必然要经历该过程之外，还有以下行为会触发这个行为：

DOM元素的添加、修改（内容）、删除( Reflow + Repaint)
仅修改DOM元素的字体颜色（只有Repaint，因为不需要调整布局）
应用新的样式或者修改任何影响元素外观的属性
Resize浏览器窗口、滚动页面
读取元素的某些属性（offsetLeft、offsetTop、offsetHeight、offsetWidth、scrollTop/Left/Width/Height、clientTop/Left/Width/Height、getComputedStyle()、currentStyle(in IE))

在继续下面的文章之前，先介绍一款强大的性能分析工具-dynaTrace，借助该功能能够清晰的得到页面中的资源消耗情况，从而对症下药。另外，更细节的方面是它可以跟踪每个函数调用所造成的CPU消耗、Repaint/Reflow。接下来就借助该工具来测试一下以上描述的几点情况。
DOM元素的增删改
先看代码
&#60;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3c.org/TR/html4/strict.dtd"&#62;
&#60;html&#62;
    &#60;body&#62;
        &#60;div id="test1" onclick="addNode()"&#62;这里是第1个节点&#60;/div&#62;
        &#60;div id="test2" onclick="modNode()"&#62;这里是第2个节点&#60;/div&#62;
        &#60;div id="test3" onclick="delNode()"&#62;这里是第3个节点&#60;/div&#62;
    [...]]]></description>
			<content:encoded><![CDATA[<p>文本内容主要包括以下几点：</p>
<ol>
<li>什么是repaint/reflow?</li>
<li>什么情况下会触发浏览器的repaint/reflow?</li>
<li>浏览器自身所作的优化</li>
<li>如何优化你的脚本来减少repaint/reflow?</li>
</ol>
<p><span id="more-232"></span></p>
<h4>一、什么是repaint/reflow?</h4>
<p>页面在加载的过程中，需要对文档结构进行解析，同时需要结合各种各样的样式来计算这个页面长什么样子，最后再经过浏览器的渲染页面就出现了。这整个过程细说起来还是比较复杂，其中充满了repaint和reflow。对于DOM结构中的各个元素都有自己的盒子（模型），这些都需要浏览器根据各种样式（浏览器的、开发人员定义的等）来计算并根据计算结果将元素放到它该出现的位置，这个过程称之为reflow；当各种盒子的位置、大小以及其他属性，例如颜色、字体大小等都确定下来后，浏览器于是便把这些元素都按照各自的特性绘制了一遍，于是页面的内容出现了，这个过程称之为repaint。</p>
<p>以上提到的只是在页面加载时必然会出现的repaint和reflow，除此之外，在页面加载完成后，用户的一些操作、脚本的一些操作都会导致浏览器发生这种行为，具体在后文阐述。</p>
<p>另外，关于浏览器渲染的更为详细的资料可以参考以下，涵盖了IE以及Firefox：</p>
<p><a href="http://blog.dynatrace.com/2009/12/12/understanding-internet-explorer-rendering-behaviour/" target="_blank">Understanding Internet Explorer Rendering Behaviour</a></p>
<p><a href="http://www.mozilla.org/newlayout/doc/reflow.html" target="_blank">Notes on HTML Reflow</a></p>
<h4>二、什么情况下会触发浏览器的repaint/reflow?</h4>
<p>除了页面在首次加载时必然要经历该过程之外，还有以下行为会触发这个行为：</p>
<ul>
<li>DOM元素的添加、修改（内容）、删除( Reflow + Repaint)</li>
<li>仅修改DOM元素的字体颜色（只有Repaint，因为不需要调整布局）</li>
<li>应用新的样式或者修改任何影响元素外观的属性</li>
<li>Resize浏览器窗口、滚动页面</li>
<li>读取元素的某些属性（offsetLeft、offsetTop、offsetHeight、offsetWidth、scrollTop/Left/Width/Height、clientTop/Left/Width/Height、getComputedStyle()、currentStyle(in IE))</li>
</ul>
<p>在继续下面的文章之前，先介绍一款强大的性能分析工具-<a href="http://www.dynatrace.com/en/" target="_blank">dynaTrace</a>，借助该功能能够清晰的得到页面中的资源消耗情况，从而对症下药。另外，更细节的方面是它可以跟踪每个函数调用所造成的CPU消耗、Repaint/Reflow。接下来就借助该工具来测试一下以上描述的几点情况。</p>
<h6>DOM元素的增删改</h6>
<p>先看代码</p>
<pre name="code" class="html">&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3c.org/TR/html4/strict.dtd"&gt;
&lt;html&gt;
    &lt;body&gt;
        &lt;div id="test1" onclick="addNode()"&gt;这里是第1个节点&lt;/div&gt;
        &lt;div id="test2" onclick="modNode()"&gt;这里是第2个节点&lt;/div&gt;
        &lt;div id="test3" onclick="delNode()"&gt;这里是第3个节点&lt;/div&gt;
    &lt;/body&gt;
    &lt;script type="text/javascript"&gt;
        function $(id){
            return document.getElementById(id);
        }
        function addNode(){
            var n = document.createElement('div');
            n.innerHTML = 'New Node';
            $('test1').appendChild(n);
        }
        function modNode(){
            $('test2').innerHTML = 'hello';
        }
        function delNode(){
            $('test3').parentNode.removeChild($('test3'));
        }
    &lt;/script&gt;
&lt;/html&gt;</pre>
<p>在依次点击完每一个按钮后，我们来看看dynaTrace的情况，首先是一目了然的点击事件分布</p>
<p><a href="http://varnow.org/wordpress/wp-content/uploads/2010/02/image.png" rel="lightbox[232]"><img style="border-top-width: 0px; display: block; border-left-width: 0px; float: none; border-bottom-width: 0px; margin-left: auto; margin-right: auto; border-right-width: 0px" title="image" src="http://varnow.org/wordpress/wp-content/uploads/2010/02/image-thumb.png" border="0" alt="image" width="244" height="75" /></a></p>
<p>放大之后来看一下每个事件的repaint/reflow情况：</p>
<p>增加节点：</p>
<p align="center"><a href="http://varnow.org/wordpress/wp-content/uploads/2010/02/image1.png" rel="lightbox[232]"><img style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" title="image" src="http://varnow.org/wordpress/wp-content/uploads/2010/02/image-thumb1.png" border="0" alt="image" width="244" height="52" /></a></p>
<p align="left">修改节点：</p>
<p align="center"><a href="http://varnow.org/wordpress/wp-content/uploads/2010/02/image2.png" rel="lightbox[232]"><img style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" title="image" src="http://varnow.org/wordpress/wp-content/uploads/2010/02/image-thumb2.png" border="0" alt="image" width="244" height="51" /></a></p>
<p align="left">删除节点：</p>
<p align="center"><a href="http://varnow.org/wordpress/wp-content/uploads/2010/02/image3.png" rel="lightbox[232]"><img style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" title="image" src="http://varnow.org/wordpress/wp-content/uploads/2010/02/image-thumb3.png" border="0" alt="image" width="244" height="55" /></a></p>
<p align="left">图中的绿色部分表示的是reflow和repaint过程，其中比较短的绿条标示的reflow过程，后面长条部分表示的是repaint过程。从图中可以看出，对DOM节点的增删改都会造成reflow和repaint，由于改动小所以reflow消耗的时间很短，但是由于repaint是全局的，因此消耗的时间都比较长。</p>
<h5>修改DOM元素前景色</h5>
<pre name="code" class="javascript">var n = $('colorNode');
n.style.color = 'red';</pre>
<p align="center"><a href="http://varnow.org/wordpress/wp-content/uploads/2010/02/image4.png" rel="lightbox[232]"><img style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" title="image" src="http://varnow.org/wordpress/wp-content/uploads/2010/02/image-thumb4.png" border="0" alt="image" width="244" height="107" /></a></p>
<p align="left">从上图中可以看到修改字体颜色后，浏览器只有repaint而没有reflow。接下来试试修改背景色：</p>
<pre name="code" class="javascript">var n = $('colorNode');
n.style.backgroundColor = 'red';</pre>
<p align="center"><a href="http://varnow.org/wordpress/wp-content/uploads/2010/02/image5.png" rel="lightbox[232]"><img style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" title="image" src="http://varnow.org/wordpress/wp-content/uploads/2010/02/image-thumb5.png" border="0" alt="image" width="244" height="116" /></a></p>
<p>由图中可以看出，修改背景色也会造成reflow和repaint。另外，经过测试发现，只要是修改元素的cssText属性，不论它的值是什么，都会导致浏览器reflow和repaint，因此在某些时候选择特定的样式属性赋值会有更好的效果。</p>
<h5>Resize浏览器窗口以及拖动滚动条</h5>
<p align="center"><a href="http://varnow.org/wordpress/wp-content/uploads/2010/02/image6.png" rel="lightbox[232]"><img style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" title="image" src="http://varnow.org/wordpress/wp-content/uploads/2010/02/image-thumb6.png" border="0" alt="image" width="244" height="45" /></a></p>
<p>测试中的操作如下：缩小浏览器窗口-&gt;放大浏览器窗口-&gt;拖动页面滚动条至页面底部。从图中可以看到Resize浏览器窗口以及拖动滚动条都会造成浏览器的repaint，而且CPU的消耗也比较大，尤其是拖动滚动条的时候。</p>
<h5>读取Layout属性</h5>
<p>根据各种参考资料中的描述，在用Javascript读取DOM节点的Layout属性（offsetLeft、offsetTop、offsetHeight、offsetWidth、scrollTop/Left/Width/Height、clientTop/Left/Width/Height、getComputedStyle()、currentStyle(in IE)) 的时候也会触发repaint，不过在以下的测试例子中并没有发现这一点。</p>
<pre name="code" class="javascript">var n = $('colorNode');
var temp = document.documentElement.currentStyle;
temp = n.offsetTop;
temp = n.offsetLeft;
temp = n.offsetWidth;
temp = n.offsetHeight;
temp = n.scrollTop;
temp = n.scrollHeight;
alert(temp);</pre>
<p align="center"><a href="http://varnow.org/wordpress/wp-content/uploads/2010/02/image7.png" rel="lightbox[232]"><img style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" title="image" src="http://varnow.org/wordpress/wp-content/uploads/2010/02/image-thumb7.png" border="0" alt="image" width="244" height="126" /></a></p>
<p align="left">
<h4>三、浏览器优化</h4>
<p>浏览器对于每一个渲染动作并不是立即执行，而是维护了一个渲染任务队列，浏览器会根据具体的需要分批集中执行其中的任务。除了浏览器自身维护的定期调度之外，脚本中的某些操作会导致浏览器立即执行渲染任务，例如读取元素的Layout属性。</p>
<pre name="code" class="javascript">var bodystyle = document.body.style;
var computed;
if (document.body.currentStyle) {
  computed = document.body.currentStyle;
} else {
  computed = document.defaultView.getComputedStyle(document.body, '');
}

//每次都读取

bodystyle.color = 'red';
bodystyle.padding = '1px';
tmp = computed.backgroundColor;
bodystyle.color = 'white';
bodystyle.padding = '2px';
tmp = computed.backgroundImage;
bodystyle.color = 'green';
bodystyle.padding = '3px';
tmp = computed.backgroundAttachment;

//最后再读取

bodystyle.color = 'yellow';
bodystyle.padding = '4px';
bodystyle.color = 'pink';
bodystyle.padding = '5px';
bodystyle.color = 'blue';
bodystyle.padding = '6px';
tmp = computed.backgroundColor;
tmp = computed.backgroundImage;
tmp = computed.backgroundAttachment;</pre>
<p>每次读取的渲染图：</p>
<p><a href="http://varnow.org/wordpress/wp-content/uploads/2010/02/image8.png" rel="lightbox[232]"><img style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" title="image" src="http://varnow.org/wordpress/wp-content/uploads/2010/02/image-thumb8.png" border="0" alt="image" width="244" height="54" /></a></p>
<p>最后读取的渲染图：</p>
<p><a href="http://varnow.org/wordpress/wp-content/uploads/2010/02/image9.png" rel="lightbox[232]"><img style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" title="image" src="http://varnow.org/wordpress/wp-content/uploads/2010/02/image-thumb9.png" border="0" alt="image" width="244" height="52" /></a></p>
<h4>四、如何优化你的脚本来减少reflow/repaint?</h4>
<p>1. 避免在document上直接进行频繁的DOM操作，如果确实需要可以采用off-document的方式进行，具体的方法包括但不完全包括以下几种：</p>
<blockquote><p>(1). 先将元素从document中删除，完成修改后再把元素放回原来的位置</p>
<p>(2). 将元素的display设置为”none”，完成修改后再把display修改为原来的值</p>
<p>(3). 如果需要创建多个DOM节点，可以使用DocumentFragment创建完后一次性的加入document</p></blockquote>
<pre name="code" class="javascript">function appendEveryTime(){
    for( var i = 5000; i--; ){
        var n = document.createElement('div');
        n.innerHTML = 'node ' + i;
        document.body.appendChild(n);/*每次创建的新节点都append到文档*/
    }
}

function appendLast(){
     var frag = document.createDocumentFragment();
     for( var i = 5000; i--; ){
         var n = document.createElement('div');
         n.innerHTML = 'node ' + i;
         frag.appendChild(n);/*每次创建的节点先放入DocumentFragment中*/
     }
     document.body.appendChild(frag);
}</pre>
<p>用dynaTrace观察的结果如下，appendLast的性能无论是在Javascript的执行时间以及浏览器渲染时间方面都优于appendEveryTime。</p>
<p>appendEveryTime:</p>
<p align="center"><a href="http://varnow.org/wordpress/wp-content/uploads/2010/02/image10.png" rel="lightbox[232]"><img style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" title="image" src="http://varnow.org/wordpress/wp-content/uploads/2010/02/image-thumb10.png" border="0" alt="image" width="244" height="35" /></a></p>
<p>appendLast:</p>
<p align="center"><a href="http://varnow.org/wordpress/wp-content/uploads/2010/02/image11.png" rel="lightbox[232]"><img style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" title="image" src="http://varnow.org/wordpress/wp-content/uploads/2010/02/image-thumb11.png" border="0" alt="image" width="244" height="37" /></a></p>
<p align="left">2. 集中修改样式</p>
<blockquote>
<p align="left">(1). 尽可能少的修改元素style上的属性</p>
<p align="left">(2). 尽量通过修改className来修改样式</p>
<p align="left">(3). 通过cssText属性来设置样式值</p>
</blockquote>
<p>如下的代码中，每一次赋值都会造成浏览器重新渲染，可以采用cssText或者className的方式</p>
<pre name="code" class="javascript">el.style.color = 'red;
el.style.height = '100px';
el.style.fontSize = '12px';
el.style.backgroundColor = 'white';</pre>
<p>3. 缓存Layout属性值</p>
<p>对于Layout属性中非引用类型的值（数字型），如果需要多次访问则可以在一次访问时先存储到局部变量中，之后都使用局部变量，这样可以避免每次读取属性时造成浏览器的渲染。</p>
<pre name="code" class="javascript">var width = el.offsetWidth;
var scrollLeft = el.scrollLeft;</pre>
<p>4. 设置元素的position为absolute或fixed</p>
<p>在元素的position为static和relative时，元素处于DOM树结构当中，当对元素的某个操作需要重新渲染时，浏览器会渲染整个页面。将元素的position设置为absolute和fixed可以使元素从DOM树结构中脱离出来独立的存在，而浏览器在需要渲染时只需要渲染该元素以及位于该元素下方的元素，从而在某种程度上缩短浏览器渲染时间，这在当今越来越多的Javascript动画方面尤其值得考虑。</p>
<pre name="code" class="html">&lt;body style="position:relative"&gt;
	&lt;div id="test" style="background-color:red;width:100px;position:relative;"&gt;Animation Here&lt;/div&gt;
&lt;/body&gt;
&lt;script type="text/javascript"&gt;
	function $(id){
		return document.getElementById(id);
	}
	window.onload = function(){
		var t = $('test');

		~function(){
			t.style.left = t.offsetLeft + 5 + 'px';
			t.style.height = t.offsetHeight + 5 + 'px';
			setTimeout(arguments.callee,500);
		}();
	}
&lt;/script&gt;</pre>
<p>通过修改#test元素的postion为relative和postion分别得到如下两个测试结果</p>
<p>position: relative</p>
<p align="center"><a href="http://varnow.org/wordpress/wp-content/uploads/2010/02/image12.png" rel="lightbox[232]"><img style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" title="image" src="http://varnow.org/wordpress/wp-content/uploads/2010/02/image-thumb12.png" border="0" alt="image" width="244" height="30" /></a></p>
<p>position: absolute</p>
<p align="center"><a href="http://varnow.org/wordpress/wp-content/uploads/2010/02/image13.png" rel="lightbox[232]"><img style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" title="image" src="http://varnow.org/wordpress/wp-content/uploads/2010/02/image-thumb13.png" border="0" alt="image" width="244" height="38" /></a></p>
<p align="left">在postion:relative的测试当中，浏览器在重新渲染时做的工作比position:absolute多了不少。</p>
<p>参考资料</p>
<p><a href="http://blog.dynatrace.com/2009/12/12/understanding-internet-explorer-rendering-behaviour/" target="_blank">Understanding Internet Explorer Rendering Behaviour</a></p>
<p><a href="http://www.mozilla.org/newlayout/doc/reflow.html" target="_blank">Notes on HTML Reflow</a></p>
<p><a href="http://dev.opera.com/articles/view/efficient-javascript/?page=3#reflow" target="_blank">EFFICIENT JAVASCRIPT</a></p>
]]></content:encoded>
			<wfw:commentRss>http://varnow.org/?feed=rss2&amp;p=232</wfw:commentRss>
		</item>
		<item>
		<title>Javascript预解析相关一则</title>
		<link>http://varnow.org/?p=197</link>
		<comments>http://varnow.org/?p=197#comments</comments>
		<pubDate>Sun, 31 Jan 2010 06:27:58 +0000</pubDate>
		<dc:creator>admin</dc:creator>
		
		<category><![CDATA[Javascript]]></category>

		<category><![CDATA[预解析]]></category>

		<guid isPermaLink="false">http://varnow.org/?p=197</guid>
		<description><![CDATA[先来看一段代码：
alert("a" in window);    /*true    -    1*/
if( false ){
    var a = 1;
}
alert("b" in window);    /*false    -    2*/
if( false ){
    b = 1;
}
alert("b" in window);    /*false    [...]]]></description>
			<content:encoded><![CDATA[<p>先来看一段代码：</p>
<pre name="code" class="javascript">alert("a" in window);    /*true    -    1*/
if( false ){
    var a = 1;
}
alert("b" in window);    /*false    -    2*/
if( false ){
    b = 1;
}
alert("b" in window);    /*false    -    3*/
alert("c" in window);    /*false    -    4*/
if( true ){
    c = 1;
}
alert("c" in window);    /*true    -    5*/</pre>
<p>以上代码片段是在全局作用域下定义的，对于第一个例子，Javascript在预解析的时候已经将变量a的声明添加到了活动对象中（具体可参考<a href="http://varnow.org/?p=171" target="_blank">Javascript的“预解析</a>），于是在运行时 &#8220;a&#8221; in window 为true。</p>
<p><span id="more-197"></span></p>
<p>然后第二个例子的结果是false，也就是说变量&#8221;b&#8221;在预解析的过程中并没有被放置在当前环境的活动对象中，原因在于没有使用var来定义变量&#8221;b&#8221;。对于使用了var定义的变量，预解析时可以明确的知道这是当前作用域的“本地”变量，因此可以准确无误的将变量添加到活动对象中，而那些没有使用var定义的变量则需要在运行时去处理。示例中的2 - 5句正好验证了没有使用var定义的变量需要在运行时进行处理。</p>
<p>在<a href="http://www.nczonline.net/blog/2010/01/26/answering-baranovskiys-javascript-quiz/" target="_blank">Answering Baranovskiy’s JavaScript quiz</a>一文中给出的前3个问题均与预解析相关，如下：</p>
<pre name="code" class="javascript">if (!("a" in window)) {
    var a = 1;
}
alert(a);/*undefined*/</pre>
<p>预解析时已经将变量a添加到window上，因此!(&#8221;a&#8221; in window)为false，导致运行时a没有被赋值，所以a的值为undefined。</p>
<pre name="code" class="javascript">var a = 1,
    b = function a(x) {
        x &amp;&amp; a(--x);
    };
alert(a);/*1*/</pre>
<p>这个在所有浏览器下都输出1，预解析时，第一行定义的变量a被添加到了window对象中，此时它的值为undefined，解析到第二行时，变量b也被添加到window对象中，其值也为undefined。同时，在IE下，后面的函数表达式也会被当做一个函数定义来处理，即相当于</p>
<pre name="code" class="javascript">var a = 1;
var b = function (x){
   x &amp;&amp; a(—x);
};
function a(x) {  /*在IE下会被当做函数定义处理*/
    x &amp;&amp; a(--x);
}
alert(a);/*1*/</pre>
<p>当解析过程完成后，window对象中的变量a的引用是一个函数定义（仅限于IE），而到了运行时给变量赋值的时候，执行了以下3个动作：</p>
<p>1. 给变量a赋值为1（覆盖了原先a上面的函数定义）</p>
<p>2. 给变量b赋值为函数表达式</p>
<p>3. alert(a)</p>
<p>因此a的值为1。</p>
<pre name="code" class="javascript">function a(x) {
    return x * 2;
}
var a;
alert(a);/*函数代码*/</pre>
<p>预解析时，完成了函数a的定义，即在window对象上添加变量a，并赋值为函数定义。之后遇到&#8221;var a;&#8221;，由于当前作用域已经有变量a，因此不需要做什么工作。在运行时，由于并没有给变量a赋值，因此原来的函数定义并没有覆盖，因此最终变量a是一个函数。如果将&#8221;var a;&#8221;改为&#8221;var a=1;&#8221;，则最终a的值为1。</p>
]]></content:encoded>
			<wfw:commentRss>http://varnow.org/?feed=rss2&amp;p=197</wfw:commentRss>
		</item>
	</channel>
</rss>
