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

<channel>
	<title>雨夜带刀&#039;s Blog 关注前端与用户体验</title>
	<atom:link href="http://stylechen.com/feed" rel="self" type="application/rss+xml" />
	<link>http://stylechen.com</link>
	<description></description>
	<lastBuildDate>Sat, 07 Apr 2012 14:28:25 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
	
<!-- Start Of Script Generated By WP-PostViews Plus -->
<script type="text/javascript">
/* <![CDATA[ */
/* ]]> */
</script>
<!-- End Of Script Generated By WP-PostViews Plus -->
	<item>
		<title>让document.write的广告无阻塞的加载</title>
		<link>http://stylechen.com/rewrite-documentwrite.html</link>
		<comments>http://stylechen.com/rewrite-documentwrite.html#comments</comments>
		<pubDate>Mon, 02 Apr 2012 07:13:38 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[前端优化]]></category>
		<category><![CDATA[jQuery插件]]></category>
		<category><![CDATA[lazyload]]></category>
		<category><![CDATA[图片加载]]></category>
		<category><![CDATA[队列]]></category>

		<guid isPermaLink="false">http://stylechen.com/?p=516</guid>
		<description><![CDATA[<h3>广告代码分析</h3>
<p>很多第三方的广告系统都是使用document.write来加载广告，如下面的一个javascript的广告链接。</p>
<pre class="brush: c-sharp">
&#60;script type="text/javascript" src="http://gg.5173.com/adpolestar/5173/
;ap=2EBE5681_1BA3_4663_FA3F_E73D2B83FBDC;ct=js;pu=5173;/?"&#62;&#60;/script&#62;
</pre>
<p>这个javascript请求返回的是这样的一段代码：</p>
<pre class="brush: c-sharp">
document.write( "&#60;a href='http://gg.5173.com/adpolestar/wayl/;" + 
"ad=6FF3F844_33E6_86EE_3B96_D94C1CF1AEC4;ap=2EBE5681_1BA3_4663_FA3F_E73D2B83FBDC;" + 
"pu=5173;/?http://www.7bao.com/g/xlsbz/index' target='_blank'&#62;&#60;img src='" +
"http://html.5173cdn.com/market/yunyinga/xly132.gif' " +
"border='0' width="132px" height="58px" /&#62;&#60;/a&#62;" );
</pre>
<p>这种看似有点二的加载方式，但是你却没办法改造它，因为它本身就是第三方的。并且代码都添加了统计的功能，上面的javascript的广告链接每请求一次都会统计一次，生成的代码也有点击统计的功能，也就是说必须以这种方式来进行加载。</p>
<p>document.write是在页面渲染的时候同步进行的，必须要等javascript代码下载好并且document.write执行完后才接着渲染后面的内容，如果广告比较多的话，就会导致页面阻塞，尤其是在页面的首屏插好几个图片尺寸比较大的这种广告，那么阻塞情况就相当明显和严重，会让用户觉得你这个网页很慢。</p>
<div class="art_inner_img"><img src="http://stylechen.com/wp-content/uploads/2012/04/1.png" alt="javascript广告的阻塞加载" title="rewrite-documentwrite" width="265" height="260" /></div>]]></description>
			<content:encoded><![CDATA[<h3>广告代码分析</h3>
<p>很多第三方的广告系统都是使用document.write来加载广告，如下面的一个javascript的广告链接。</p>
<pre class="brush: c-sharp">
&lt;script type="text/javascript" src="http://gg.5173.com/adpolestar/5173/
;ap=2EBE5681_1BA3_4663_FA3F_E73D2B83FBDC;ct=js;pu=5173;/?"&gt;&lt;/script&gt;
</pre>
<p>这个javascript请求返回的是这样的一段代码：</p>
<pre class="brush: c-sharp">
document.write( "&lt;a href='http://gg.5173.com/adpolestar/wayl/;" +
"ad=6FF3F844_33E6_86EE_3B96_D94C1CF1AEC4;ap=2EBE5681_1BA3_4663_FA3F_E73D2B83FBDC;" +
"pu=5173;/?http://www.7bao.com/g/xlsbz/index' target='_blank'&gt;&lt;img src='" +
"http://html.5173cdn.com/market/yunyinga/xly132.gif' " +
"border='0' width="132px" height="58px" /&gt;&lt;/a&gt;" );
</pre>
<p>这种看似有点二的加载方式，但是你却没办法改造它，因为它本身就是第三方的。并且代码都添加了统计的功能，上面的javascript的广告链接每请求一次都会统计一次，生成的代码也有点击统计的功能，也就是说必须以这种方式来进行加载。</p>
<p>document.write是在页面渲染的时候同步进行的，必须要等javascript代码下载好并且document.write执行完后才接着渲染后面的内容，如果广告比较多的话，就会导致页面阻塞，尤其是在页面的首屏插好几个图片尺寸比较大的这种广告，那么阻塞情况就相当明显和严重，会让用户觉得你这个网页很慢。</p>
<div class="art_inner_img"><img src="http://stylechen.com/wp-content/uploads/2012/04/1.png" alt="javascript广告的阻塞加载" title="rewrite-documentwrite" width="265" height="260" /></div>
<h3>重写document.write</h3>
<p>为了避免阻塞，就不能让document.write方法在页面渲染的时候执行，必须想办法让javascript的广告代码在DOM树就绪(DOM ready)之后才执行，但是在DOM树就绪后执行document.write会重新渲染整个页面，这样也是不行的。document.write虽然是浏览器原生的方法，但是也可以自定义一个方法来覆盖掉原来的方法。在javascript广告代码加载之前，重写document.write，等加载并执行完再改回来。</p>
<div class="art_inner_img"><img src="http://stylechen.com/wp-content/uploads/2012/04/2.png" alt="javascript广告无阻塞加载" title="renwrite-documentwrite2" width="271" height="384" /></div>
<h3>延迟加载javascript代码</h3>
<p>上面比较关键的一步，延迟加载javascript代码，如何实现呢？先尝试通过改写script的type属性，比如将type设置成一个自定义的属性&#8221;type/cache&#8221;，但这样大部分浏览器(Chrome不会下载)仍然会下载这段代码，但不会执行，在页面渲染的时候下载这么一段代码仍然会阻塞，通过改写script的type并不能实现真正的延迟加载，最多能实现只加载不执行，而且还存在兼容问题。</p>
<p>将script标签放到textarea标签中，等需要加载的时候再读取textarea的内容，这样可以实现真正的延迟加载script，这个方法要感谢玉伯提出的<a href="http://lifesinger.wordpress.com/2011/09/23/bigrender-for-taobao-item/" target="_blank">BigRender</a>(墙外)方案。</p>
<pre class="brush: c-sharp">
&lt;div&gt;
&lt;textarea style="display:none"&gt;
&lt;script type="text/javascript" src="http://gg.5173.com/adpolestar/5173/
;ap=2EBE5681_1BA3_4663_FA3F_E73D2B83FBDC;ct=js;pu=5173;/?"&gt;&lt;/script&gt;
&lt;/textarea&gt;
&lt;/div&gt;
</pre>
<p>延迟加载script并重写document.write，下面是代码实现：</p>
<pre class="brush: c-sharp">
/**
 * 重写document.write实现无阻塞加载script
 * @param { Dom Object } textarea元素
 */
var loadScript = function( elem ){
	var url = elem.value.match( /src="([\s\S]*?)"/i )[1],
		parent = elem.parentNode,
		// 缓存原生的document.write
		docWrite = document.write,
		// 创建一个新script来加载
		script = document.createElement( 'script' ),
		head = document.head ||
			document.getElementsByTagName( 'head' )[0] ||
			document.documentElement;

	// 重写document.write
	document.write = function( text ){
		parent.innerHTML = text;
	};

	script.type = 'text/javascript';
	script.src = url;

	script.onerror =
	script.onload =
	script.onreadystatechange = function( e ){
		e = e || window.event;
		if( !script.readyState ||
		/loaded|complete/.test(script.readyState) ||
		e === 'error'
		){

			// 恢复原生的document.write
			document.write = docWrite;
			head.removeChild( script );

			// 卸载事件和断开DOM的引用
			// 尽量避免内存泄漏
			head =
			parent =
			elem =
			script =
			script.onerror =
			script.onload =
			script.onreadystatechange = null;

		}
	}

	// 加载script
	head.insertBefore( script, head.firstChild );
};
</pre>
<h3>图片延迟加载的增强版</h3>
<p>实现了无阻塞式的延迟加载javascript广告代码，能否进一步优化？如果广告没在首屏出现，能否像通常的<a href="http://stylechen.com/imglazyload2.html">图片的延迟加载</a>一样来进行延迟加载？答案是肯定的。对我之前写的图片延迟加载的小插件进行扩展，将原来的图片加载方式(替换src)改成上面的loadScript方式加载就可以实现。当然，仅仅是这样的修改还是会有问题的。如果有多个图片，并且loadScript是同时进行的，而document.write又是全局的方法，保不准在加载A的时候不影响到B，必须让它们一个个的按顺序加载，加载完A之后才能加载B。</p>
<h3>队列控制</h3>
<p>为了让javascript广告代码按顺序加载就需要一个队列来控制加载。于是又有了下面这段简单的队列控制代码：</p>
<pre class="brush: c-sharp">
var loadQueue = [];
// 入列
var queue = function( data ){
	loadQueue.push( data );
	if( loadQueue[0] !== 'runing' ){
		dequeue();
	}
};
// 出列
var dequeue = function(){
	var fn = loadQueue.shift();
	if( fn === 'runing' ){
		fn = loadQueue.shift();
	}

	if( fn ){
		loadQueue.unshift( 'runing' );
		fn();
	}
};
</pre>
<div class="demo"><a href="http://stylechen.com/wp-content/uploads/demo/imglazyload/loadscript.html" target="_blank">查看演示</a></div>
<p>图片延迟加载插件的使用说明：<a href="http://stylechen.com/imglazyload2.html">http://stylechen.com/imglazyload2.html</a></p>
<p>图片延迟加载的增强版插件下载地址：<a href="http://stylechen.com/wp-content/uploads/download/imglazyload.zip">http://stylechen.com/wp-content/uploads/download/imglazyload.zip</a></p>
<div id="art_copy">原载于：雨夜带刀&#39;s Blog<br />
本文链接：<a title="让document.write的广告无阻塞的加载" href="http://stylechen.com/rewrite-documentwrite.html">http://stylechen.com/rewrite-documentwrite.html</a><br />
如需转载请以链接形式注明原载或原文地址。
</div>
]]></content:encoded>
			<wfw:commentRss>http://stylechen.com/rewrite-documentwrite.html/feed</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>IE6中请求莫名中断</title>
		<link>http://stylechen.com/ie6-aborted-ajax.html</link>
		<comments>http://stylechen.com/ie6-aborted-ajax.html#comments</comments>
		<pubDate>Sat, 17 Mar 2012 03:21:10 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[ajax]]></category>
		<category><![CDATA[默认事件]]></category>

		<guid isPermaLink="false">http://stylechen.com/?p=508</guid>
		<description><![CDATA[<p>场景还原：给a标签绑定了一个click事件用来触发ajax请求，在IE6中，请求时常会被中断，在非IE6中都一切正常。</p>
<pre class="brush: c-sharp"> 
&#60;a href="javascript:;" id="btn"&#62;click me&#60;/a&#62; 
&#60;script type="text/javascript" src="jquery.js"&#62;&#60;/script&#62; 
&#60;script type="text/javascript"&#62; 
&#160;&#160;var url = 'http://api.flickr.com/services/' + 
&#160;&#160;&#160;&#160;'feeds/photos_public.gne?tags=car&#38;' + 
&#160;&#160;&#160;&#160;'tagmode=any&#38;format=json&#38;jsoncallback=?'; 
&#160;&#160;$( '#btn' ).click(function(){ 
&#160;&#160;&#160;&#160;$.getJSON( url, function( data ){ 
&#160;&#160;&#160;&#160;&#160;&#160;alert( data ); 
&#160;&#160;&#160;&#160;}); &#160;&#160;}); 
&#60;/script&#62;
</pre>]]></description>
			<content:encoded><![CDATA[<p>场景还原：给a标签绑定了一个click事件用来触发ajax请求，在IE6中，请求时常会被中断，在非IE6中都一切正常。</p>
<pre class="brush: c-sharp">
&lt;a href="javascript:;" id="btn"&gt;click me&lt;/a&gt;
&lt;script type="text/javascript" src="jquery.js"&gt;&lt;/script&gt;
&lt;script type="text/javascript"&gt;
&nbsp;&nbsp;var url = 'http://api.flickr.com/services/' +
&nbsp;&nbsp;&nbsp;&nbsp;'feeds/photos_public.gne?tags=car&amp;' +
&nbsp;&nbsp;&nbsp;&nbsp;'tagmode=any&amp;format=json&amp;jsoncallback=?';
&nbsp;&nbsp;$( '#btn' ).click(function(){
&nbsp;&nbsp;&nbsp;&nbsp;$.getJSON( url, function( data ){
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;alert( data );
&nbsp;&nbsp;&nbsp;&nbsp;}); &nbsp;&nbsp;});
&lt;/script&gt;
</pre>
<p>在IE6中使用Fiddler2监视请求，经常会出现&#8217;aborted&#8217;，折腾了许久，相当的诡异。后来将a标签换成button，请求正常，最后经提醒，可能是a标签的默认事件中断了请求。但是，在HTML代码中，已经将a标签的href设置成了&#8221;javascript:;&#8221;，通常这样是可以阻止默认事件了(页面跳转)。a标签的click事件会先执行，然后才执行href的跳转，如果href是一段javascript语句，这个时候就会执行。IE6在执行href的javascript语句时就中断了click触发的ajax请求。使用href=&#8221;javascript:;&#8221;是为了阻止默认事件，将阻止默认事件的动作移到click事件中就可以解决问题了，这样就不会执行href中的javascript语句了。</p>
<pre class="brush: c-sharp">
$( '#btn' ).click(function(e){
	$.getJSON( url, function( data ){
		alert( data );
	});
	e.preventDefault();
});
</pre>
<div id="art_copy">原载于：雨夜带刀&#39;s Blog<br />
本文链接：<a title="IE6中请求莫名中断" href="http://stylechen.com/ie6-aborted-ajax.html">http://stylechen.com/ie6-aborted-ajax.html</a><br />
如需转载请以链接形式注明原载或原文地址。
</div>
]]></content:encoded>
			<wfw:commentRss>http://stylechen.com/ie6-aborted-ajax.html/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>图片延迟加载的实现</title>
		<link>http://stylechen.com/imglazyload2.html</link>
		<comments>http://stylechen.com/imglazyload2.html#comments</comments>
		<pubDate>Fri, 02 Mar 2012 02:15:29 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[jQuery]]></category>
		<category><![CDATA[jQuery插件]]></category>
		<category><![CDATA[lazyload]]></category>
		<category><![CDATA[图片加载]]></category>

		<guid isPermaLink="false">http://stylechen.com/?p=489</guid>
		<description><![CDATA[<p>图片延迟加载也称懒加载，通常应用于图片比较多的网页，如果一个页面图片比较多，且页面高度或宽度有好几屏，页面初次加载时，只显示可视区域的图片，当页面滚动的时候，图片进入了可视区域再进行加载，这样可以显著的提高页面的加载速度，更少的图片并发请求数也可以减轻服务器的压力。如果用户仅仅在首屏停留，还可以节省流量。如果TAB中的图片较多，也同样可以应用于TAB中，当触发TAB时再进行图片的加载。</p>
<p>图片延迟加载的原理比较简单，先将图片的真实地址缓存在一个自定义的属性(lazy-src)中，而src地址使用一个1×1的全透明的占位图片来代替，当然占位图片也可以是其他的图片...</p>]]></description>
			<content:encoded><![CDATA[<p>图片延迟加载也称懒加载，通常应用于图片比较多的网页，如果一个页面图片比较多，且页面高度或宽度有好几屏，页面初次加载时，只显示可视区域的图片，当页面滚动的时候，图片进入了可视区域再进行加载，这样可以显著的提高页面的加载速度，更少的图片并发请求数也可以减轻服务器的压力。如果用户仅仅在首屏停留，还可以节省流量。如果TAB中的图片较多，也同样可以应用于TAB中，当触发TAB时再进行图片的加载。</p>
<p>图片延迟加载的原理比较简单，先将图片的真实地址缓存在一个自定义的属性(lazy-src)中，而src地址使用一个1×1的全透明的占位图片来代替，当然占位图片也可以是其他的图片。</p>
<pre class="brush: c-sharp">
&lt;img src="images/placeholder.png"  lazy-src="images/realimg.jpg" /&gt;
</pre>
<p>因为是使用javascript来加载图片，如果用户禁用了javascript，可以设置一个替代的方案。</p>
<pre class="brush: c-sharp">
&lt;img src="images/placeholder.png"  lazy-src="images/realimg.jpg" alt="" /&gt;
&lt;noscript&gt;&lt;img src="images/realimg.jpg"  alt="" /&gt;&lt;/noscript&gt;
</pre>
<p>页面初次加载时获取图片在页面中的位置并缓存(每次取offset的值会引发页面的reflow)，计算出可视区域，当图片的位置出现在可视区域中，将src的值替换成真实的地址，此时图片就开始加载了。</p>
<p>当页面滚动的时候，再判断图片已经缓存的位置值是否出现在可视区域内，进行替换src加载。当所有的图片都加载完之后，将相应的触发事件卸载，避免重复操作引起的内存泄漏。将整个窗口看成是一个大容器，那么也可以在页面中设置一个小容器，在小容器中也同样可以实现图片的延迟加载。</p>
<p>下面是实现的代码，我写成了jQuery插件。</p>
<pre class="brush: c-sharp">
(function( $ ){
$.fn.imglazyload = function( options ){
	var o = $.extend({
				attr		:   'lazy-src',
				container 	: 	window,
				event 		: 	'scroll',
				fadeIn      :   false,
				threshold 	: 	0,
				vertical 	: 	true
			}, options ),

		event = o.event,
		vertical = o.vertical,
		container = $( o.container ),
		threshold = o.threshold,
		// 将jQuery对象转换成DOM数组便于操作
		elems = $.makeArray( $(this) ),
		dataName = 'imglazyload_offset',
		OFFSET = vertical ? 'top' : 'left',
		SCROLL = vertical ? 'scrollTop' : 'scrollLeft',
		winSize = vertical ? container.height() : container.width(),
		scrollCoord = container[ SCROLL ](),
		docSize = winSize + scrollCoord;

	// 延迟加载的触发器
	var trigger = {

		init : function( coord ){
			return coord >= scrollCoord &#038;&#038;
                            coord <= ( docSize + threshold );
		},

		scroll : function( coord ){
			var scrollCoord = container[ SCROLL ]();
			return coord >= scrollCoord &#038;&#038;
                    coord <= ( winSize + scrollCoord + threshold );
		},

		resize : function( coord ){
			var scrollCoord = container[ SCROLL ](),
				winSize = vertical ?
                            container.height() :
                            container.width();
			return coord >= scrollCoord &#038;&#038;
                   coord <= ( winSize + scrollCoord + threshold );
		}
	};

	var loader = function( triggerElem, event ){
		var i = 0,
			isCustom = false,
			isTrigger, coord, elem, $elem, lazySrc;

		// 自定义事件只要触发即可，无需再判断
		if( event ){
			if( event !== 'scroll' &#038;&#038; event !== 'resize' ){
				isCustom = true;
			}
		}
		else{
			event = 'init';
		}

		for( ; i < elems.length; i++ ){
			isTrigger = false;
			elem = elems[i];
			$elem = $( elem );
			lazySrc = $elem.attr( o.attr );

			if( !lazySrc || elem.src === lazySrc ){
				continue;
			}
			// 先从缓存获取offset值，缓存中没有才获取计算值,
			// 将计算值缓存，避免重复获取引起的reflow
			coord = $elem.data( dataName );

			if( coord === undefined ){
				coord = $elem.offset()[ OFFSET ];
				$elem.data( dataName, coord );
			}

			isTrigger = isCustom || trigger[ event ]( coord );			

			if( isTrigger ){
				// 加载图片
				elem.src = lazySrc;
				if( o.fadeIn ){
					$elem.hide().fadeIn();
				}
				// 移除缓存
				$elem.removeData( dataName );
				// 从DOM数组中移除该DOM
				elems.splice( i--, 1 );
			}
		}

		// 所有的图片加载完后卸载触发事件
		if( !elems.length ){
			if( triggerElem ){
				triggerElem.unbind( event, fire );
			}
			else{
				container.unbind( o.event, fire );
			}
			$( window ).unbind( 'resize', fire );
			elems = null;
		}

	};

	var fire = function( e ){
		loader( $(this), e.type );
	};

	// 绑定事件
	container = event === 'scroll' ? container : $( this );
	container.bind( event, fire );
	$( window ).bind( 'resize', fire );

	// 初始化
	loader();

	return this;
};

})( jQuery );
</pre>
<h3>调用：</h3>
<pre class="brush: c-sharp">
$( 'img' ).imglazyload({
	event : 'scroll',
	attr : 'lazy-src'
});
</pre>
<p>默认的调用可以省略所有参数。</p>
<pre class="brush: c-sharp">
$( 'img' ).imglazyload();
</pre>
<h3>图片延迟加载的插件API说明：</h3>
<ul>
<li><strong>attr</strong> string</li>
<li>存放图片真实地址的属性名，与HTML对应，默认是lazy-src。</li>
<li><strong>container</strong> dom &amp; selector</li>
<li>默认的容器为window，可自定义容器。</li>
<li><strong>event</strong> stirng</li>
<li>触发图片加载的事件类型，默认为window.onscroll事件</li>
<li><strong>fadeIn</strong> boolean</li>
<li>是否使用jQuery的fadeIn效果来显示，默认是false。</li>
<li><strong>threshold</strong> number</li>
<li>页面滚动到离图片还有指定距离的时候就进行加载，默认是0。</li>
<li><strong>vertical</strong> boolean</li>
<li>是否横向滚动，默认为true(纵向)。</li>
<li><strong>loadScript(增强版的功能)</strong> boolean</li>
<li>是否无阻塞加载javascript广告图片，默认为false。</li>
</ul>
<div class="demo"><a href="http://stylechen.com/wp-content/uploads/demo/imglazyload/index.html" target="_blank">查看演示</a></div>
<p>图片延迟加载的插件下载地址：<a href="http://stylechen.com/wp-content/uploads/download/imglazyload.zip">http://stylechen.com/wp-content/uploads/download/imglazyload.zip</a></p>
<div id="art_copy">原载于：雨夜带刀&#39;s Blog<br />
本文链接：<a title="图片延迟加载的实现" href="http://stylechen.com/imglazyload2.html">http://stylechen.com/imglazyload2.html</a><br />
如需转载请以链接形式注明原载或原文地址。
</div>
]]></content:encoded>
			<wfw:commentRss>http://stylechen.com/imglazyload2.html/feed</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>DOM数组去重</title>
		<link>http://stylechen.com/dom-distinct.html</link>
		<comments>http://stylechen.com/dom-distinct.html#comments</comments>
		<pubDate>Sat, 04 Feb 2012 08:02:19 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[selector]]></category>
		<category><![CDATA[sort]]></category>
		<category><![CDATA[去重]]></category>
		<category><![CDATA[选择器]]></category>

		<guid isPermaLink="false">http://stylechen.com/?p=475</guid>
		<description><![CDATA[<p>之前我在<a href="http://stylechen.com/array-distinct.html">也谈数组去重</a>中介绍了普通的数组去除重复的元素的博文，在选择器的开发过程中，会碰到这样的需求，就是删除一组DOM数组中重复的元素，使用之前的方法肯定行不通，对于 DOM 数组去重，需要另外的处理办法。什么情况下会选取重复的 DOM 元素？下面是一个比较常见的情况，先看 HTML 结构：</p>
<pre class="brush: c-sharp">
&#60;ul&#62;
&#160;&#160;&#60;li&#62;test1&#60;/li&#62;
&#160;&#160;&#60;li&#62;test2&#60;/li&#62;
&#160;&#160;&#60;li&#62;test3
&#160;&#160;&#160;&#160;&#60;ul&#62;
&#160;&#160;&#160;&#160;&#160;&#160;&#60;li&#62;测试1&#60;/li&#62;
&#160;&#160;&#160;&#160;&#160;&#160;&#60;li&#62;测试2&#60;/li&#62;
&#160;&#160;&#160;&#160;&#60;/ul&#62;
&#160;&#160;&#60;/li&#62;
&#60;/ul&#62;
</pre>
<p>如果是这样的选择器：query( 'ul li' )，按照从左到右的查找顺序，会先得到所有 ul 的集合。</p>
<pre class="brush: c-sharp">
[ ul, ul ]
</pre>]]></description>
			<content:encoded><![CDATA[<p>之前我在<a href="http://stylechen.com/array-distinct.html">也谈数组去重</a>中介绍了普通的数组去除重复的元素，在选择器的开发过程中，会碰到这样的需求，就是删除一组DOM数组中重复的元素，使用之前的方法肯定行不通，对于 DOM 数组去重，需要另外的处理办法。什么情况下会选取重复的 DOM 元素？下面是一个比较常见的情况，先看 HTML 结构：</p>
<pre class="brush: c-sharp">
&lt;ul&gt;
&nbsp;&nbsp;&lt;li&gt;test1&lt;/li&gt;
&nbsp;&nbsp;&lt;li&gt;test2&lt;/li&gt;
&nbsp;&nbsp;&lt;li&gt;test3
&nbsp;&nbsp;&nbsp;&nbsp;&lt;ul&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;li&gt;测试1&lt;/li&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;li&gt;测试2&lt;/li&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;/ul&gt;
&nbsp;&nbsp;&lt;/li&gt;
&lt;/ul&gt;
</pre>
<p>如果是这样的选择器：query( &#8216;ul li&#8217; )，按照从左到右的查找顺序，会先得到所有 ul 的集合。</p>
<pre class="brush: c-sharp">
[ ul, ul ]
</pre>
<p>得到了所有 ul 的集合，继续使用 getElementsByTagName 来查找 li，那么第1个 ul 元素就会查找到所有的 li 元素，接下来第2个也会查找到2个 li 元素，最后2个 li 元素就是重复的。这样的情况下，用常规的去重办法要将查找到的所有的 li 元素进行遍历，然后再排序，最后再过滤，这种方法稍后再说。</p>
<p>先来看看上面的 ul 的集合，两个元素之间是否存在关系呢？第1个 ul 是第2个 ul 的父元素，那么第1个 ul 就包含了第2个 ul。</p>
<pre class="brush: c-sharp">
// 检测a元素是否包含了b元素
var	contains = function( a, b ){
	// 标准浏览器支持compareDocumentPosition
	if( a.compareDocumentPosition ){
		return !!( a.compareDocumentPosition(b) &#038; 16 );
	}
	// IE支持contains
	else if( a.contains ){
		return a !== b &#038;&#038; a.contains( b );
	}

	return false;
};
</pre>
<p>上面的函数就是检测某元素是否被另一个元素包含，利用这个函数，那么在使用 getElementsByTagName 查找子元素的时候只需要查找第1个 ul 的所有 li 元素即可，这样就可以避免查找重复的元素。</p>
<p>如果无法检测相邻 DOM 数组元素间是否存在包含关系，那么就要先对数组先进行排序，然后再删除重复的元素。</p>
<pre class="brush: c-sharp">
var hasDuplicate = false,  // 是否有重复的DOM元素

	distinct = function( nodelist ){
		// 对于标准浏览器的处理
		var k = 1;
		// 先用自定义的sort函数对数组进行排序
		nodelist.sort(function( a, b ){

			if( a === b ){
				hasDuplicate = true;
				return 0;
			}

			return a.compareDocumentPosition(b) &#038; 4 ? -1 : 1;
		});

		// 如果存在重复的数组元素使用splice进行删除
		if( hasDuplicate ){
			for( ; k < nodelist.length; k++ ){
				elem = nodelist[k];
				if( elem === nodelist[k - 1] ){
					nodelist.splice( k--, 1 );
				}
			}
		}

		return nodelist;
	};
</pre>
<p>对于IE，IE支持 sourceIndex，但不支持 compareDocumentPosition 方法，利用 sourceIndex，可以大大的优化排序的算法。</p>
<p>新建一个空对象A和空数组B ，将元素的 sourceIndex 属性取出作为空对象A的键(key)，使用 new String 创建一个对象C，将 DOM 元素存放到该C对象中，再将该对象C存放到空数组B中，遍历 DOM 数组的时候检测A中是否已经存在相同的键(key)。然后再对数组B进行sort排序，这样避免了直接对 DOM 元素进行 sort 排序，而是对 sourceIndex 进行排序，排序完后再取出数组B中存放的 DOM 元素。那么最终的代码如下：</p>
<pre class="brush: c-sharp">
var hasDuplicate = false,  // 是否有重复的DOM元素

	distinct = function( nodelist ){
		if( nodelist.length < 2 ){
			return nodelist;
		}
		var i = 0,
			k = 1,
			len = nodelist.length;

		// IE的DOM元素都支持sourceIndex
		if( nodelist[0].sourceIndex ){
			var arr = [],
				obj = {},
				elems = [],
				j = 0,
				index, elem;

			for( ; i < len; i++ ){
				elem = nodelist[i];
				index = elem.sourceIndex + 1e8;	

				if( !obj[index] ){
					// 使用 new String 创建一个对象 C，
					// 将 DOM 元素存放到该 C 对象中，
					// 再将该对象 C 存放到空数组 B 中
					( arr[j++] = new String(index) ).elem = elem;
					// 将 sourceIndex 属性取出作为空对象 A 的键(key)
					obj[index] = true;
				}
			}

			arr.sort();
			// 取出存放在数组C中的DOM元素
			while( j ){
				elems[--j] = arr[j].elem;
			}

			arr = null;
			return elems;
		}
		// 标准浏览器的DOM元素都支持compareDocumentPosition
		else{
			// 先用自定义的sort函数对数组进行排序
			nodelist.sort(function( a, b ){

				if( a === b ){
					hasDuplicate = true;
					return 0;
				}

				return a.compareDocumentPosition(b) &#038; 4 ? -1 : 1;
			});

			// 如果存在重复的数组元素使用splice进行删除
			if( hasDuplicate ){
				for( ; k < nodelist.length; k++ ){
					elem = nodelist[k];
					if( elem === nodelist[k - 1] ){
						nodelist.splice( k--, 1 );
					}
				}
			}

			return nodelist;
		}
	};
</pre>
<p>将上面的代码稍微变动下，就可以增加一个功能，检测一组 DOM 数组中是否存在同级元素，然后删除同级元素，只要将 elem 替换成 elem.parentNode 即可。这个功能在选择器的开发中也非常有用，这里就不贴重复的代码了。</p>
<p>参考：</p>
<ul>
<li><a href="http://www.cnblogs.com/jkisjk/archive/2011/01/28/array_quickly_sortby.html" target="_blank">http://www.cnblogs.com/jkisjk/archive/2011/01/28/array_quickly_sortby.html</a></li>
<li><a href="http://www.cnblogs.com/rubylouvre/archive/2011/11/10/2243838.html" target="_blank">http://www.cnblogs.com/rubylouvre/archive/2011/11/10/2243838.html</a></li>
<li><a href="https://github.com/jquery/sizzle/blob/master/sizzle.js" target="_blank">https://github.com/jquery/sizzle/blob/master/sizzle.js</a></li>
</ul>
<div id="art_copy">原载于：雨夜带刀&#39;s Blog<br />
本文链接：<a title="DOM数组去重" href="http://stylechen.com/dom-distinct.html">http://stylechen.com/dom-distinct.html</a><br />
如需转载请以链接形式注明原载或原文地址。
</div>
]]></content:encoded>
			<wfw:commentRss>http://stylechen.com/dom-distinct.html/feed</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>javascript选择器的那些事儿</title>
		<link>http://stylechen.com/javascript-selector.html</link>
		<comments>http://stylechen.com/javascript-selector.html#comments</comments>
		<pubDate>Thu, 19 Jan 2012 06:33:24 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[selector]]></category>
		<category><![CDATA[选择器]]></category>

		<guid isPermaLink="false">http://stylechen.com/?p=468</guid>
		<description><![CDATA[<p>象 CSS 选择器那样，在 javascript 中用同样的语法来选取一个或一组 DOM 元素，这种简便的语法在目前的 javascript 选择器领域中算是事实上的标准了，jQuery 的成功也与它的选择器(sizzle)的易用是密不可分。</p>
<p>各大 javascript 框架和类库都有一套独自的选择器，我花了点时间自己开发了一套选择器，在性能测试方面也不输给 jQuery 的 sizzle。在 javascript 选择器的开发过程中，学到了不少东西，这些东西如果自己不去开发，估计永远也没机会学到。现在，对于 javascript 选择器的原理以及如何去实现都有了一定的了解，在这里，我觉得有必要将一些心得记录下来，并分享给有需要的人。</p>
<p>jQuery 在 CSS3 的选择器出来以前，就实现了很多自己私有的选择器，虽然这些私有的选择器在日常的应用中使用率都不太高，但是其作为一个类库，要适应和兼容各种场景，面对各种技术水平的web开发者，这些私有选择器还是有不小的用处，甚至可以说，CSS3 的选择器在一定程度上也吸纳了 jQuery 的私有选择器的思想，在它的基础上再做一些改良，使之成为现在的 <a href="http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#selectors" target="_blank">W3C 标准</a>。</p>]]></description>
			<content:encoded><![CDATA[<p>象 CSS 选择器那样，在 javascript 中用同样的语法来选取一个或一组 DOM 元素，这种简便的语法在目前的 javascript 选择器领域中算是事实上的标准了，jQuery 的成功也与它的选择器(sizzle)的易用是密不可分。</p>
<p>各大 javascript 框架和类库都有一套独自的选择器，我花了点时间自己开发了一套选择器，在性能测试方面也不输给 jQuery 的 sizzle。在 javascript 选择器的开发过程中，学到了不少东西，这些东西如果自己不去开发，估计永远也没机会学到。现在，对于 javascript 选择器的原理以及如何去实现都有了一定的了解，在这里，我觉得有必要将一些心得记录下来，并分享给有需要的人。</p>
<p>jQuery 在 CSS3 的选择器出来以前，就实现了很多自己私有的选择器，虽然这些私有的选择器在日常的应用中使用率都不太高，但是其作为一个类库，要适应和兼容各种场景，面对各种技术水平的web开发者，这些私有选择器还是有不小的用处，甚至可以说，CSS3 的选择器在一定程度上也吸纳了 jQuery 的私有选择器的思想，在它的基础上再做一些改良，使之成为现在的 <a href="http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#selectors" target="_blank">W3C 标准</a>。</p>
<p>我的选择器将尽量遵循 CSS3 选择器的 W3C 标准，并适当的加入一些非标准但是比较有用的选择器，下面是支持的选择器列表：</p>
<pre class="brush: c-sharp">
/* 基本选择器 */
E
E#myid
E.class

/* 关系选择器 */
E > F
E + F
E ~ F

/* 属性选择器 */
E[attr]
E[attr="value"]
E[attr!="value"]    // 非标准
E[attr*="value"]
E[attr~="value"]
E[attr^="value"]
E[attr$="value"]
E[attr|="value"]

/* 伪类选择器 */
E:nth-child(n)
E:nth-last-child(n)
E:nth-of-type(n)
E:nth-last-of-type(n)
E:first-child
E:last-child
E:first-of-type
E:last-of-type
E:only-child
E:only-of-type
E:empty
E:not(s)
E:enabled
E:disabled
E:checked
E:selected    // 非标准
E:hidden    // 非标准
E:visible    // 非标准
</pre>
<p>熟悉 jQuery 的都知道 jQuery 有很多表单选择器，如:text、:password、:radio等，这些表单选择器完全可以用属性选择器来实现，如input[type="text"]，还有 :first、:last、:even、:odd、:eq 等伪类选择器，这些选择器也完全可以用 :nth-child、:first-child 等标准的伪类选择器来取代。给用户的选择越多，也会使用户更加迷惑。从用户的角度出发，符合标准既能减少学习成本又能实现常用的功能。</p>
<p>标准浏览器以及IE8都有一个 querySelectorAll 的方法，该方法支持符合 W3C 标准的选择器，其中IE8只支持到 CSS2，不支持 CSS3 选择器，所以自己开发出来的选择器的主要是应用于不支持 querySelectorAll 方法的浏览器(主要就是IE6和IE7)。</p>
<h3>基本选择器</h3>
<p>下面是一个比较常见的选择器：</p>
<pre class="brush: c-sharp">
#box .list p
</pre>
<p>该选择器由3个部分组成，#box : id选择器，.list : class选择器，p : tag选择器。这算是3种最基本的选择器了，一些复杂的选择器都是从这个基础上加以演变而来。接下来对这个选择器进行分割，可以用正则或者字符串的 split 方法转换成数组，那么上面的选择器字符串将变成下面的数组形式，有了这个数组，接下来就好办了。</p>
<pre class="brush: c-sharp">
[ '#box', '.list', 'p' ]
</pre>
<p>选择器的查找顺序有从右到左，也有从左到右，我的选择器使用的是从左到右的查找顺序。不管是从右到左还是从左到右，只要速度够快就行，如果你觉得从右到左更有意思，更能体现技术水平，你大可以去尝试下，如果你的选择器在性能上比我的更好，那么我就服你了。这么说的原因只是觉得要实现某个功能可能有好几种方法，不管是使用哪种方法，结果的优劣才是最好的说明，当然，这不是绝对的，使用哪种方法更好也是因人而异的。</p>
<p>有了数组，确定了顺序，现在可以一个个查找了。从第一个 #box 开始，这个我们一看就知道是 id 选择器，但是 javascript 不能看，只能查找特征来区分，#box 的特征符号就是“#” ，以此类推 .list 的特征符号就是“.”，那么 p 的特征符号是什么呢？tag选择器没有特征符号，如果排除了 id 和 class 选择器，剩下的不就是 tag 吗？</p>
<p>将第一个查找到的结果作为第二个选择器的上下文(context)，那么第二个选择器将在第一个选择器的上下文中查找结果，这样以此类推，直至查找到最后一个选择器，最后一个选择器的结果就是整个选择器的结果。可以用一个数组来存放查询结果，每次都读取最后一个数组元素来作为查找范围。第一个选择器如果没有指定查找范围，那么这个查找范围就默认为页面的根元素 document。最后为了便于操作，将最终的查找结果转换成真实的数组，因为查找结果可能会是 nodelist 的 array-like 的数据。</p>
<p>我的选择器的实现思路基本就是这样，通过上面的分析后，选择器的开发思路其实并不难，有了这个思路，其他类型的选择器同样可以效仿之。</p>
<h3>关系选择器</h3>
<p>关系选择器主要包含3种，当然也可以将空格的算进来，那么就是4种。</p>
<p>按照基本选择器的实现思路，那么关系选择器的特征符号就分别是“ ”、“>”、“+”、“~”。关系选择器就是先查找children、nextSibling元素，然后进行相应的过滤。比如 div + p，按照从左到右的顺序先查找 div 元素，然后查找 div 的 nextSibling 元素，最后再通过 p 的 tagName 来进行过滤得到结果。部分关系选择器使用从右到左的顺序可能会更快，以后做优化的时候我会尝试。</p>
<h3>属性选择器</h3>
<p>标准的属性选择器有7个，各框架和类库的选择器中都有一个私有的属性选择器“!=”，这个是非标准的，但是比较有用。</p>
<p>属性选择器的特征符号是“[”，需要注意区分的是“~=”和关系选择器的“~”。如 div[class="test"] 可以分割成4个部分：</p>
<ul>
<li>1、tag 选择器 div</li>
<li>2、attr (属性名) class</li>
<li>3、过滤符号 =</li>
<li>4、value (属性值) test</li>
</ul>
<p>先查找到 div 元素，然后使用 getAttribute 方法来获取该元素的属性值，最后通过各种过滤方法进行过滤。需要注意的是 getAttribute 方法不能滥用，部分属性是可以直接取的，如 elm.type ，不通过 getAttribute 方法性能会有所提升。</p>
<h3>伪类选择器</h3>
<p>伪类选择器易于扩展，所以大部分的jQuery的私有选择器都是伪类选择器。标准的伪类选择器主要有索引伪类(也称位置伪类)、表单伪类等。伪类选择器的特征符号“:”后面可以是任意的字符串，如果想自定义某种伪类选择器会非常方便。</p>
<p>如何分割伪类选择器？比如 p:nth-child(enev) 可以先分割成2个部分：</p>
<ul>
<li>1、tag 选择器 p</li>
<li>2、伪类选择器 nth-child(enev)</li>
</ul>
<p>用中划线“-”来判断是否为索引伪类还是其他的伪类，其他伪类的实现很简单就不多说，这里简单的说下索引伪类。得到了nth-child(enev)可以继续分割：</p>
<ul>
<li>1、nth-child</li>
<li>2、even</li>
</ul>
<p>到这一步还可以继续分割，也可以不分割，就看你要用什么方法来处理了。标准的索引伪类选择器都有一定的规律，找准规律才好处理。nth系列的伪类比较麻烦，因为其参数的种类很多：3、3n、3n+1、3n-1、even、odd，这些参数一看就蛋痛。还是拿 p:nth-child(enev) 来说，先查找 p 元素，然后查找 p 元素的父级元素，再通过父级元素得到 firstChild，再从 firstChild 入手，遍历 nextSibling 元素同时过滤索引，对于p:nth-last-child(enev)，就反过来查找，从 lastChild 入手，遍历 previousSibling 元素。</p>
<p>首尾索引伪类 first-child、last-child 等系列选择器，也同样可以查找 nextSibling、previousSibling 元素来进行过滤，如果是 firstChild 元素，那么其 previousSibling 元素必然为 undefined。</p>
<p>这些索引伪类的实现方法比较多，这只是我个人觉得速度还不至于太慢的方法，也都是简单的说说思路。</p>
<p>马上要过年了，在这里祝大家新年快乐！</p>
<p><a href="http://stylechen.com/wp-content/uploads/demo/slickspeed/index.html" target="_blank" title="选择器速度测试"><strong>选择器速度测试</strong></a> 测试说明：使用IE6/7进行测试，标准浏览器测试的是浏览器本身对querySelector方法的速度，没有意义，另jQuery、mootools、prototype都有一个实例化的过程，其他选择器引擎是直接返回了结果，直接返回结果的肯定是快些，速度测试还是有一定的片面性，我的选择器引擎是最后一个easyJS。</p>
<p>想看我的选择器源码的同志可以下载，正常运行需要其他的模块的支持，暂时只提供<a href="http://stylechen.com/wp-content/uploads/demo/slickspeed/frameworks/selector.js">选择器模块</a>。</p>
<div id="art_copy">原载于：雨夜带刀&#39;s Blog<br />
本文链接：<a title="javascript选择器的那些事儿" href="http://stylechen.com/javascript-selector.html">http://stylechen.com/javascript-selector.html</a><br />
如需转载请以链接形式注明原载或原文地址。
</div>
]]></content:encoded>
			<wfw:commentRss>http://stylechen.com/javascript-selector.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>也谈javascript数组去重</title>
		<link>http://stylechen.com/array-distinct.html</link>
		<comments>http://stylechen.com/array-distinct.html#comments</comments>
		<pubDate>Fri, 09 Dec 2011 09:03:03 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[sort]]></category>
		<category><![CDATA[数组]]></category>

		<guid isPermaLink="false">http://stylechen.com/?p=457</guid>
		<description><![CDATA[<p>有时会碰上这种需求，需要将数组中重复的元素删除掉，而只保留一个。最先想到的办法很可能就是用2个for循环来做比较然后去除掉重复的元素，代码如下所示：</p>
<p><strong>方法1：</strong></p>
<pre class="brush: c-sharp">
Array.prototype.distinct = function(){
	var arr = [],
	     len = this.length;

	for ( var i = 0; i < len; i++ ){
		for( var j = i+1; j < len; j++ ){
			if( this[i] === this[j] ){
				j = ++i;
			}
		}
		arr.push( this[i] );
	}
	return arr;
};
</pre>
<p>使用方法1如果碰到数据比较多时性能上会差很多。那么请继续看下面的方法...</p>]]></description>
			<content:encoded><![CDATA[<p>有时会碰上这种需求，需要将数组中重复的元素删除掉，而只保留一个。最先想到的办法很可能就是用2个for循环来做比较然后去除掉重复的元素，代码如下所示：</p>
<p><strong>方法1：</strong></p>
<pre class="brush: c-sharp">
Array.prototype.distinct = function(){
	var arr = [],
	     len = this.length;

	for ( var i = 0; i < len; i++ ){
		for( var j = i+1; j < len; j++ ){
			if( this[i] === this[j] ){
				j = ++i;
			}
		}
		arr.push( this[i] );
	}
	return arr;
};
</pre>
<p>使用方法1如果碰到数据比较多时性能上会差很多。那么请继续看下面的方法。</p>
<p><strong>方法2：</strong></p>
<pre class="brush: c-sharp">
Array.prototype.distinct = function(){

	var self = this,
		arr = self.concat().sort(); // 创建一个新数组并排序

	arr.sort(function( a, b ){
		if( a === b ){
			var n = self.indexOf( a ); //获取索引值
			self.splice( n, 1 );
		}
	});

	return self;

};
</pre>
<p>方法2使用了 sort 的自定义回调函数，也用到了 indexOf 这个IE6/7/8不支持的方法。当然，indexOf可以自己模拟，但是更大的问题是IE6/7/8的sort方法和标准浏览器之间也有差别。在IE6/7/8中使用 sort 方法的自定义回调函数陷阱比较多，上面的自定义 sort 的回调函数的代码在IE6/7/8中会直接报“缺少数字”的错误，回调函数的返回是NaN的话就会报这个错，因为理论上 sort 的回调函数只能返回整数。就算忽略返回值的问题还是有其他问题，最后也没有过多的去纠结了，方法2在IE6/7/8中行不通。</p>
<p>从<a href="http://www.css88.com/archives/2429" target="_blank">愚人码头</a>那里看来了方法3，下面是他的代码：</p>
<p><strong>方法3：</strong></p>
<pre class="brush: c-sharp">
Array.prototype.delRepeat=function(){
	var newArray=[];
	var provisionalTable = {};
	for (var i = 0, item; (item= this[i]) != null; i++) {
        if (!provisionalTable[item]) {
            newArray.push(item);
            provisionalTable[item] = true;
        }
    }
    return newArray;
};
</pre>
<p>方法3使用了一个临时的对象来存储数组的元素，如果碰上重复的数组元素，将会忽略掉。但是，如果碰到下面这种数组：</p>
<pre class="brush: c-sharp">
var arr = [ 'firefox', 1, '1' ];
</pre>
<p>上面的数组如果用方法3会误将 1 和 “1” 当成重复元素而删除掉，于是有将方法3做了一点点的小修改，可以解决这个BUG。</p>
<p><strong>方法3的修改版：</strong></p>
<pre class="brush: c-sharp">
Array.prototype.distinct = function(){
	var arr = [],
		obj = {},
		i = 0,
		len = this.length,
		result;

	for( ; i < len; i++ ){
		result = this[i];
		if( obj[result] !== result ){
			arr.push( result );
			obj[result] = result;
		}
	}

	return arr;
};
</pre>
<p>之后又看了愚人码头文章后面的评论，该方法和Rekey提供的方法是一样的，但是这个方法也有BUG，如果碰到这样的2B数组就杯具了：</p>
<pre class="brush: c-sharp">
var arr = [ 'firefox', 1, '1', 1 ];
</pre>
<p>上面的数组用方法3的修改版，将不会删除后3个元素，不过这种数组有点极端了，如果碰到字符串字面量和数字相同的数据应该预先处理下以规避这种BUG。使用临时对象的方法比 sort 在标准浏览器中略快，sort 方法在各浏览器中的算法应该也有区别。</p>
<div id="art_copy">原载于：雨夜带刀&#39;s Blog<br />
本文链接：<a title="也谈javascript数组去重" href="http://stylechen.com/array-distinct.html">http://stylechen.com/array-distinct.html</a><br />
如需转载请以链接形式注明原载或原文地址。
</div>
]]></content:encoded>
			<wfw:commentRss>http://stylechen.com/array-distinct.html/feed</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>事件触发器</title>
		<link>http://stylechen.com/trigger.html</link>
		<comments>http://stylechen.com/trigger.html#comments</comments>
		<pubDate>Mon, 24 Oct 2011 14:50:49 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[dispatchEvent]]></category>
		<category><![CDATA[event]]></category>
		<category><![CDATA[fireEvent]]></category>
		<category><![CDATA[javascript事件]]></category>
		<category><![CDATA[trigger]]></category>

		<guid isPermaLink="false">http://stylechen.com/?p=445</guid>
		<description><![CDATA[<p>事件触发器从字面意思上可以很好的理解，就是用来触发事件的，但是有些没有用过的朋友可能就会迷惑了，事件不是通常都由用户在页面上的实际操作来触发的吗？这个观点不完全正确，因为有些事件必须由程序来实现，如自定义事件，jQuery的ajax框架的一些自定义事件就必须由事件触发器来实现。当然，在一些特殊情况下，用事件触发器来触发事件比用户的实际操作来触发事件更方便。</p>
<p>对于实现事件触发器，浏览器都有原生的方法来支持，但是在兼容性上又有很大的出入，这种兼容性的问题完全在意料之中，IE有自己的方法，其他标准浏览器也有一套方法，不说谁的方法好与不好，对于WEB开发者来说搞出几套方法就是对开发人员的一种折磨。IE支持fireEvent方法来实现事件触发，标准浏览器支持dispatchEvent来实现事件触发，两面派的IE9是两者都支持。下面是出自prototype.js的源码(其实我是从司徒正美的博客复制过来的)：</p>
<pre class="brush: c-sharp">
var fireEvent = function fireEvent(element,event){
	if (document.createEventObject){
		// IE浏览器支持fireEvent方法
		var evt = document.createEventObject();
		return element.fireEvent('on'+event,evt)
	}
	else{
		// 其他标准浏览器使用dispatchEvent方法
		var evt = document.createEvent( 'HTMLEvents' );
		// initEvent接受3个参数：
		// 事件类型，是否冒泡，是否阻止浏览器的默认行为
		evt.initEvent(event, true, true);  
		return !element.dispatchEvent(evt);
	}
};
</pre>]]></description>
			<content:encoded><![CDATA[<p>事件触发器从字面意思上可以很好的理解，就是用来触发事件的，但是有些没有用过的朋友可能就会迷惑了，事件不是通常都由用户在页面上的实际操作来触发的吗？这个观点不完全正确，因为有些事件必须由程序来实现，如自定义事件，jQuery的ajax框架的一些自定义事件就必须由事件触发器来实现。当然，在一些特殊情况下，用事件触发器来触发事件比用户的实际操作来触发事件更方便。</p>
<p>对于实现事件触发器，浏览器都有原生的方法来支持，但是在兼容性上又有很大的出入，这种兼容性的问题完全在意料之中，IE有自己的方法，其他标准浏览器也有一套方法，不说谁的方法好与不好，对于WEB开发者来说搞出几套方法就是对开发人员的一种折磨。IE支持fireEvent方法来实现事件触发，标准浏览器支持dispatchEvent来实现事件触发，两面派的IE9是两者都支持。下面是出自prototype.js的源码(其实我是从司徒正美的博客复制过来的)：</p>
<pre class="brush: c-sharp">
var fireEvent = function(element,event){
	if (document.createEventObject){
		// IE浏览器支持fireEvent方法
		var evt = document.createEventObject();
		return element.fireEvent('on'+event,evt)
	}
	else{
		// 其他标准浏览器使用dispatchEvent方法
		var evt = document.createEvent( 'HTMLEvents' );
		// initEvent接受3个参数：
		// 事件类型，是否冒泡，是否阻止浏览器的默认行为
		evt.initEvent(event, true, true);
		return !element.dispatchEvent(evt);
	}
};
</pre>
<p>上面的方法可以兼容主流的浏览器以实现事件触发器的功能。但是对于一些封装好的事件处理系统，如jQuery的event模块，就没这么简单了，只能通过模拟来实现了。我在之前写过一个很简单的<a href="http://stylechen.com/easyevent.html">事件处理系统</a>，最近又碰到自定义事件的需求，于是在之前的事件系统的基础上模拟了一个事件触发器，代码如下：</p>
<pre class="brush: c-sharp">
/**
 * 事件触发器
 * @param { Object } DOM元素
 * @param { String / Object } 事件类型 / event对象
 * @param { Array }  传递给事件处理函数的附加参数
 * @param { Boolean } 是否冒泡
 */
trigger : function( elem, event, data, isStopPropagation ){
	var type = event.type || event,
		// 冒泡的父元素，一直到document、window
		parent = elem.parentNode ||
			elem.ownerDocument ||
			elem === elem.ownerDocument &#038;&#038; win,
		eventHandler = $.data( elem, type + 'Handler' );

	isStopPropagation = typeof data === 'boolean' ?
		data : (isStopPropagation || false);

	data = data &#038;&#038; isArray( data ) ? data : [];

	// 创建自定义的event对象
	event = typeof event === 'object' ?
		event : {
			type : type,
			preventDefault : noop,
			stopPropagation : function(){
				isStopPropagation = true;
			}
		};

	event.target = elem;
	data.unshift( event );
	if( eventHandler ){
		eventHandler.call( elem, data );
	}
	// 递归调用自身来模拟冒泡
	if( parent &#038;&#038; !isStopPropagation ){
		data.shift();
		this.trigger( parent, event, data );
	}
}
</pre>
<p>
模拟的原理并不难，给某元素绑定一个事件处理函数，如果有触发事件的实际操作就会执行相应的事件处理函数，所以要达到事件触发器的功能只要获取到相应的事件处理函数然后执行就差不多了，这是最基本的。</p>
<p>在实际的事件发生时浏览器会生成一个event对象，里面包含了一些事件发生时的属性和信息。如果没有实际的事件发生是没有这个event对象的，所以上面的代码也创建了一个event对象满足最基本的功能。
</p>
<p>还有事件冒泡，如果没有实际的事件发生，自然也不会有冒泡的行为，那么如果要模拟冒泡的功能，就必须不断的查找父元素并检查是否绑定了相同类型的事件，直至到document和window为止，如果结构复杂，这种递归调用的方法性能估计会不怎么样。</p>
<p>最后是浏览器的默认行为，我觉得这个要去模拟相当麻烦，麻烦到不知如何去实现，比如a标签默认的跳转，我测试了jQuery的trigger，也没有实现，但是一些其他的行为貌似又在API手册中有介绍。毕竟这个功能不是很重要，暂时也没做过多的深究，下面是一些演示。</p>
<div class="demo"><a href="http://stylechen.com/wp-content/uploads/demo/trigger/index.html" target="_blank">查看演示</a></div>
<div id="art_copy">原载于：雨夜带刀&#39;s Blog<br />
本文链接：<a title="事件触发器" href="http://stylechen.com/trigger.html">http://stylechen.com/trigger.html</a><br />
如需转载请以链接形式注明原载或原文地址。
</div>
]]></content:encoded>
			<wfw:commentRss>http://stylechen.com/trigger.html/feed</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>CSS颜色值转换</title>
		<link>http://stylechen.com/parsecolor.html</link>
		<comments>http://stylechen.com/parsecolor.html#comments</comments>
		<pubDate>Sat, 01 Oct 2011 04:52:56 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[计算样式]]></category>
		<category><![CDATA[进制转换]]></category>
		<category><![CDATA[颜色值]]></category>

		<guid isPermaLink="false">http://stylechen.com/?p=440</guid>
		<description><![CDATA[<p>CSS的颜色值有3种表示方式，为十六进制、RGB、颜色名称，用代码表示分别为：</p>
<pre class="brush: c-sharp">
color : #345456;
color : rgb(255,152,10);
color : red;
</pre>
<p>平时在编写CSS代码时，在设置颜色值时使用的最多的方式是十六进制。如果要对颜色值进行计算，就必须将十六进制的颜色值转换成RGB模式。那么如何进行转换呢？</p>
<p>别急，先来看看十六进制的颜色值和RGB颜色值之间的对应关系。比如上面的十六进制的颜色值：#345456，其中的前2位“34”对应R，中间2位“54”对应G，最后2位“56”对应B，有了这个对应关系那么剩下的就是进制转换的事了。</p>]]></description>
			<content:encoded><![CDATA[<p>CSS的颜色值有3种表示方式，为十六进制、RGB、颜色名称，用代码表示分别为：</p>
<pre class="brush: c-sharp">
color : #345456;
color : rgb(255,152,10);
color : red;
</pre>
<p>平时在编写CSS代码时，在设置颜色值时使用的最多的方式是十六进制。如果要对颜色值进行计算，就必须将十六进制的颜色值转换成RGB模式。那么如何进行转换呢？</p>
<p>别急，先来看看十六进制的颜色值和RGB颜色值之间的对应关系。比如上面的十六进制的颜色值：#345456，其中的前2位“34”对应R，中间2位“54”对应G，最后2位“56”对应B，有了这个对应关系那么剩下的就是进制转换的事了。</p>
<p>javascript中使用parseInt()方法可以将将第二个参数设置成16就可以将十六进制转换成十进制。需要注意的是，CSS中的十六进制颜色值支持简写，在转换的过程中要考虑到简写的处理办法。</p>
<p>下面是我写的一个将十六进制的颜色值转换成RGB的函数，该函数接受一个十六进制或RGB的CSS颜色值，返回的是一个包含了r、g、b三个颜色值的对象，这样就可以很方便的对颜色值进行计算了。</p>
<pre class="brush: c-sharp">
var	parseColor = function( val ){
	var r, g, b;
	// 参数为RGB模式时不做进制转换，直接截取字符串即可
	if( /rgb/.test(val) ){
		var arr = val.match( /\d+/g );
		r = parseInt( arr[0] );
		g = parseInt( arr[1] );
		b = parseInt( arr[2] );
	}
	// 参数为十六进制时需要做进制转换
	else if( /#/.test(val) ){
		var len = val.length;
		// 非简写模式 #0066cc
		if( len === 7 ){
			r = parseInt( val.slice(1, 3), 16 );
			g = parseInt( val.slice(3, 5), 16 );
			b = parseInt( val.slice(5), 16 );
		}
		// 简写模式 #06c
		else if( len === 4 ){
			r = parseInt( val.charAt(1) + val.charAt(1), 16 );
			g = parseInt( val.charAt(2) + val.charAt(2), 16 );
			b = parseInt( val.charAt(3) + val.charAt(3), 16 );
		}
	}
	else{
		return val;
	}

	return {
		r : r,
		g : g,
		b : b
	}
};
</pre>
<p>下面是调用结果：</p>
<pre class="brush: c-sharp">
parseColor( '#009652' ); // { r = 0, g = 150, b = 82 }
parseColor( '#06c' ); // { r = 0, g = 102, b = 204 }
parseColor( 'rgb(255,10,102)' ); // { r = 255, g = 10, b = 102 }
</pre>
<div class="art_copy">
原载于：雨夜带刀&#39;s Blog<br />
本文链接：<a href="http://stylechen.com/parsecolor.html">http://stylechen.com/parsecolor.html</a><br />
如需转载请以链接形式注明原载与原文地址。
</div>
]]></content:encoded>
			<wfw:commentRss>http://stylechen.com/parsecolor.html/feed</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>getStyle的优化</title>
		<link>http://stylechen.com/getstyle2.html</link>
		<comments>http://stylechen.com/getstyle2.html#comments</comments>
		<pubDate>Sat, 24 Sep 2011 09:43:07 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[计算样式]]></category>

		<guid isPermaLink="false">http://stylechen.com/?p=429</guid>
		<description><![CDATA[<p>之前我写过一篇关于<a href="http://stylechen.com/getstyle.html">获取元素计算的样式</a>的文章，后来在使用发现中还有一些没想到的兼容问题，今天我对原来的代码进行了优化，并对一些常见的兼容问题进行了处理。</p>
<p>在javascript中“-”(中划线或连字符)代表的是减号，而在CSS中，许多样式属性都有这个符号，如：padding-left、font-size等，所以在javascript中如果出现如下的代码就一个错误：</p>
<pre class="brush: c-sharp">
elem.style.margin-left = '20px';
</pre>
<p>正确的写法应该是：</p>
<pre class="brush: c-sharp">
elem.style.marginLeft = '20px';
</pre>
<p>这里需要把CSS的中划线去掉并把原来紧跟在中划线后的字母大写，俗称“驼峰式”写法，不管是使用javascript设置或是获取元素的CSS样式都应该是驼峰式的写法。但是不少对CSS熟悉而又对javascript不太熟悉的新手朋友总是会犯这种低级错误，使用replace的高级用法可以很简单的将CSS属性中的中划线替换成驼峰式的写法。</p>
<pre class="brush: c-sharp">
var newProp = prop.replace( /\-(\w)/g, function( $, $1 ){
	return $1.toUpperCase();
});
</pre>]]></description>
			<content:encoded><![CDATA[<p>之前我写过一篇关于<a href="http://stylechen.com/getstyle.html">获取元素计算的样式</a>的文章，后来在使用发现中还有一些没想到的兼容问题，今天我对原来的代码进行了优化，并对一些常见的兼容问题进行了处理。</p>
<p>在javascript中“-”(中划线或连字符)代表的是减号，而在CSS中，许多样式属性都有这个符号，如padding-left、font-size等，所以在javascript中如果出现如下的代码就一个错误：</p>
<pre class="brush: c-sharp">
elem.style.margin-left = '20px';
</pre>
<p>正确的写法应该是：</p>
<pre class="brush: c-sharp">
elem.style.marginLeft = '20px';
</pre>
<p>这里需要把CSS的中划线去掉并把原来紧跟在中划线后的字母大写，俗称“驼峰式”写法，不管是使用javascript设置或是获取元素的CSS样式都应该是驼峰式的写法。但是不少对CSS熟悉而又对javascript不太熟悉的新手朋友总是会犯这种低级错误，使用replace的高级用法可以很简单的将CSS属性中的中划线替换成驼峰式的写法。</p>
<pre class="brush: c-sharp">
var newProp = prop.replace( /\-(\w)/g, function( $, $1 ){
	return $1.toUpperCase();
});
</pre>
<p>
对于float，在javascript中属于保留字，在javascript中设置或获取元素的float的值，都有其他的代替写法，在标准浏览器中为cssFloat，而在IE6/7/8中为styleFloat。
</p>
<p>如果top、right、bottom、left没有一个显式的值，在获取这些值的时候部分浏览器会返回一个auto，虽然auto这个值是一个合法的CSS属性值，但绝不是我们想要的结果，而应该是0px。</p>
<p>在IE6/7/8中要设置元素的透明度需要用到滤镜、如：filter:alpha(opacity=60)，对于标准浏览器直接设置opacity即可，IE9两种写法都支持，我对获取元素的透明度也做了兼容处理，只要使用opacity就可以获取到所有浏览器元素的透明度的值。</p>
<p>在IE6/7/8中获取元素的宽度和高度已经在上篇文中介绍过了，这里就不再复述了。还有一个需要注意的地方就是，如果元素的样式是使用style内联的写法，或者是已经使用javascript设置过样式的属性，可以使用下面的方法获取到元素的计算样式：</p>
<pre class="brush: c-sharp">
var height = elem.style.height;
</pre>
<p>这个方法比读取getComputedStyle或currentStyle中的属性值都要快，应该优先使用，当然前提条件就是样式是通过内联的写法设置的(使用javascript设置也是设置内联样式)。优化过的最终代码如下：</p>
<pre class="brush: c-sharp">
var getStyle = function( elem, p ){
  var rPos = /^(left|right|top|bottom)$/,
    ecma = 'getComputedStyle' in window,
  // 将中划线转换成驼峰式 如：padding-left => paddingLeft
  p = p.replace( /\-(\w)/g, function( $, $1 ){
    return $1.toUpperCase();
  });
  // 对float进行处理
  p = p === 'float' ? ( ecma ? 'cssFloat' : 'styleFloat' ) : p;

  return !!elem.style[p] ?
    elem.style[p] :
    ecma ?
    function(){
      var val = getComputedStyle( elem, null )[p];
      // 处理top、right、bottom、left为auto的情况
      if( rPos.test(p) &#038;&#038; val === 'auto' ){
        return '0px';
      }
      return val;
    }() :
    function(){
      var val =  elem.currentStyle[p];
          // 获取元素在IE6/7/8中的宽度和高度
	  if( (p === "width" || p === "height") &#038;&#038; val === 'auto' ){
	    var rect =  elem.getBoundingClientRect();
	    return ( p === 'width' ? rect.right - rect.left : rect.bottom - rect.top ) + 'px';
	  }
	// 获取元素在IE6/7/8中的透明度
	  if( p === 'opacity' ){
	    var filter = elem.currentStyle.filter;
	    if( /opacity/.test(filter) ){
  	      val = filter.match( /\d+/ )[0] / 100;
	      return (val === 1 || val === 0) ? val.toFixed(0) : val.toFixed(1);
	    }
	    else if( val === undefined ){
	      return '1';
	    }
	  }
	  // 处理top、right、bottom、left为auto的情况
	  if( rPos.test(p) &#038;&#038; val === 'auto' ){
	    return '0px';
	  }
	  return val;
	}();
};
</pre>
<p>下面是调用示例：</p>
<pre class="brush: c-sharp">
&lt;style&gt;
.box{
  width:500px;
  height:200px;
  background:#000;
  filter:alpha(opacity=60);
  opacity:0.6;
}
&lt;/style&gt;

&lt;div id="box"&gt;&lt;/div&gt;

&lt;script&gt;
var box = document.getElementById( 'box' );

alert( getStyle(box, 'width') ); // '500px'
alert( getStyle(box, 'background-color') ); // 'rgb(0, 0, 0)' / '#000'
alert( getStyle(box, 'opacity') ); // '0.6'
alert( getStyle(box, 'float') ); // 'none'
&lt;/script&gt;
</pre>
<div class="art_copy">
原载于：雨夜带刀&#8217;s Blog<br />
本文链接：<a href="http://stylechen.com/getstyle2.html">http://stylechen.com/getstyle2.html</a><br />
如需转载请以链接形式注明原载与原文地址。
</div>
]]></content:encoded>
			<wfw:commentRss>http://stylechen.com/getstyle2.html/feed</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>replace的妙用</title>
		<link>http://stylechen.com/jsreplace.html</link>
		<comments>http://stylechen.com/jsreplace.html#comments</comments>
		<pubDate>Tue, 13 Sep 2011 12:27:06 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[replace]]></category>

		<guid isPermaLink="false">http://stylechen.com/?p=420</guid>
		<description><![CDATA[<p>最近在看<a href="#">司徒正美</a>的ajax框架代码的时候发现一段有意思的代码：</p>
<pre class="brush: c-sharp">
"get post".replace( dom.rword, function(method){
	dom[ method ] = function( url, data, callback, type ) {
		if ( dom.isFunction(data) ) {
			type = type &#124;&#124; callback;
			callback = data;
			data = undefined;
		}
		return dom.ajax({
			type: method,
			url: url,
			data: data,
			success: callback,
			dataType: type
		});
	};
});
</pre>]]></description>
			<content:encoded><![CDATA[<p>最近在看<a href="http://www.cnblogs.com/rubylouvre/" target="_blank">司徒正美</a>的ajax框架代码的时候发现一段有意思的代码：</p>
<pre class="brush: c-sharp">
"get post".replace( dom.rword, function(method){
	dom[ method ] = function( url, data, callback, type ) {
		if ( dom.isFunction(data) ) {
			type = type || callback;
			callback = data;
			data = undefined;
		}
		return dom.ajax({
			type: method,
			url: url,
			data: data,
			success: callback,
			dataType: type
		});
	};
});
</pre>
<p>从ajax框架代码中提取出来的，无法单独运行，我再加点代码让这段代码可以正常运行：</p>
<pre class="brush: c-sharp">
var dom = {
	rword : /[^, ]+/g,
	isFunction : function( obj ){
		return typeof obj === 'function';
	},
	ajax : function( options ){
		console.debug( options );
	}
};
</pre>
<p>调用的结果如下：</p>
<pre class="brush: c-sharp">
alert( typeof dom.get === 'function' );  //true
alert( typeof dom.post === 'function' ); //true
</pre>
<p>replace()方法可以用来替换和搜索字符串，第一个参数是一个正则表达式，第二个参数是要进行替换的字符串，第二个参数还可以是一个函数。上面的代码中，replace()的第二个参数就是一个函数，使用replace()方法生成了两个几乎一样的函数，如果按照常规的写法要定义两个函数肯定是要一个一个的来写，如果将上面的函数进行拆分，可以写成下面的形式：</p>
<pre class="brush: c-sharp">
dom.get = function( url, data, callback, type ) {
		if ( dom.isFunction(data) ) {
			type = type || callback;
			callback = data;
			data = undefined;
		}
		return dom.ajax({
			type: 'get',
			url: url,
			data: data,
			success: callback,
			dataType: type
		});
	};
};

dom.post = function( url, data, callback, type ) {
		if ( dom.isFunction(data) ) {
			type = type || callback;
			callback = data;
			data = undefined;
		}
		return dom.ajax({
			type: 'post',
			url: url,
			data: data,
			success: callback,
			dataType: type
		});
	};
};
</pre>
<p>如果几个函数体基本都相同的函数使用replace()方法来写，可以大大节省代码量，这个办法太精妙了。</p>
<div class="art_copy">
原载于：雨夜带刀&#8217;s Blog<br />
本文链接：<a href="http://stylechen.com/jsreplace.html">http://stylechen.com/jsreplace.html</a><br />
如需转载请以链接形式注明原载或原文地址。
</div>
]]></content:encoded>
			<wfw:commentRss>http://stylechen.com/jsreplace.html/feed</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
	</channel>
</rss>

