Vanilla JavaScript 等效于 jQuery 的 $.ready() - 当页面/DOM 准备好时如何调用函数

新手上路,请多包涵

使用 jQuery,我们都知道美妙的 .ready() 函数:

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

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

我知道我可以做到:

window.onload="myFunction()";

或者我可以使用 body 标签:

<body onload="myFunction()">

或者我什至可以在完成所有操作后在页面底部尝试,但最后的 bodyhtml 标记如下:

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

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

原文由 chris 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 514
2 个回答

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

 <!doctype html>
 <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>


对于现代浏览器(来自 IE9 和更新版本的任何浏览器以及任何版本的 Chrome、Firefox 或 Safari),如果您希望能够实现可以从任何地方调用的 jQuery 之类的 $(document).ready() 方法(无需担心调用脚本的位置),您可以使用以下内容:

 function docReady(fn) {
 // see if DOM is already available
 if (document.readyState === "complete" || document.readyState === "interactive") {
 // call on next available tick
 setTimeout(fn, 1);
 } else {
 document.addEventListener("DOMContentLoaded", fn);
 }
 }

用法:

 docReady(function() {
 // DOM is loaded and ready for manipulation here
 });


如果你需要完全的跨浏览器兼容性(包括旧版本的 IE)并且你不想等待 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/


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

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

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

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

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

  5. 检查文件是否已经准备好。如果是这样,则执行所有准备好的处理程序。

  6. 如果我们还没有安装事件监听器,还不知道文档何时准备就绪,那么现在就安装它们。

  7. 如果 document.addEventListener 存在,则使用 .addEventListener()"DOMContentLoaded""load" 事件安装事件处理程序。 “负载”是安全的备用事件,不应该被需要。

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

  9. onreadystatechange 事件中,检查 document.readyState === "complete" ,如果是,调用一个函数来触发所有准备好的处理程序。

  10. 在所有其他事件处理程序中,调用一个函数来触发所有准备好的处理程序。

  11. 在调用所有就绪处理程序的函数中,检查状态变量以查看我们是否已经触发。如果我们有,什么也不做。如果我们还没有被调用,则遍历准备好的函数数组,并按照它们被添加的顺序调用每个函数。设置一个标志以指示这些都已被调用,因此它们永远不会被多次执行。

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

使用 docReady() 注册的处理程序保证按照它们注册的顺序被触发。

如果在文档已经准备好之后调用 docReady(fn) ,回调将被安排在当前执行线程使用 setTimeout(fn, 1) 完成后立即执行。这允许调用代码始终假定它们是稍后将调用的异步回调,即使稍后是 JS 的当前线程完成并且它保留调用顺序。

原文由 jfriend00 发布,翻译遵循 CC BY-SA 4.0 许可协议

如果您在不使用 jQuery 的情况下执行 VANILLAJavaScript ,那么您必须使用(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!");
});

哪个 ALSO 可以像这样写成 SHORTHAND,哪个 jQuery 将在就绪 事件发生 后运行。

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

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

不要使用像这样的自执行的 IIFE

  Example:

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

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

原文由 Tom Stickel 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题