热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

jQuery源码domready分析

一、前言在平时开发web项目时,我们使用jquery框架时,可能经常这样来使用$(document).ready(fn),$(function(){}),这样使用的原因是在浏览器把DOM树渲染好之前,javascript是无法操作没渲染好的DOM节点。其实除了$(document).ready(fn),$(function(){})写法外,还有两种让dom渲染

  一、前言

  在平时开发web项目时,我们使用jquery框架时,可能经常这样来使用$(document).ready(fn),$(function(){}),这样使用的原因是在浏览器把DOM树渲染好之前,Javascript是无法操作没渲染好的DOM节点。

  其实除了$(document).ready(fn),$(function(){})写法外,还有两种让dom渲染完之后执行js的写法:

$(document).on('ready', fn2)  //通过on事件绑定函数,通过trigger触发也可以达到

jQuery.ready.promise().done(fn); //通过这种方式也可以实现,jQuery.ready.promise()返回一个deferred对象,done(fn)添加回调方法

  其中具体流程图如下(自己简单画了一下,有错请大家指正)

  jQuery源码dom ready分析

 二、源码部分(建议看这部分是,先理解清楚deferred,promise)

  ①$(function(){}) =>到rootjQuery.ready(selector);

  我们知道,jQuery是由new jQuery.fn.init(selector, context, rootjQuery)实例出来的,对接了两个参数,selector,context

 // 构造函数,定义一个局部变量的jQuery
    jQuery = function (selector, context) {
        // jQuery对象实际上是init的构造函数的引用
        return new jQuery.fn.init(selector, context, rootjQuery);
    }

  当我们使用$(function(){}),则选择器selector参数就变成了funciton,jQuery.fn.init函数判断selector为Funtion时,又指向了rootjQuery.ready(selector),就是$(document).ready(fn);

