JavaScript定时器队列

看了高性能JavaScript 快速响应的用户界面,文中提到“为了解决过度使用定时器会造成负面影响,可以使用定时器队列来解决:确保同时只有一个定时器在运行,一个定时器执行完成之后再进入下一个定时器。”,此处的定时器队列我还没有见过,google了一番没能查到什么相关的信息,就自己琢磨了下写了一个。

var TimeoutQ = {
	timerLimit: 2,    //可以通过修改这个参数来控制页面可以生成的定时器数量
	timerCount: 0,    //记录页面已触发的定时器数量
	q: [],   //存放定时器对象的队列

        /**
        * setTimeout方法 与原生的setTimeout方法保持一致
        */
	setTimeout: function ( fn, delay ){

                //如果此刻已触发的定时器数量少于配置的定时器数量限制
                //就立刻设置一个新的定时器
		if( TimeoutQ.timerCount < TimeoutQ.timerLimit ){

			TimeoutQ.timerCount++;

			return window.setTimeout( function (){

                                //当前定时器结束的时候唤醒队列中的定时器
				TimeoutQ.wakeTimer();

				fn();

			}, delay );

		}

                //否则就将当前设置的定时器推入队列等待唤醒
		this.q.push({

			fn: fn,

			delay: delay,

			sleep: +new Date()     //记录推入队列的时间,以便更准确的模拟设置的延时

		});

		return null;

	},

        /**
        * wakeTimer 唤醒定时器队列中等待的定时器
        */
	wakeTimer: function (){

		TimeoutQ.timerCount--;

		if( TimeoutQ.q.length === 0 ){
			return;
		}

		var timeout = TimeoutQ.q.shift(),
			timepassed = +new Date() - timeout.sleep,
			delay = timepassed >= timeout.delay ? 0 : timeout.delay - timepassed;

		TimeoutQ.setTimeout( timeout.fn, delay );

	}

};

现在的问题是,进入队列的超时调用必然会被执行,没办法使用clearTimeout清除。

这里有一个简陋的demo

发表在 JAVASCRIPT | 标签为 , , , | 留下评论

跨浏览器的DOMContentLoaded事件

DOMContentLoaded事件是在DOM已经准备好,而其他资源可能还未下载完毕时触发的。我们可以藉此尽快的给各种交互用的dom绑定交互事件。无奈IE678不支持此事件,需要模拟。能Google到许多相关资源,比如主流框架中DOMContentLoaded事件的实现

目前,在IE中模拟DOMContentLoaded事件有3种方法。

document.attachEvent("onreadystatechange", function(){
    if ( document.readyState === "complete" ) {
        document.detachEvent( "onreadystatechange", arguments.callee );
        //这里触发DOMContentLoaded事件
    }
});
//IE中 script元素的defer属性指示浏览器在dom ready之后再加载该script
document.write("<"+"script id=__onDOMContentLoaded defer src=//:><\/script>");
document.getElementById("__onDOMContentLoaded").onreadystatechange = function() {
    if (this.readyState == "complete") {
        this.onreadystatechange = null;
        //这里触发DOMContentLoaded事件
    }
};
(function(){
    try{
        //doScroll方法只有在dom ready之后可以调用,否则会抛异常
        document.documentElement.doScroll('left');
    }catch(e){
	window.setTimeout( arguments.callee, 0 );
	return;
    }
    //这里触发DOMContentLoaded事件
})();

在3种模拟DOMContentLoaded的方法中,检测document.readyState状态的方法和检测defer的script的方法会因为页面中包含iframe而无法及时触发(要等到iframe加载完毕readyState的值才会变为complete),而调用doScroll的方法可以避免此问题。但是调用doScroll方法在脚本本身运行于iframe中时会在iframe的dom准备好之前就可以使用,我想可能是因为iframe的包含文档已经处于dom ready状态,所以导致doScroll方法可用了。 继续阅读

发表在 JAVASCRIPT | 标签为 , , | 留下评论

jQuery插件Lazy Load Plugin的效果讨论

根据Jan同学的回复,该插件作者已经发布了可用新版本,并在其首页进行了简短的(=。=)说明,新版本的插件要求在编写html结构时就需要做出src替换操作。但是基于该JS插件的wordpress插件jQuery Image Lazy Load WP似乎仍然没有进行更多的更新。

