目录存档: WebDev

兵贵神速-domReady事件原理及相应jQuery源码分析

突然发现,其实本来自以为非常熟悉的一些技术,一旦被人提问以后就会发现自己并不能很好地说明清楚,所以开始认真地写一些非常基础的技术分享,不断查资料探究原理的过程还是非常有趣的

2009090812124856310.jpg

之前被问domReady事件到底是啥,比onload到底好在哪里,一句话说的话就是 DomTree加载完成之后,不等图片加载完domReady就会发生,这时绝大多数JS已经可以正常地运行了(因为页面上初始的dom元素都齐了)。但要说清楚这个domReady的来龙去脉,还真没那么简单。

W3C标准页面加载过程

W3C中关于浏览器加载过程的时间信息的接口标准中有很多页面加载方面的标准,简单地说在chrome里检查performance.timing就可以看见各个事件发生的时间戳
無名の QQ スクリーンショット.png
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才会被触发,但时机是一样的
  • loadEventStart load事件被触发
  • loadEventEnd load事件执行完毕

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

  1. 处理了被调用时已经load事件都发生过的情况(L430)
  2. 对标准浏览器,用domContentLoaded事件并同时也绑上load事件作为fallback来支持早期版本 (L437)
  3. 对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

[PY]复杂情况(GAE)下Django的模板搜寻顺序自定义

嘛,作为下定决心学习新技术系列,正式开始折腾Django了,作为不放到网上跑会死星人放和不折腾会死星人,以及没钱买VPS穷鳖星人果断选择了在GAE上搭Django环境来玩,多亏python系和google系的众多Geek,让我有很多已有的open-source的project可以引用(后附清单)

因为引用的东西很多,所以目录管理成了很重要的一个问题,模板目录是一个比较复杂的问题,因为众多app中既有引用的app,可能需要用公用的模板去覆盖(比如openid登录),也有自己的实际项目的app,需要覆盖掉公用的模板,所以我在根目录建立了templates_override和templates_fallback来区分,然后如何设置对应的settings.py就成了问题

翻手册翻文档翻源码,需要自定义一个类来解决这个问题,下面是settings.py的片段

?View Code PYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
from django.template.loaders.filesystem import Loader as fsLoader
class settingsLoader(fsLoader):
    def __init__(self, dirs = None):
        self.dirs = dirs
    def load_template_source(self, template_name, template_dirs=None):
        if not template_dirs:
            template_dirs = self.dirs
        return super(settingsLoader, self).load_template_source(template_name, template_dirs)
TEMPLATE_LOADERS = (
    ('settings.settingsLoader', [os.path.join(os.path.dirname(__file__), 'templates_override')]),
    'django.template.loaders.app_directories.load_template_source',
    ('settings.settingsLoader', [os.path.join(os.path.dirname(__file__), 'templates_fallback')]),
)

目前来看Django还是比较顺手的,就是墙内部署GAE实在蛋疼……尤其是有这么多引用的app,文件数量很大……万幸GAE部署的时候不是一个个文件上传而是打包一炮上传的  

附录:目前引用的project清单

Permanent link to this article: http://mcfog.com/p735/py%e5%a4%8d%e6%9d%82%e6%83%85%e5%86%b5gae%e4%b8%8bdjango%e7%9a%84%e6%a8%a1%e6%9d%bf%e6%90%9c%e5%af%bb%e9%a1%ba%e5%ba%8f%e8%87%aa%e5%ae%9a%e4%b9%89.fog

