jQuery的$ .ready()的纯JavaScript等价物 – 如何在页面/ DOM准备好时调用一个函数
好吧,这可能只是一个愚蠢的问题,虽然我确信还有很多其他人不时地提出同样的问题。 我,我只是想100%确定这一点。 有了jQuery,我们都知道这很棒
$('document').ready(function(){});
然而,假设我想运行一个用标准JavaScript编写的函数,而没有库支持它,而且我希望在页面准备好处理它时立即启动一个函数。 什么是正确的方法来解决这个问题?
我知道我可以这样做:
window.onload="myFunction()";
…或者我可以使用body
标签:
<body onload="myFunction()">
…或者甚至可以在页面的底部尝试所有内容,但最终的body
或html
标签如:
<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/
以下是它的工作原理的总结:
- 创build一个IIFE(立即调用的函数expression式),这样我们就可以拥有非公共的状态variables。
- 声明一个公共函数
docReady(fn, context)
- 当
docReady(fn, context)
,检查ready handler是否已经被触发。 如果是这样的话,只要在JS的这个线程以setTimeout(fn, 1)
结束之后安排新添加的callback就可以了。 - 如果就绪处理程序尚未触发,则将此新callback添加到稍后调用的callback列表中。
- 检查文档是否已经准备好。 如果是,请执行所有准备好的处理程序。
- 如果我们还没有安装事件监听器,但是不知道文档何时准备就绪,请立即安装它们。
- 如果
document.addEventListener
存在,则为"DOMContentLoaded"
和"load"
事件安装使用.addEventListener()
事件处理程序。 “负载”是安全的备份事件,不应该被需要。 - 如果
document.addEventListener
不存在,则使用.attachEvent()
为"onreadystatechange"
和"onload"
事件安装事件处理程序。 - 在
onreadystatechange
事件中,检查document.readyState === "complete"
是否document.readyState === "complete"
,如果是,则调用一个函数来触发所有就绪处理程序。 - 在所有其他事件处理程序中,调用一个函数来触发所有就绪处理程序。
- 在调用所有就绪处理函数的函数中,检查一个状态variables,看看我们是否已经被触发。 如果我们有,什么都不要做。 如果我们还没有被调用,然后遍历已经准备就绪的函数,并按照它们添加的顺序调用每个函数。 设置一个标志来表示这些全部被调用,所以它们永远不会被执行多次。
- 清除函数数组,以便它们可能使用的任何闭包都可以被释放。
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(如未uninitialized
和loading
,前两个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来处理依赖关系。
如果您在使用VANILLA纯JavaScript而不使用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是否准备就绪? 。