大概半年以前就看见过这个jQuery插件,当时就研究了下,结果发现了很囧的结果。因为最近看到很多博客在使用基于该插件的WordPress插件,就我个人的研究结果来看,这个插件实际上还会增加博客的负担,我觉得应该向无辜的博主们说明一下该插件的实际效果。而一两句话的留言又不太说得清楚,所以写下此文以求更好的说明效果,并且如果您对此有更加深入的看法或者不同的意见,我还希望获得一些技术上的交流,毕竟我个人的研究是有局限性的。

当时在浏览某些博客的时候看见了貌似是图片延迟加载的效果,想看看到底是怎么做的,用谷哥追到了它的主页:Lazy Load plugin for jQuery,大概看了下介绍,然后研究了下它的demo。该插件主要是在页面加载后用js修改所有img标签的src属性为空值并将其原值记录在相应的dom节点上,然后当img标签快要出现在浏览器视口的时候再用记录的图片路径原值去填充img标签的src属性以“达到”延迟加载的目的。

然而,就浏览器对页面的外部资源的加载策略来看这一做法并不能获得其作者想要的效果。这里我们使用Firefox4和firebug 1.7.3来分析一下该插件的live demo页面(已经404了)的情况。

Firefox访问demo时firebug的net面板

从截图中可以看到,浏览器在接收到页面之后,并发了对页面中11个外部资源的请求,包括蓝色的线框标出的一些该插件意图延迟加载的图片在内…也就是说,在该jQuery插件加载完成之前,浏览器很有可能已经对页面上一部分img资源进行请求并下载这些图片了(js已经阻止不了浏览器了!)。我之所以还用红色标记标出了15个请求,是因为该插件之前的版本会造成已加载的图片在被重新赋值src后又发出新的请求,这根本就是帮了倒忙…不过目前最新版本的demo只是偶尔会出现这样的情况了。

现在再看一下我最近看到的一个使用基于该插件的WordPress插件 jQuery Image Lazy Load WP的博客 Var Hi的情况。 继续阅读

发表在 HTTP协议学习, WordPress相关 | 标签为 , , , , | 14 条评论

HTTP Developer’s Handbook 中文翻译 Chapter 5: 第三节 请求(消息)头部(二)

The Cookie Header

Cookie头部是HTTP状态管理机制最关键的部分,将会在Chapter 11-13有详细介绍。

任何与所请求的内容相关的cookie都会添加到Cookie头部传输给Web服务器。就算有多个Cookie值要传输,每个请求也只有一个Cookie头。Cookie是被分号分隔的名值对,例如:

Cookie: fname=chris; lname=shiflett

通常用“设置一个Cookie”和“读取一个Cookie”来形容Web服务器的行为,这会让人们误以为浏览器是任由服务器摆布的。但事实上Cookie数据的交换要求很多的浏览器和服务器之间的合作,比上述的2个短语所描述的要多。

当一个服务器想要设置一个Cookie的时候它需要对浏览器发起一个请求,让浏览器存储一些数据(名值对)以便在后续的请求中返回这些数据。有关设置Cookie的请求和相关标准,请参阅Chapter 6中对Set-Cookie响应头部的介绍。

一个服务器设置Cookie的请求是否会被执行取决于浏览器以及其用户隐私设置。因为Cookie中的数据有可能是私人的,用户有权拒绝支持Cookie。

如果Cookie被接受了,它将会被存储在内存或者硬盘当中,这取决于Set-Cookie响应头中的关键部分,并且它将会被包含在后续的HTTP请求头部当中。通常会误以为在下发了Set-Cookie请求之后能够得知浏览器是否接受了你的Cookie,但是在没有收到后续的请求之前这是没有办法知道的。所以有些开发者会让浏览器在收到设置Cookie的响应后立即再发送一个请求至服务器,以此来决定使用哪种状态管理机制。关于使用Cookie来维护状态的更多信息请参阅Chapter 11,“用Cookie维护HTTP状态”。

Note
相关资料可以在Chapter 6中Set-Cookie响应头的介绍中找到。

继续阅读

