• 深入理解Javascript封装DOMContentLoaded事件


    最近在写一个Javascript的框架,刚把DOMContentLoaded事件封装好,略带小兴奋,把开发过程中遇到的原理和兼容性问题做篇笔记,省的忘记到处找。

    我们在写js代码的时候,一般都会添加window.onload事件,主要是为了在DOM加载完后可以使用getElementById,getElementsByTagName等方法选取DOM元素进行操作,但是window.load会等到加载完DOM、脚本、CSS,最后加载完图片甚至是iframe中的所有资源才会触发,很多时候网页的图片较多较大,要等最后图片这个耗时大户加载完才执行js明显有些太迟了,很多时候都会影响用户体验。

    很多js框架都有个document.ready的功能,像JQuery的$(document).ready()方法,可以在DOM加载完就立即执行js代码,让图片自个慢慢加载吧。

    document.ready的核心是DOMContentLoaded事件,firefox、chrome、opera、safari、ie9+都可以使用addEventListener(‘DOMContentLoaded’,fn,false)进行事件绑定,而ie6~8不支持DOMContentLoaded事件,所以要针对ie6~8做兼容性处理。

    资料上说ie6~8可以使用document.onreadystatechange事件监听document.readyState状态是否等于complete来判断DOM是否加载完毕,如果页面中嵌有iframe的话,ie6~8的document.readyState会等到iframe中的所有资源加载完才会变成complete,此时iframe变成了耗时大户。但是经过测试,即使页面中没有iframe,当readyState等于complete时,实际触发的是onload事件而不是DOMContentLoaded事件,对这点表示惊奇。

    还好ie有个特有的doScroll方法,当页面DOM未加载完成时,调用doScroll方法时,就会报错,反过来,只要一直间隔调用doScroll直到不报错,那就表示页面DOM加载完毕了,不管图片和iframe中的内容是否加载完毕,此法都有效。

    如果有多个js文件绑定了document.ready事件,为了防止浏览器重复绑定,同时有序执行,可以引入一个事件队列机制来解决。

    以上就是document.ready事件的原理和兼容性问题,下面贴段实例代码,为了方便理解执行过程,使用函数封装的模式,执行过程都写在注释里了,如果有不妥之处欢迎指教。

    //保存domReady的事件队列
    eventQueue = [];
     
    //判断DOM是否加载完毕
    isReady = false;
     
    //判断DOMReady是否绑定
    isBind = false;
     
    /*执行domReady()
     * www.111cn.net
     *@param    {function}
     *@execute  将事件处理程序压入事件队列,并绑定DOMContentLoaded
     *          如果DOM加载已经完成,则立即执行
     *@caller
     */
    function domReady = function(fn){
        if (isReady) {
            fn.call(window);
        }
        else{
            eventQueue.push(fn);
        };
     
        bindReady();
    };
     
    /*domReady事件绑定
     *
     *@param    null
     *@execute  现代浏览器通过addEvListener绑定DOMContentLoaded,包括ie9+
                ie6-8通过判断doScroll判断DOM是否加载完毕
     *@caller   domReady()
     */
    function bindReady = function(){
        if (isReady) return;
        if (isBind) return;
        isBind = true;
     
        if (window.addEventListener) {
            document.addEventListener('DOMContentLoaded',execFn,false);
        }
        else if (window.attachEvent) {
            doScroll();
        };
    }; www.111Cn.net
     
    /*doScroll判断ie6-8的DOM是否加载完成
     *
     *@param    null
     *@execute  doScroll判断DOM是否加载完成
     *@caller   bindReady()
     */
    function doScroll = function(){
        try{
            document.documentElement.doScroll('left');
        }
        catch(error){
            return setTimeout(doScroll,20);
        };
        execFn();
    };
     
    /*执行事件队列
     *
     *@param    null
     *@execute  循环执行队列中的事件处理程序
     *@caller   bindReady()
     */
    function execFn = function(){
        if (!isReady) {
            isReady = true;
            for (var i = 0; i < eventQueue.length; i++) {
                eventQueue[i].call(window);
            };
            eventQueue = [];
        };
    };
     
    //js文件1
    domReady(function(){
        ...
    });
    //js文件2
    domReady(function(){
        ...
    });
     
    

      

    /注意,如果是异步加载的js就不要绑定domReady方法,不然函数不会执行,
    //因为异步加载的js下载之前,DOMContentLoaded已经触发,addEventListener执行时已经监听不到了
    测试页面:都加载了两张很大的图片,onload需要图片加载完才能执行js,DOMContentLoaded只需等到DOM加载完即可执行js。可以打开firebug查看加载过程,每次测试前记得先清理下浏览器缓存。

    onload 例子

    <!DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>onload</title>
    <style type="text/css">
    .div{ 200px;height: 200px;background: red;}
    body{background: url('bg.jpg') no-repeat 0 0;}
    </style> www.111cn.net
    <!--script type="text/javascript" >
     function loadscript(url){
      var head=document.getElementsByTagName('head')[0];
      var s=document.createElement('script');
      s.type='text/javascript';
      s.src=url;
      s.async=false;
      head.appendChild(s);
     }
     loadscript('go.js');
     loadscript('onload2.js');
    </script-->
    <script type="text/javascript" src="go.js"></script>
    <script type="text/javascript">
    window.onload=function(){
     $('.div').mousehover(function(){
      $(this).css('background','green');
     },function(){
      $(this).css('background','red');
     });
    };
    </script>
    </head>
    <body>
     <div class="div">只有等到图片加载完,鼠标移到我身上才会变色呢。<br/><br/><br/><br/><br/>(因为所用框架不支持ie6,请用ie8+测试)</div>
     <img src="bg.jpg">
     <img src="bg2.jpg">
    </body>
    </html>
    

      DOMContentLoaded例子

    <!DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>DOMContentLoaded</title>
    <style type="text/css">
    .div{ 200px;height: 200px;background: red;}
    body{background: url('bg.jpg') no-repeat 0 0;}
    </style>
    <!--script type="text/javascript" >
     function loadscript(url){
      var head=document.getElementsByTagName('head')[0];
      var s=document.createElement('script');
      s.type='text/javascript';
      s.src=url;
      s.async=false;
      head.appendChild(s);
     }
     loadscript('go.js');
     loadscript('onload2.js');
    </script-->
    <script type="text/javascript" src="go.js"></script>
    <script type="text/javascript">
    $(function(){
     $('.div').mousehover(function(){
      $(this).css('background','green');
     },function(){
      $(this).css('background','red');
     });
    })
    </script>
    </head>
    <body>
     <div class="div">在图片还没加载完之前,把鼠标移到我身上会变色哦.<br/><br/><br/><br/><br/>(因为所用框架不支持ie6,请用ie8+测试)</div>
     <img src="bg.jpg">
     <img src="bg2.jpg">
    </body>
    </html>
    

      

  • 相关阅读:
    总结
    ElasticSearch课件1.22
    clickhouse1.0.5
    大数据实时项目(ads层)
    大数据实时项目(dws层)1.2
    大数据实时项目(日活)1.6.1
    01_大数据技术之Spark入门(2.1)
    03_大数据技术之SparkSql(2.0)
    04_大数据技术之SparkStreaming(2.0)
    05_大数据技术之Spark内核解析(1.1)
  • 原文地址:https://www.cnblogs.com/jymz/p/4218258.html
Copyright © 2020-2023  润新知