
Permanent link to this article: http://mcfog.com/p866/its-mcfogs-note.fog
兵贵神速-domReady事件原理及相应jQuery源码分析
突然发现,其实本来自以为非常熟悉的一些技术,一旦被人提问以后就会发现自己并不能很好地说明清楚,所以开始认真地写一些非常基础的技术分享,不断查资料探究原理的过程还是非常有趣的

之前被问domReady事件到底是啥,比onload到底好在哪里,一句话说的话就是 DomTree加载完成之后,不等图片加载完domReady就会发生,这时绝大多数JS已经可以正常地运行了(因为页面上初始的dom元素都齐了)。但要说清楚这个domReady的来龙去脉,还真没那么简单。
W3C标准页面加载过程
W3C中关于浏览器加载过程的时间信息的接口标准中有很多页面加载方面的标准,简单地说在chrome里检查performance.timing就可以看见各个事件发生的时间戳

W3C的这张图很好地描述了浏览器加载页面的整个过程,除了domLoading(开始解析dom树)可能会抢先在responseEnd(网络接受应答完毕)之前发生之外,基本是从左到右时间顺序发生

这里我们关注processing以及之后的部分,我尝试解释一下各个事件的含义
domLoading浏览器拿到了一些HTML代码,开始解析dom树- 设置
document.readyState为”loading”(也就触发了readyStateChange) - 注意引用的外部script会阻塞dom树解析
- 解析过程比较复杂这里不多涉及,可以参考W3C的Parsing部分的标准
- 设置
domInteractive浏览器初步解析完dom树,但并不稳定,仍残余一些工作,W3C的相关标准链接- 设置
document.readyState为”interactive” - 清空打开标签堆栈,也就是强制关闭仍未闭合的标签
- 写在html中的script元素,含defer属性且不含async属性的会在这个时机被执行
- 此时才会触发
domContentLoaded事件
- 设置
domContentLoaded其实就是俗称的domReady事件,此时dom树已经稳定- aync属性的script元素在这个时机被执行
- 满足另一种条件的script元素也是这个时机被执行,我没能看懂,估计是来自于
document.createElement创建出来的某种情况
domComplete需要加载的资源(包括图片、iFrame等等等等)以及脚本全部执行完毕- 设置
document.readyState为”complete” - 这个事件和load事件的区别是load事件必须要有Browsing Context才会被触发,但时机是一样的
- 设置
loadEventStartload事件被触发loadEventEndload事件执行完毕
domReady的现实
一般来说我们希望页面上的脚本尽量快速地被执行,但很多情景下都需要完整可操作的dom树才能保证脚本执行正确,尤其是IE6在interactive以及之前的阶段,如果用JS操作了dom树会直接“无法打开站点”。
问题是绝大多数情况下,图片(或者swf等)都不是JS脚本需要的资源,也就是说我们要找的时间点是dom树完整之后,图片加载之前的一个地方来执行我们的脚本,也就是所谓的domReady。对于主流标准浏览器来说,domContentLoaded正是我们需要的domReady,但很不幸我们生活在有IE的世界里,所以需要为IE找一个替代品(doScroll checking)
jQuery相关源码解析
这里看的是jQuery1.7.2的代码,相关部分在core.js里面
jQuery里domReady的绑定代码是jQuery(callback)或者jQuery(document).ready(callback),最终都落到了jQuery.fn.ready上,readyList维护注册在ready时间上的callback集合,jQuery.ready是jQuery希望绑定在domReady上的callback,里面会执行所有通过各种手段绑定上ready的callback,于是我们主要看jQuery.bindReady
- 处理了被调用时已经load事件都发生过的情况(L430)
- 对标准浏览器,用domContentLoaded事件并同时也绑上load事件作为fallback来支持早期版本 (L437)
- 对IE,首先绑定了readystatechange的complete的情况,以及load事件,然后尝试使用
doScrollCheck来更早地触发ready事件(L445)
domReady的问题
domReady虽然通常比load快不少,但此时并不能保证外部资源全部load到,所以需要读img/包含img的元素的尺寸时不能直接在domReady里面跑,flash及类似的外部资源也有类似的问题
参考:DOM Ready 详解
Permanent link to this article: http://mcfog.com/p848/%e5%85%b5%e8%b4%b5%e7%a5%9e%e9%80%9f-domready%e4%ba%8b%e4%bb%b6%e5%8e%9f%e7%90%86%e5%8f%8a%e7%9b%b8%e5%ba%94jquery%e6%ba%90%e7%a0%81%e5%88%86%e6%9e%90.fog
Nite.PointDenoiser的正确用法
摘要
在OpenNI/Kinect框架开发中,如何正确地使用Nite.PointDenoiser (XnVPointDenoiser) 来对手势进行平滑、降噪处理
问题
嗯,最近调教Kinect做手势识别,上OpenNI/Nite框架。
追踪手部用Nite.SessionManager很轻松就能弄出样子来,但是用带除噪的Nite.PointDenoiser代替Nite.PointControl却无效,坐标依旧不停地颤动。
原因
在GoogleGroups里找到了一些有关的讨论,简单地说就是根据API手册,相关的事件如OnPrimaryPointUpdate在PointDenoiser类中压根没有重载,
翻API手册,可以找到一张难看的类图