发表在 HTTP协议学习 | 标签为 , , , | 留下评论

HTTP Developer’s Handbook 中文翻译 Chapter 5: 第三节 请求(消息)头部(一)

Request Headers

每一个HTTP请求都有一个消息头部(HTTP headers)的集合。这些消息头部提供了那些能够使Web服务器更好地满足请求的信息。尽管这些头部被分成 请求头部,通用头部和实体头部3类,但是每一个头部都有着自己的特有的功能。

Request headers (请求头部)只适用于HTTP请求。HTTP/1.1定义了19个请求头部。我将逐一介绍这些头部,另外还有一个已经被广泛支持并且定义在RFC2109“HTTP状态管理机制”中的Cookie头。

The Accept Header

Accept头的基本功能就是告知Web服务器该请求接受哪些种类的内容类型(Content type),以及在可接受的内容类型的权重(即更倾向于接受哪一种类型的内容)。

使用第一节给出的例子,当我们在Google中搜索“HTTP”关键字时,你可以检查该请求中的Accept头。

Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,
        text/plain;q=0.8, video/x-mng,image/png,image/jpeg,image/gif;q=0.2,
        text/css,*/*;q=0.1

拆解来看,你会发现有几部分拥有相同的格式:

text/xml,application/xml,application/xhtml+xml,text/html;q=0.9
text/plain;q=0.8
video/x-mng,image/png,image/jpeg,image/gif;q=0.2
text/css,*/*;q=0.1

这些条目中的每一个都是由“,”分隔的一或多个内容类型(也可以说是媒体类型(media type))后面紧跟一个由“;”分隔的权重系数(q)组成的。q值越高,请求越倾向于获得其“;”之前的类型的内容。这个q值可以取0到1之间(包含0和1)的任何数,如果没有指定q值的话默认为1。被赋值为0则用于提醒服务器哪些是我不接受的内容类型。但是由于浏览器需要尽可能的兼容已经过时的内容类型,指定不接受某些类型的做法是不常见的。

每一种内容类型都被分成类型(type)和子类型(subtype)。*号可以被用于像 */html这样的类型中,也可以被用于像 text/* 这样的子类型中。常会见到 */*这样的用法。这基本上就允许了接收任何类型的内容(尽管这通常会被赋予最低的权重),因此也是浏览器最后的应急措施。如果浏览器没有明确指定的可接受的内容类型,那么所有内容类型的权重将被赋值为0。就类似于浏览器会无视他们不认识的标签一样。 继续阅读

发表在 HTTP协议学习 | 标签为 , , , | 留下评论

HTTP Developer’s Handbook 中文翻译 Chapter 5:第二节 HTTP请求方法

Request method(请求方法)是HTTP请求最重要的属性之一。方法指明了Web客户端请求的大体意图。他们都有重要的作用,尽管许多方法都不常应用于实践。大多数情况下我只会讨论由HTTP/1.1定义的方法,如果有必要我将会提示其与之前版本的不同。

HTTP/1.1中定义了8种方法:GET, POST, PUT, DELETE, HEAD, TRACE, OPTIONS 和 CONNECT

我将介绍这8种方法是最新的也是大多Web客户端和服务器遵守的。出于兼容性的考虑,通常会适当地介绍之前版本中与HTTP/1.1版本同名的方法。另外,几乎所有的现代Web客户端与服务器都至少支持HTTP/1.0。

The GET Method

Web客户端最常用的方法就是GET。这是当你点击一个链接或者在浏览器地址栏中输入一个URL,你的浏览器应用的请求类型。一个GET请求从根本上讲是一个用来接收位于一个特定URL中的内容。这是HTTP中最简单也是最古老的请求,成为HTTP/0.9中唯一能用的方法。

查询字串(query string), 就是URL中位于?符号之后,#符号之前(如果#存在的话)的部分,由一个或多个名值对用&字符分隔而组成。因此,一个查询字串指定的3个变量就是以下这种格式:

var1=value1&var2=value2&var3=value3

HTML表单中,<form>标签有一个属性method,可能的值有get和post。当赋值为get时,每一个表单域的名字和值会被包含在URL中作为查询字符串,Web客户端会向form表单的action属性中指定的地址发起一个GET请求。