rootjQuery = $(document)
jQuery.fn = jquery.prototype = {
  init:function(selector,context,rootjQuery){
if (jQuery.isFunction(selector)) { // 引用非静态成员ready方法,等价于$(document).ready(selector) return rootjQuery.ready(selector); }
  }
  
} 

 

  ②$(document).on("ready",fn) => 到jQuery(document).trigger("ready").off("ready");

  要理解$(document).on("ready",fn),我们要看ready部分

    //扩展方法到jquery****************************************
    jQuery.extend({
        /**
        记录监听DOMContentLoaded事件(DOM是否加载完成),加载完成设置为true(类似一个开关)
        * type {Boolean} 默认为false,表示页面未加载完。当页面DOM加载完成,设置为true
        */
        isReady: false,
 
        /**
        需要预加载的观察者数量
        * type {Number} 观察者数量
        */
        readyWait: 1,
 
        /**
        DOM加载完成,执行预加载
        * @param {Boolean} wait 为true表示锁定委托人,为false表示释放委托人
        */
        ready: function (wait) {
 
            if (wait === true ? --jQuery.readyWait : jQuery.isReady) {
                return;
            }
 
            // 检测body是否存在(IE的一个bug),存在继续向下执行,不存在进入语句,执行setTimeout定时器,直到body存在
            if (!document.body) {
                return setTimeout(jQuery.ready);
            }
 
 
            // 开关,记录DOM加载完成
            jQuery.isReady = true;
 
            // 检测所有的观察者是否执行完成
            if (wait !== true && --jQuery.readyWait > 0) {
                return;
            }
 

            // 委托人readyList通知观察者,开始执行回调函数,并将document作为这些函数的上下文环境
            readyList.resolveWith(document, [jQuery]);
 
            // 检测trigger方法是否存在,触发绑定在document上的事件,执行完成之后并解绑
            //    $(document).on('ready', fn2);
            //    $(document).ready(fn1);
            //这里的fn1会先执行,自己的ready事件绑定的fn2回调后执行
            if (jQuery.fn.trigger) {
                jQuery(document).trigger("ready").off("ready");
            }
        }
    })    

 

  ③$(document).ready(fn)

    jQuery.fn = jquery.prototype = {
    if (jQuery.isFunction(selector)) {
        // 引用非静态成员ready方法
        return rootjQuery.ready(selector);
    },
     ready: function (fn) {
                // Add the callback
                // promise类似一种事件委托,相当于一个创建委托人(已创建则无需创建,通过readyList判断是否已创建),在DOM加载完成之后,委托人会通知观察者done去执行回调函数fn
                //调委托函数,返回deferred对象,添加done(fn)回调函数
                jQuery.ready.promise().done(fn);
     
                return this;
            }
    }

 

 

最后dom ready相关部分源码详细如下:

 // 构造函数,定义一个局部变量的jQuery
    jQuery = function (selector, context) {
        // jQuery对象实际上是init的构造函数的引用
        return new jQuery.fn.init(selector, context, rootjQuery);
    }
    
  // 就绪事件处理程序
    completed = function (event) {

        // document.readyState 判断文档加载状态,'complete'代表文档已经完全加载
        
        if (document.addEventListener || event.type === "load" || document.readyState === "complete") {
        
            detach(); //清理方法
            
            jQuery.ready();//执行延迟加载方法
        }
    }

    
    // 清理DOMContentLoaded事件处理程序,为DOM事件做好准备,触发jQuery.ready方法
    detach = function () {
        // 标准的W3C监听事件
        if (document.addEventListener) {
            //删除DOMContentLoaded监听事件
            document.removeEventListener("DOMContentLoaded", completed, false);
            window.removeEventListener("load", completed, false);
        } else {
            // 针对IE,非标准的浏览器
            // 删除onreadystatechange监听事件
            document.detachEvent("onreadystatechange", completed);
            window.detachEvent("onload", completed);
        }
    };    
    var  readyList,rootjQuery=$(document);
    jQuery.fn = jquery.prototype = {
    if (jQuery.isFunction(selector)) {
        // 引用非静态成员ready方法
        return rootjQuery.ready(selector);
    },
     ready: function (fn) {
                // Add the callback
                // promise类似一种事件委托,相当于一个创建委托人(已创建则无需创建,通过readyList判断是否已创建),在DOM加载完成之后,委托人会通知观察者done去执行回调函数fn
                //调委托函数,返回deferred对象,添加done(fn)回调函数
                jQuery.ready.promise().done(fn);
     
                return this;
            }
    }
    
    
    //扩展方法到jquery****************************************
    jQuery.extend({
        /**
        记录监听DOMContentLoaded事件(DOM是否加载完成),加载完成设置为true(类似一个开关)
        * type {Boolean} 默认为false,表示页面未加载完。当页面DOM加载完成,设置为true
        */
        isReady: false,
 
        /**
        需要预加载的观察者数量
        * type {Number} 观察者数量
        */
        readyWait: 1,
 
        /**
        锁定或释放预加载委托人
        * @param {Boolean} hold 为true表示锁定预加载委托人,为false表示释放委托人
        */
        holdReady: function (hold) {
            if (hold) {
                jQuery.readyWait++;
            } else {
                jQuery.ready(true);
            }
        },
 
        /**
        DOM加载完成,执行预加载
        * @param {Boolean} wait 为true表示锁定委托人,为false表示释放委托人
        */
        ready: function (wait) {
 
            // Abort if there are pending holds or we're already ready
            if (wait === true ? --jQuery.readyWait : jQuery.isReady) {
                return;
            }
 
            // 检测body是否存在(IE的一个bug),存在继续向下执行,不存在进入语句,执行setTimeout定时器,直到body存在
            if (!document.body) {
                return setTimeout(jQuery.ready);
            }
 
 
            // 开关,记录DOM加载完成
            jQuery.isReady = true;
 
            // 检测所有的观察者是否执行完成
            if (wait !== true && --jQuery.readyWait > 0) {
                return;
            }
 

            // 委托人readyList通知观察者,开始执行回调函数,并将document作为这些函数的上下文环境
            readyList.resolveWith(document, [jQuery]);
 
            // 检测trigger方法是否存在,触发绑定在document上的事件,执行完成之后并解绑
            //    $(document).on('ready', fn2);
            //    $(document).ready(fn1);
            //这里的fn1会先执行,自己的ready事件绑定的fn2回调后执行
            if (jQuery.fn.trigger) {
                jQuery(document).trigger("ready").off("ready");
            }
        }
    })    
        
        
    // 预加载委托函数
    jQuery.ready.promise = function (obj) {
        // 判断预加载委托人是否存在
        if (!readyList) {
            // 创建一个预加载委托人
            readyList = jQuery.Deferred();
            /**
            W3C标准DOM浏览器
            * 0-uninitialized:XML 对象被产生,但没有任何文件被加载。
            * 1-loading:加载程序进行中,但文件尚未开始解析。
            * 2-loaded:部分的文件已经加载且进行解析,但对象模型尚未生效。
            * 3-interactive:仅对已加载的部分文件有效,在此情况下,对象模型是有效但只读的。
            * 4-complete:文件已完全加载,代表加载成功。
            */

            // 检测document内容是否加载完成,加载完成返回true,否者返回false
            if (document.readyState === "complete") {

                /**
                * 函数延迟0毫秒执行并不是立即执行, 而是等浏览器运行完挂起的事件句柄和已经更新完文档状态之后才
                * 运行这个函数.详情见《Javascript Definition Guide 5th Edition(Javascript权威指南第5版)》
                * 函数延迟1毫秒或者为空代表立即执行
                */
                // 立即调用jQuery.ready方法,执行预加载内容
                setTimeout(jQuery.ready);
 
                // 检测是否符合W3C标准事件模型(IE不支持)
            } else if (document.addEventListener) {
                // 监听DOMContentLoaded事件,当DOM加载完成,触发completed方法
                // DOMContentLoaded事件在DOM加载完成时触发
                // completed方法,目的是先删除监听DOMContentLoaded事件,然后执行jQuery.ready()方法。(删除事件,清除内存)
                document.addEventListener("DOMContentLoaded", completed, false);
 
                // 若失败,给window.onload注册一个jQuery.ready方法,并在页面加载完成之后执行,目的是兼容低版本浏览器,防止出现预加载失败
                window.addEventListener("load", completed, false);
 
                // IE事件模型
            } else {
                // 监听onreadystatechange事件,当DOM加载完成,触发DOMContentLoaded方法
                document.attachEvent("onreadystatechange", completed);
 
                // 同理,若失败,兼容操作.
                window.attachEvent("onload", completed);
 
                // 声明一个变量top, 目的:如果在IE下,文档不是嵌套在框架中, 就不断地检测文档是否准备就绪.
                var top = false;
 
                try {
                    //检测是否嵌套在框架中,嵌套在框架中返回false,不嵌套返回document.documentElement 
                    //返回嵌入当前window对象的元素(比如  或者 ),如果当前window对象已经是顶层窗口,则返回null,如果有框架则document.documentElement返回为null.
                    top = window.frameElement == null && document.documentElement;
                } catch (e) { }
 
                // 检测是否存在doScroll方法
                // IE支持doScroll,doScroll捕捉页面垂直和水平的滚动,他在ondocumentready 事件触发之后,onload事件触发之前触发
                if (top && top.doScroll) {
                    (function doScrollCheck() {
                        if (!jQuery.isReady) {
 
                            try {
                                top.doScroll("left");
                            } catch (e) {
                                return setTimeout(doScrollCheck, 50);
                            }
 
                            detach();

                            // 执行jQuery.ready方法,说明页面DOM加载完成,其实就是改变deffered对象的状态,resolvewith完成,触发完成回调done(fn)
                            jQuery.ready();
                        }
                    })();
                }
            }
        }
        // 返回一个被限制的委托人(只能执行,不能改变的状态的委托人)
        return readyList.promise(obj);
    };

 


推荐阅读
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • Nginx使用(server参数配置)
    本文介绍了Nginx的使用,重点讲解了server参数配置,包括端口号、主机名、根目录等内容。同时,还介绍了Nginx的反向代理功能。 ... [详细]
  • PHP图片截取方法及应用实例
    本文介绍了使用PHP动态切割JPEG图片的方法,并提供了应用实例,包括截取视频图、提取文章内容中的图片地址、裁切图片等问题。详细介绍了相关的PHP函数和参数的使用,以及图片切割的具体步骤。同时,还提供了一些注意事项和优化建议。通过本文的学习,读者可以掌握PHP图片截取的技巧,实现自己的需求。 ... [详细]
  • Webpack5内置处理图片资源的配置方法
    本文介绍了在Webpack5中处理图片资源的配置方法。在Webpack4中,我们需要使用file-loader和url-loader来处理图片资源,但是在Webpack5中,这两个Loader的功能已经被内置到Webpack中,我们只需要简单配置即可实现图片资源的处理。本文还介绍了一些常用的配置方法,如匹配不同类型的图片文件、设置输出路径等。通过本文的学习,读者可以快速掌握Webpack5处理图片资源的方法。 ... [详细]
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • 如何使用Java获取服务器硬件信息和磁盘负载率
    本文介绍了使用Java编程语言获取服务器硬件信息和磁盘负载率的方法。首先在远程服务器上搭建一个支持服务端语言的HTTP服务,并获取服务器的磁盘信息,并将结果输出。然后在本地使用JS编写一个AJAX脚本,远程请求服务端的程序,得到结果并展示给用户。其中还介绍了如何提取硬盘序列号的方法。 ... [详细]
  • Mac OS 升级到11.2.2 Eclipse打不开了,报错Failed to create the Java Virtual Machine
    本文介绍了在Mac OS升级到11.2.2版本后,使用Eclipse打开时出现报错Failed to create the Java Virtual Machine的问题,并提供了解决方法。 ... [详细]
  • javascript  – 概述在Firefox上无法正常工作
    我试图提出一些自定义大纲,以达到一些Web可访问性建议.但我不能用Firefox制作.这就是它在Chrome上的外观:而那个图标实际上是一个锚点.在Firefox上,它只概述了整个 ... [详细]
  • 本文详细介绍了Linux中进程控制块PCBtask_struct结构体的结构和作用,包括进程状态、进程号、待处理信号、进程地址空间、调度标志、锁深度、基本时间片、调度策略以及内存管理信息等方面的内容。阅读本文可以更加深入地了解Linux进程管理的原理和机制。 ... [详细]
  • 本文介绍了高校天文共享平台的开发过程中的思考和规划。该平台旨在为高校学生提供天象预报、科普知识、观测活动、图片分享等功能。文章分析了项目的技术栈选择、网站前端布局、业务流程、数据库结构等方面,并总结了项目存在的问题,如前后端未分离、代码混乱等。作者表示希望通过记录和规划,能够理清思路,进一步完善该平台。 ... [详细]
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • 本文介绍了绕过WAF的XSS检测机制的方法,包括确定payload结构、测试和混淆。同时提出了一种构建XSS payload的方法,该payload与安全机制使用的正则表达式不匹配。通过清理用户输入、转义输出、使用文档对象模型(DOM)接收器和源、实施适当的跨域资源共享(CORS)策略和其他安全策略,可以有效阻止XSS漏洞。但是,WAF或自定义过滤器仍然被广泛使用来增加安全性。本文的方法可以绕过这种安全机制,构建与正则表达式不匹配的XSS payload。 ... [详细]
author-avatar
能然然520
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有