首页 文章

纯JavaScript相当于jQuery的$ .ready() - 如何在页面/ DOM准备就绪时调用函数[重复]

提问于
浏览
991

这个问题在这里已有答案:

好吧,这可能只是一个愚蠢的问题,但我确信还有很多其他人不时会问同样的问题 . 我,我只是想以任何方式100%确定 . 有了jQuery,我们都知道这很精彩

$('document').ready(function(){});

但是,假设我想运行一个用标准JavaScript编写的函数,没有库支持它,并且我想在页面准备好处理它时立即启动一个函数 . 接近这个的正确方法是什么?

我知道我能做到:

window.onload="myFunction()";

...或者我可以使用 body 标签:

<body onload="myFunction()">

...或者我甚至可以在所有内容之后尝试在页面底部,但结束 bodyhtml 标记如:

<script type="text/javascript">
   myFunction();
</script>

什么是以jQuery的 $.ready() 方式发布一个或多个函数的跨浏览器(旧/新)兼容方法?

10 回答

  • 73

    如果您在没有jQuery的情况下执行 VANILLA plain JavaScript ,则必须使用(Internet Explorer 9或更高版本):

    document.addEventListener("DOMContentLoaded", function(event) {
        // Your code to run since DOM is loaded and ready
    });
    

    以上是jQuery .ready 的等价物:

    $(document).ready(function() {
        console.log("Ready!");
    });
    

    哪个也可以像这样编写SHORTHAND,jQuery将在准备就绪后运行occurs .

    $(function() {
        console.log("ready!");
    });
    

    下面不要混淆(这不是DOM准备好的):

    请勿使用自动执行的IIFE

    Example:
    
    (function() {
       // Your page initialization code here  - WRONG
       // The DOM will be available here   - WRONG
    })();
    

    这个IIFE不会等你的DOM加载 . (我甚至在谈论最新版的Chrome浏览器!)

  • 114

    您的方法(在关闭正文标记之前放置脚本)

    <script>
       myFunction()
    </script>
    </body>
    </html>
    

    是一种支持新旧浏览器的可靠方法 .

  • 93

    这是一个清理过的,非eval-using版本的Ram-swaroop's "works in all browsers"品种 - 适用于所有浏览器!

    function onReady(yourMethod) {
      var readyStateCheckInterval = setInterval(function() {
        if (document && document.readyState === 'complete') { // Or 'interactive'
          clearInterval(readyStateCheckInterval);
          yourMethod();
        }
      }, 10);
    }
    // use like
    onReady(function() { alert('hello'); } );
    

    它会等待额外10毫秒来运行,但是,这是一个更复杂的方式,不应该:

    function onReady(yourMethod) {
      if (document.readyState === 'complete') { // Or also compare to 'interactive'
        setTimeout(yourMethod, 1); // Schedule to run immediately
      }
      else {
        readyStateCheckInterval = setInterval(function() {
          if (document.readyState === 'complete') { // Or also compare to 'interactive'
            clearInterval(readyStateCheckInterval);
            yourMethod();
          }
        }, 10);
      }
    }
    
    // Use like
    onReady(function() { alert('hello'); } );
    
    // Or
    onReady(functionName);
    

    另见How to check if DOM is ready without a framework? .

  • 2

    在IE9中测试过,最新的Firefox和Chrome也在IE8中得到了支持 .

    document.onreadystatechange = function () {
      var state = document.readyState;
      if (state == 'interactive') {
          init();
      } else if (state == 'complete') {
          initOnCompleteLoad();
      }
    }​;
    

    示例:http://jsfiddle.net/electricvisions/Jacck/

    UPDATE - reusable version

    我刚开发了以下内容 . 这是一个相当简单的等价于jQuery或Dom准备,没有向后兼容性 . 它可能需要进一步完善 . 在最新版本的Chrome,Firefox和IE(10/11)中进行了测试,并且可以在旧版浏览器中使用,如评论所述 . 如果我发现任何问题,我会更新 .

    window.readyHandlers = [];
    window.ready = function ready(handler) {
      window.readyHandlers.push(handler);
      handleState();
    };
    
    window.handleState = function handleState () {
      if (['interactive', 'complete'].indexOf(document.readyState) > -1) {
        while(window.readyHandlers.length > 0) {
          (window.readyHandlers.shift())();
        }
      }
    };
    
    document.onreadystatechange = window.handleState;
    

    用法:

    ready(function () {
      // your code here
    });
    

    它是为处理JS的异步加载而编写的,但您可能希望首先同步加载此脚本,除非您正在缩小 . 我发现它在开发中很有用 .

    现代浏览器还支持脚本的异步加载,这进一步增强了体验 . 支持异步意味着可以同时下载多个脚本,同时仍然可以呈现页面 . 根据异步加载的其他脚本或使用minifier或类似browserify来处理依赖项时,请注意 .

  • 17

    准备好了

    function ready(fn){var d=document;(d.readyState=='loading')?d.addEventListener('DOMContentLoaded',fn):fn();}
    

    使用像

    ready(function(){
        //some code
    });
    

    用于自调用代码

    (function(fn){var d=document;(d.readyState=='loading')?d.addEventListener('DOMContentLoaded',fn):fn();})(function(){
    
        //Some Code here
        //DOM is avaliable
        //var h1s = document.querySelector("h1");
    
    });
    

    支持:IE9

  • 7

    我想在这里提到一些可能的方法和 pure javascript trick which works across all browsers

    // with jQuery 
    $(document).ready(function(){ /* ... */ });
    
    // shorter jQuery version 
    $(function(){ /* ... */ });
    
    // without jQuery (doesn't work in older IEs)
    document.addEventListener('DOMContentLoaded', function(){ 
        // your code goes here
    }, false);
    
    // and here's the trick (works everywhere)
    function r(f){/in/.test(document.readyState)?setTimeout('r('+f+')',9):f()}
    // use like
    r(function(){
        alert('DOM Ready!');
    });
    

    正如original author所解释的,这里的技巧是我们正在检查 document.readyState 属性 . 如果它包含字符串 in (如 uninitializedloading ,前5个中的前两个DOM ready states),我们设置超时并再次检查 . 否则,我们执行传递的函数 .

    这是jsFiddle的伎俩 works across all browsers.

    感谢Tutorialzine将其包含在他们的书中 .

  • 3

    HubSpot的好人有一个资源,你可以找到纯Javascript方法来实现很多jQuery的好处 - 包括 ready

    http://youmightnotneedjquery.com/#ready

    function ready(fn) {
      if (document.readyState != 'loading'){
        fn();
      } else if (document.addEventListener) {
        document.addEventListener('DOMContentLoaded', fn);
      } else {
        document.attachEvent('onreadystatechange', function() {
          if (document.readyState != 'loading')
            fn();
        });
      }
    }
    

    示例内联用法:

    ready(function() { alert('hello'); });
    
  • 4

    在没有为您提供所有跨浏览器兼容性的框架的情况下,最简单的方法就是在正文末尾调用代码 . 这比 onload 处理程序执行起来更快,因为它只等待DOM准备好,而不是所有图像都要加载 . 而且,这适用于每个浏览器 .

    <html>
    <head>
    </head>
    <body>
    Your HTML here
    
    <script>
    // self executing function here
    (function() {
       // your page initialization code here
       // the DOM will be available here
    
    })();
    </script>
    </body>
    </html>
    

    如果你真的不想等待 window.onload ,那么你可能应该去看看像jQuery这样的框架如何实现它的 $(document).ready() 方法 . 根据浏览器的功能,它相当复杂 .

    为了让您了解jQuery的功能(在脚本标记放置的任何位置都可以使用) .

    如果支持,它会尝试标准:

    document.addEventListener('DOMContentLoaded', fn, false);
    

    回落:

    window.addEventListener('load', fn, false )
    

    或者对于旧版本的IE,它使用:

    document.attachEvent("onreadystatechange", fn);
    

    回落:

    window.attachEvent("onload", fn);
    

    并且,在IE代码路径中有一些我不太关注的解决方法,但看起来它与帧有关 .


    这是用简单的javascript编写的jQuery的 .ready() 的完全替代:

    (function(funcName, baseObj) {
        // The public function name defaults to window.docReady
        // but you can pass in your own object and own function name and those will be used
        // if you want to put them in a different namespace
        funcName = funcName || "docReady";
        baseObj = baseObj || window;
        var readyList = [];
        var readyFired = false;
        var readyEventHandlersInstalled = false;
    
        // call this when the document is ready
        // this function protects itself against being called more than once
        function ready() {
            if (!readyFired) {
                // this must be set to true before we start calling callbacks
                readyFired = true;
                for (var i = 0; i < readyList.length; i++) {
                    // if a callback here happens to add new ready handlers,
                    // the docReady() function will see that it already fired
                    // and will schedule the callback to run right after
                    // this event loop finishes so all handlers will still execute
                    // in order and no new ones will be added to the readyList
                    // while we are processing the list
                    readyList[i].fn.call(window, readyList[i].ctx);
                }
                // allow any closures held by these functions to free
                readyList = [];
            }
        }
    
        function readyStateChange() {
            if ( document.readyState === "complete" ) {
                ready();
            }
        }
    
        // This is the one public interface
        // docReady(fn, context);
        // the context argument is optional - if present, it will be passed
        // as an argument to the callback
        baseObj[funcName] = function(callback, context) {
            if (typeof callback !== "function") {
                throw new TypeError("callback for docReady(fn) must be a function");
            }
            // if ready has already fired, then just schedule the callback
            // to fire asynchronously, but right away
            if (readyFired) {
                setTimeout(function() {callback(context);}, 1);
                return;
            } else {
                // add the function and context to the list
                readyList.push({fn: callback, ctx: context});
            }
            // if document already ready to go, schedule the ready function to run
            if (document.readyState === "complete") {
                setTimeout(ready, 1);
            } else if (!readyEventHandlersInstalled) {
                // otherwise if we don't have event handlers installed, install them
                if (document.addEventListener) {
                    // first choice is DOMContentLoaded event
                    document.addEventListener("DOMContentLoaded", ready, false);
                    // backup is window load event
                    window.addEventListener("load", ready, false);
                } else {
                    // must be IE
                    document.attachEvent("onreadystatechange", readyStateChange);
                    window.attachEvent("onload", ready);
                }
                readyEventHandlersInstalled = true;
            }
        }
    })("docReady", window);
    

    最新版本的代码在GitHub上公开分享https://github.com/jfriend00/docReady

    用法:

    // pass a function reference
    docReady(fn);
    
    // use an anonymous function
    docReady(function() {
        // code here
    });
    
    // pass a function reference and a context
    // the context will be passed to the function as the first argument
    docReady(fn, context);
    
    // use an anonymous function with a context
    docReady(function(context) {
        // code here that can use the context argument that was passed to docReady
    }, ctx);
    

    这已经过测试:

    IE6 and up
    Firefox 3.6 and up
    Chrome 14 and up
    Safari 5.1 and up
    Opera 11.6 and up
    Multiple iOS devices
    Multiple Android devices
    

    工作实施和试验台:http://jsfiddle.net/jfriend00/YfD3C/

    以下是它的工作原理摘要:

    • 创建一个IIFE(立即调用的函数表达式),这样我们就可以拥有非公共状态变量 .

    • 声明一个公共函数 docReady(fn, context)

    • 调用 docReady(fn, context) 时,检查就绪处理程序是否已经触发 . 如果是这样,只需在JS的这个线程完成 setTimeout(fn, 1) 后立即安排新添加的回调 .

    • 如果尚未触发就绪处理程序,则将此新回调添加到稍后要调用的回调列表中 .

    • 检查文档是否已准备就绪 . 如果是,请执行所有就绪处理程序 .

    • 如果我们尚未安装事件监听器但尚未知道文档何时准备就绪,那么现在就安装它们 .

    • 如果 document.addEventListener 存在,则使用 .addEventListener()"DOMContentLoaded""load" 事件安装事件处理程序 . "load"是安全的备份事件,不需要 .

    • 如果 document.addEventListener 不存在,则使用 .attachEvent()"onreadystatechange""onload" 事件安装事件处理程序 .

    • onreadystatechange 事件中,检查是否 document.readyState === "complete" 以及如果是,调用函数来触发所有就绪处理程序 .

    • 在所有其他事件处理程序中,调用函数来触发所有就绪处理程序 .

    • 在调用所有就绪处理程序的函数中,检查一个状态变量以查看我们是否已被调用,然后循环遍历就绪函数数组并按照它们被添加的顺序调用每个函数 . 设置一个标志以指示这些都已被调用,因此它们永远不会被执行多次 .

    • 清除函数数组,以便释放它们可能正在使用的任何闭包 .

    注册 docReady() 的处理程序保证按其注册顺序解雇 .

    如果在文档准备就绪后调用 docReady(fn) ,则将在使用 setTimeout(fn, 1) 完成当前执行线程后立即执行回调 . 这允许调用代码总是假设它们是稍后将调用的异步回调,即使稍后在JS的当前线程完成并且它保留调用顺序之后 .

  • 1448

    我不太确定你在问什么,但也许这有助于:

    window.onload = function(){
        // Code. . .
    
    }
    

    要么:

    window.onload = main;
    
    function main(){
        // Code. . .
    
    }
    
  • 4

    document.ondomcontentready=function(){} 应该做的伎俩,但它没有完全的浏览器兼容性 .

    好像你应该只使用jQuery min

相关问题