[PHP]微博用分割消息代码(避免分割URL等语素

给自己的推特机器人用的代码,避免分割URL、@、#,另外会在后面表明消息总条数和当前编号,另外由于多数客户端都是新的在前面,所以逆向输出以方便阅读

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
public function tweet($msg) {
    $msg = trim($msg);
    if(($len = mb_strlen($msg))>130) {
        if($len > 1200) gx::throw_('tooooo long msg x_x');
 
        $nonbreak = '%((?:@[a-zA-Z0-9_\-]+)|(?:#[\S]+)|(?:https?://[\w\s\.]+(?:/\S*)?))%';
        $units = preg_split($nonbreak, $msg,-1, PREG_SPLIT_DELIM_CAPTURE);
        $msgs = array();
        $buffer = '';
        while($u=array_shift($units)) {
            if(mb_strlen($buffer)+mb_strlen($u) > 120) {
                if(preg_match($nonbreak, $u)) {//this unit is non-break!
                    $msgs[] = $buffer;
                    $buffer = $u;
                    continue;
                }
                //break words
                $buffer .= $u;
                while(mb_strlen($buffer)>120) {
                    $msgs[] = mb_substr($buffer,0,120);
                    $buffer = mb_substr($buffer,120);
                }
            } else {//not too long yet, wait for next unit
                $buffer .= $u;
            }
        }
        $now = $all = count($msgs);
        $objs = array();
        while($m = array_pop($msgs)) {//output in reversed order
            $objs[] = $this->tweet($m."($now/$all)");
            $now--;
        }
        return $objs;
    }
    list($code, $response) = $this->request('1/statuses/update.json', 'POST', array(
      'status' => $msg
    ));
 
    if ($code == 200) {
      return json_decode($response);
    } else {
      gx::throw_(htmlentities($response), $code);
    }
}

Permanent link to this article: http://mcfog.com/p731/php%e5%be%ae%e5%8d%9a%e7%94%a8%e5%88%86%e5%89%b2%e6%b6%88%e6%81%af%e4%bb%a3%e7%a0%81%e9%81%bf%e5%85%8d%e5%88%86%e5%89%b2url%e7%ad%89%e8%af%ad%e7%b4%a0.fog

[PHP]QueryPath中的编码问题

其实发现QueryPath这个神奇的类库已经是几年前的事情了,QueryPath是一个PHP的DOM操作类库,仿jQuery风格。最近终于有机会来实战一把,却遇到了万恶的编码问题,根据API文档用convert_to_encoding来控制编码却一直失败(源码是UTF-8的),拿起了传说中的霰弹枪一顿乱打之后,还是在PHP官网的手册评论里面找到了答案,嗯,来一段代码或许更清楚一些

1
2
3
4
5
		return htmlqp($html, null,
			array(
				//'convert_from_encoding'=>'UTF-8',
				'convert_to_encoding'=>'HTML-ENTITIES'
			))->find('.tml_item')->text();

Permanent link to this article: http://mcfog.com/p728/phpquerypath%e4%b8%ad%e7%9a%84%e7%bc%96%e7%a0%81%e9%97%ae%e9%a2%98.fog

真·SWF图片幻灯(JS-ActionScript通信)

图片幻灯是一个异常古老而经典的Flash应用,然而实际应用时发现网上找的多数幻灯效果都停留在上个世纪的水平,所谓的JS+SWF不过是document.write罢了

正好在项目里要用图片幻灯,也思考了一下,Flash的唯一长处在于显示图片切换效果的时候更加流畅、自然,但在灵活性上输JS无数条大街,于是自己动手写了个SWF图片幻灯,非常简单,对外接口只有2个:一个JS回调方法loadImage(url),载入url指向的图片,可选的用flashvar传递的初始图片

此SWF就做一件事:收到loadImage(或者flashvar的初值)的时候,显示一个ajax loading的gif,然后去读图片,图片读到以后随机一种效果显示出来覆盖掉原来的图片 文字说明啊,定时切换啊什么的这些功能就扔给JS来实现了,用jQuery的话都是优雅地数行代码轻松搞定的问题(tips:如果需要和点击互动,请自行在flash上罩一层DIV即可)

演示地址: http://p.mcfog.com/mcfocus/ SWF文件请自抠

推荐配合jQuery以及jquery.swfobject

Permanent link to this article: http://mcfog.com/p676/%e7%9c%9f%c2%b7swf%e5%9b%be%e7%89%87%e5%b9%bb%e7%81%afjs-actionscript%e9%80%9a%e4%bf%a1.fog

userscript开发笔记:contentEval解决访问全局JS变量/资源问题

嗯,整了一个userscript 玩玩,过程中发现UserScript访问页面中定义的JS变量(比如说jQuery的美刀$) 在不同环境(FF+GreaseMonkey/Chrome原生/Chrome+不同的userscript管理扩展)下有各种稀奇古怪的问题,FF的GreaseMonkey里的unsafeWindow丑陋又不靠谱,Chrome原生干脆不支持unsafeWindow,Chrome的一些插件尝试了支持unsafeWindow但即不靠谱,还要多装一个东西……
搜索了一下找到一个很好的解决方案,在这里记录一下
继续阅读 »

Permanent link to this article: http://mcfog.com/p665/userscript%e5%bc%80%e5%8f%91%e7%ac%94%e8%ae%b0%ef%bc%9acontenteval%e8%a7%a3%e5%86%b3%e8%ae%bf%e9%97%ae%e5%85%a8%e5%b1%80js%e5%8f%98%e9%87%8f%e8%b5%84%e6%ba%90%e9%97%ae%e9%a2%98.fog

Pages: 1 2 3 >>

旧文章 «

3.141592653589793238462643383279502884197169399
初音