使用GET请求的URL允许用户将URL生成书签,或者生成一个链接,或者发送给朋友等等。例如你可以将Google搜索结果生成书签,这样你每次访问这个书签都可以得到最新的搜索结果。同样的,你也可以在你的个人网站上包含一个这样的链接供用户访问。

对于很多表单来说,POST方法是更合适的。例如,你不会希望用户能够使用一个书签来产生购物订单。你也不会希望敏感的数据出现在浏览器的地址栏中,因为那将会使数据暴露在不怀好意的人的眼中。当你考虑在表单中使用哪一种方法时,要知道GET方法有一些缺点。GET方法在数据参数的长度上有所限制,并且这些限制对于不同的Web客户端和服务器有可能是不同的,这取决于他们的实现。几乎所有的现代Web客户端和服务器,包括Apache服务器,都可以处理1024个字符长度的URL。然而规范警告要当心那些超过255个字符长度的URL。你要知道URL并不仅仅只是包含查询字符串,如果表单有太多的表单项将有可能超出Web客户端或服务器的处理能力。

一个GET请求通常不包含正文内容,也就没有实体头部。

The POST Method

POST方法可以说是HTTP协议最大的进步之一,可以说它推动了Web向真正的交互型应用开发平台的变革。 继续阅读

发表在 HTTP协议学习 | 标签为 , , , | 一条评论

HTTP Developer’s Handbook 中文翻译 Chapter 5:第一节 HTTP请求的语法

一个Http请求(即由Web客户端发往Web服务器的消息),由以下3部分组成:

  • Request line (请求行)
  • HTTP headers (HTTP消息头,以下会简称消息头)
  • Content (消息正文)

HTTP请求的第一行总是请求行(request line),请求行指定了请求方法(request method),资源的位置以及使用的HTTP协议的版本。这3个部分之间用空格隔开。例如:

GET / HTTP/1.1

这个例子指定了GET方法,位于/(文档根目录)下的资源以及 HTTP/1.1 版本的协议。

HTTP请求的第二个部分是 HTTP消息头(HTTP headers) 。 消息头包含了那些可以更清楚地解释Web客户端的请求的信息。可以出现在HTTP请求中的消息头有3种类型:

  • General headers (通用的消息头)
  • Request headers (请求头,与请求控制相关的头部)
  • Entity headers (实体头,与消息正文相关的头部)

没有规定要求消息头遵循上述顺序。因为实体头是与消息正文相关的头部,所以较少在请求中出现。

Note
大多数HTTP请求不包含任何正文内容,因为他们的目的通常就是去请求一些内容。然而你将会看到,允许在请求中发送正文内容这种灵活性是非常有用的。尤其是对于需要交互的网站来说,为了交互需要,用户必须能够发送一些数据。

这里有一个例子,是作者在Chapter 3中使用他的Galeon 1.2.0浏览器在google中搜索HTTP时,浏览器向google的服务器发送的请求。