作为一个PointFilter的子类,PointDenoiser是有信息输入(订阅者)MessageListener 和信息输出(发布者)MessageGenerator两者的特性的,在XnVPointDenoiser Member List页面可以很清楚地看到PrimaryPointUpdate等事件都是属于PointControl也就是MessageListener继承而来的,所以这些事件中的信息必然是和输入的信息一模一样不可能被降噪,要获取降噪后的结果,我们需要的是MessageGenerator下的方法,也就是PointDenoiser::AddListener
解决
最简单的方法就是把PointControl的实例传给PointDenoiser::AddListener,然后监听PointControl的相关事件,大致代码如下
1 2 3 4 5 6 7 8 | ScriptNode scrNode; SessionManager session = Context.CreateFromXmlFile("./path/to/config.xml", out scrNode); PointControl pointControl = new PointControl(); PointDenoiser pointDenoiser = new PointDenoiser(); session.AddListener(pointDenoiser); pointDenoiser.AddListener(pointControl); pointControl.PrimaryPointUpdate += new EventHandler<HandEventArgs>(pointControl_PrimaryPointUpdate); |
最后贴个完整的类,转发了PointDenoiser的三个配置属性,另外在当中插入了一个broadcaster预留给子类一个访问和控制的接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | using System; using NITE; namespace GGI.Model { class PointDenoiseControl:PointControl { public PointDenoiseControl(SessionManager _session) : base() { session = _session; broadcaster = new Broadcaster(); broadcaster.AddListener(this); pointDenoiser = new PointDenoiser(); session.AddListener(pointDenoiser); pointDenoiser.AddListener(broadcaster); } protected Broadcaster broadcaster; protected SessionManager session; protected PointDenoiser pointDenoiser; public float DistanceThreshold { get { return pointDenoiser.DistanceThreshold; } set { pointDenoiser.DistanceThreshold = value; } } public float CloseRatio { get { return pointDenoiser.CloseRatio; } set { pointDenoiser.CloseRatio = value; } } public float FarRation { get { return pointDenoiser.FarRatio; } set { pointDenoiser.FarRatio = value; } } } } |
PS: 还是MarkDown爽,怎么就没早点想起来装个MarkDown的WP插件呢= =
Permanent link to this article: http://mcfog.com/p787/nite-pointdenoiser%e7%9a%84%e6%ad%a3%e7%a1%ae%e7%94%a8%e6%b3%95.fog
流水账
一不小心又是一年多没有写这个分类了,随便翻翻前面,按短的算我入宅都有3年了……这个分类最早从高考前的月经贴开始,而我现在已经在开始头疼能不能正常毕业了……
不知道是不是一边上学一边实习的缘故,总觉得最近的状态没有暑假的时候的好了,工作方面开始出现一些差错和不足了,在公司抽不出时间来看新番了,自信心似乎也没以前那么强了
学校这边还要还挂科的债,而且大四这帮课竟然充满了PJ,实在是没有天理(顺带一提这周一我翘了一个PJ的检查,昨天下午去找了助教演示那个残缺的PJ被鄙视了,今天上午还要再去)
找工作方面,我越来越佩服那些玩海投战术的同志了……我就投了俩,其中支付宝还渺无音讯呢,都觉得焦头烂额了……不过我还是坚持自己的做法,投的少&准,希望这周末的面试能果断拿下一个offer先吧……
PS,我发现这个分类其实可以改名叫黑历史了……
PS2,准备尝试实施GTD了……
Permanent link to this article: http://mcfog.com/p781/%e6%b5%81%e6%b0%b4%e8%b4%a6.fog
又见拖延症
唔……还有5门考试和4篇文章,今天下午还就有一门压根还没还没看的期末,睡觉前还应该交掉一篇文章,我却还在各种拖……
希望这不是第一万五千四百九十八次六月底Orz
Permanent link to this article: http://mcfog.com/p770/%e5%8f%88%e8%a7%81%e6%8b%96%e5%bb%b6%e7%97%87.fog
小黑X220入手,上果照
港行,i5-2410M,2G,320G,蓝指六,6.5K送货上门
等去学校了再搞个内存条就行了


Permanent link to this article: http://mcfog.com/p760/%e5%b0%8f%e9%bb%91x220%e5%85%a5%e6%89%8b%ef%bc%8c%e4%b8%8a%e6%9e%9c%e7%85%a7.fog


