jQuery的$ .ready()的纯JavaScript等价物 – 如何在页面/ DOM准备好时调用一个函数

好吧,这可能只是一个愚蠢的问题,虽然我确信还有很多其他人不时地提出同样的问题。 我,我只是想100%确定这一点。 有了jQuery,我们都知道这很棒

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

然而,假设我想运行一个用标准JavaScript编写的函数,而没有库支持它,而且我希望在页面准备好处理它时立即启动一个函数。 什么是正确的方法来解决这个问题?

我知道我可以这样做:

 window.onload="myFunction()"; 

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

 <body onload="myFunction()"> 

…或者甚至可以在页面的底部尝试所有内容,但最终的bodyhtml标签如:

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

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

在没有为您提供所有跨浏览器兼容性的框架的情况下,最简单的事情就是在本体末尾调用您的代码。 这比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()方法。 这取决于浏览器的function。

给你一个小的想法,jQuery的function(无论脚本标签放在哪里都可以工作)。

如果支持,则尝试标准:

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

回退到:

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

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

 document.attachEvent("onreadystatechange", fn); 

回退到:

 window.attachEvent("onload", fn); 

而且,在IE代码path中还有一些我不太喜欢的方法,但是看起来它和框架有关。


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

 (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); 

这已经过testing:

 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 

工作实施和testing平台: http : //jsfiddle.net/jfriend00/YfD3C/


以下是它的工作原理的总结:

  1. 创build一个IIFE(立即调用的函数expression式),这样我们就可以拥有非公共的状态variables。
  2. 声明一个公共函数docReady(fn, context)
  3. docReady(fn, context) ,检查ready handler是否已经被触发。 如果是这样的话,只要在JS的这个线程以setTimeout(fn, 1)结束之后安排新添加的callback就可以了。
  4. 如果就绪处理程序尚未触发,则将此新callback添加到稍后调用的callback列表中。
  5. 检查文档是否已经准备好。 如果是,请执行所有准备好的处理程序。
  6. 如果我们还没有安装事件监听器,但是不知道文档何时准备就绪,请立即安装它们。
  7. 如果document.addEventListener存在,则为"DOMContentLoaded""load"事件安装使用.addEventListener()事件处理程序。 “负载”是安全的备份事件,不应该被需要。
  8. 如果document.addEventListener不存在,则使用.attachEvent()"onreadystatechange""onload"事件安装事件处理程序。
  9. onreadystatechange事件中,检查document.readyState === "complete"是否document.readyState === "complete" ,如果是,则调用一个函数来触发所有就绪处理程序。
  10. 在所有其他事件处理程序中,调用一个函数来触发所有就绪处理程序。
  11. 在调用所有就绪处理函数的函数中,检查一个状态variables,看看我们是否已经被触发。 如果我们有,什么都不要做。 如果我们还没有被调用,然后遍历已经准备就绪的函数,并按照它们添加的顺序调用每个函数。 设置一个标志来表示这些全部被调用,所以它们永远不会被执行多次。
  12. 清除函数数组,以便它们可能使用的任何闭包都可以被释放。

docReady()注册的处理程序保证按照注册的顺序被解雇。

如果在文档已准备好之后调用docReady(fn) ,则只要当前的执行线程使用setTimeout(fn, 1)完成,就会调用callback。 这允许调用代码总是假定它们是稍后将被调用的asynchronouscallback,即使稍后只要JS的当前线程完成并且保存调用顺序。

我想在这里提到一些可能的方法,以及在所有浏览器中都可以使用的纯JavaScript技巧

 // 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!'); }); 

正如原始作者所解释的,这里的技巧是我们正在检查document.readyState属性。 如果包含string(如未uninitializedloading ,前两个DOM就绪状态中的5个),我们设置超时并再次检查。 否则,我们执行传递的函数。

这里是所有浏览器都可以使用的jsFiddle技巧

感谢Tutorialzine在他们的书中包含这个。

testingIE9,最新的Firefox和Chrome,也支持IE8。

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

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

更新 – 可重复使用的版本

我刚刚开发了以下内容。 这是一个相当简单的相当于没有向后兼容jQuery或Dom准备。 这可能需要进一步改进。 经过最新版本的Chrome,Firefox和IE(10/11)的testing,并且可以在旧版浏览器中使用。 我会更新,如果我发现任何问题。

 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的asynchronous加载,但是你可能想要先同步加载这个脚本,除非你正在缩小。 我发现它在开发中很有用。

现代浏览器也支持脚本的asynchronous加载,进一步增强了体验。 对asynchronous的支持意味着可以同时下载多个脚本,同时仍然可以呈现页面。 只要注意取决于asynchronous加载的其他脚本,或者使用缩小器或像browserify来处理依赖关系。

如果您在使用VANILLAJavaScript而不使用jQuery,那么您必须使用(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在ready之后会运行。

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

不要混淆以下(这不意味着DOM准备好):

不要使用像这样自我执行的IFFI:

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

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

HubSpot的好人有一个资源,你可以find纯粹的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'); }); 

你的方法(把脚本放在结束标签之前)

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

是支持新老浏览器的可靠方法。

准备

 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 +

我不太清楚你在问什么,但也许这可以帮助:

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

要么:

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

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

看起来像你应该只使用jQuery min

下面是Ram-swaroop的“适用于所有浏览器”方法的清理,非评估版本的方法:

 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); 

另请参见如何在没有框架的情况下检查DOM是否准备就绪?