GET /search?hl=en&q=HTTP&btnG=Google+Search HTTP/1.1
Host: www.google.com
User-Agent: Mozilla/5.0 Galeon/1.2.0 (X11; Linux i686; U;) Gecko/20020326
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,
        text/plain;q=0.8, video/x-mng,image/png,image/jpeg,image/gif;q=0.2,
        text/css,*/*;q=0.1
Accept-Language: en
Accept-Encoding: gzip, deflate, compress;q=0.9
Accept-Charset: ISO-8859-1, utf-8;q=0.66, *;q=0.66
Keep-Alive: 300
Connection: keep-alive

拆解来看,以下是该请求的请求行: 继续阅读

发表在 HTTP协议学习 | 标签为 , , , | 7 条评论

JS 跨浏览器的dom事件封装

上次的 《JS面试题 跨浏览器的事件处理函数的绑定和解绑定》一文中提供的跨浏览器事件处理函数的绑定中缺失了对event对象的跨浏览器处理。这里对它进行补完。

在兼容DOM的浏览器中,无论使用DOM0级(element.onXXX=handler)还是DOM2级(element.addEventListener)来绑定事件处理函数,event对象都会作为事件处理函数的参数被传入。然而在IE中,使用DOM0级方法绑定事件处理函数却没有将event对象当做参数传入,而是作为window对象的一个属性存在。因此,跨浏览器获得一个事件对象需要如下的代码

element.onclick = function (event){
    event = event || window.event;
}

标准的event对象包含以下属性及方法

属性/方法 类型 读写 说明
bubbles Boolean 只读 表明事件是否会冒泡
cancelable Boolean 只读 表明是否可以取消事件的默认行为
currentTarget Element 只读 即绑定该事件处理函数的元素,不一定是事件的目标元素,因为事件可以冒泡至绑定了相应事件的上层元素
eventPhase Integer 只读 调用事件处理程序的阶段:1.捕获阶段 2.处于目标 3.冒泡阶段
type String 只读 事件类型
target Element 只读 事件的目标元素,例如点击的dom中最深层次的元素
preventDefault Function 只读 取消事件的默认行为的方法。只有事件的cancelable为true时调用此方法才有效。
stopPropogation Function 只读 取消事件的进一步冒泡或捕获。

而IE的事件对象包含如下属性 继续阅读

发表在 JAVASCRIPT | 标签为 , | 留下评论

JS面试题 跨浏览器的事件处理函数的绑定和解绑定

题目如题,还有一个条件,要保证事件处理函数的this正确指向绑定的元素。

说来惭愧,第一次做这道题的时候,根本不知道IE的attachEvent方法没有让事件处理函数的this正确指向所绑定的元素,就简单处理了一下跨浏览器的绑定与解绑定。

 

var LIZ = {
    util : {
        addEventListener : function ( element, type, handler ){
            if( element.addEventListener ){
                element.addEventListener(type, handler, false);
            } else if ( element.attachEvent ){
                element.attachEvent('on'+type, handler);
            } else {
                element['on'+type] = handler;
            }
        },
        removeEventListener : function ( element, type, handler ){
              if( element.removeEventListener ){
                  element.removeEventListener(type, handler, false);
             } else if ( element.dettachEvent ){
                 element.dettachEvent('on'+type, handler);
             } else {
                 element['on'+type] = null;
             }
       }
   }
};


那次面试就这样悲剧了-。-回来之后开始研究怎么做。以上代码倒是可以跨浏览器绑定和解绑事件处理函数,但是this指向在IE下是不正确的。让this指向正确很简单,使用handler.call(element)或者handler.apply(element)就可以了么……突然发现这个handler不是由我来调用的,傻X了…… 记得一般会有类似于jQuery.proxy函数的bind方法,可以返回指定context调用的方法,简单一点就是

LIZ.patterns = {
	bind : function ( fn, context ){
	    return function (){
	        return fn.apply( context, arguments );
	    };
	}
};

这样给IE的attachEvent方法传入通过bind方法处理过的函数就可以保证事件处理函数this的正确指向了。然而… 继续阅读

发表在 JAVASCRIPT | 标签为 , | 留下评论

使用WP post view插件和roundaboutJS制作的博客热门文章展区

不知道是什么原因,WordPress没有自带文章浏览次数记录的功能,但是这样的插件还是很多的,因为我的主题叫People(大众脸),所以就挑了很多人用的WP-PostViews插件来制作我的热门文章展区,尽管我连文章都还没几篇……

上个图先

热门文章展区

热门文章展区截图

如果你是直接浏览我的博客中的此文的话,是看不见这个展区的……我个人是认为如果你点击一篇文章之后是想关注其内容而非其他,浏览完毕之后可能还会想这文章不错看看还有没有别的,所以我设置了只有在其他页面才会展示这个展区。

WP-postviews插件提供了一个侧边栏小工具可供显示你网站上的热门文章,或者某个类别下的热门文章,你可以把它拖拽到侧边栏或底部边栏,它还提供了模板编辑,你可以自由编辑这个小工具的html模板。但是这个小工具的模板只能配置一个,现在我需要放在侧边栏和顶部展区的热门文章有不同的展示形式,我希望在侧边栏的热门文章只显示标题和阅读次数,而顶部展区还需要显示文章摘要。这下要在代码里做手脚了。 继续阅读

发表在 JAVASCRIPT, WordPress相关 | 标签为 , , | 一条评论