纯 JavaScript 等效于 jQuery 的 $.ready() - 如何在页面/DOM 准备好时调用函数 [英] Pure JavaScript equivalent of jQuery's $.ready() - how to call a function when the page/DOM is ready for it

查看:35
本文介绍了纯 JavaScript 等效于 jQuery 的 $.ready() - 如何在页面/DOM 准备好时调用函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

With jQuery, we all know the wonderful .ready() function:

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

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

However, let's say I want to run a function that is written in standard JavaScript with no library backing it, and that I want to launch a function as soon as the page is ready to handle it. What's the proper way to approach this?

我知道我能做到:

window.onload="myFunction()";

或者我可以使用 body 标签:

Or I can use the body tag:

<body onload="myFunction()">

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

Or I can even try at the bottom of the page after everything, but the end body or html tag like:

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

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

What is a cross-browser(old/new)-compliant method of issuing one or more functions in a manner like jQuery's $.ready()?

推荐答案

在没有为您完成所有跨浏览器兼容性的框架的情况下,最简单的做法就是在最后调用您的代码的身体.这比 onload 处理程序执行起来更快,因为它只等待 DOM 准备好,而不是所有图像都加载完毕.而且,这适用于所有浏览器.

The simplest thing to do in the absence of a framework that does all the cross-browser compatibility for you is to just put a call to your code at the end of the body. This is faster to execute than an onload handler because this waits only for the DOM to be ready, not for all images to load. And, this works in every browser.

<!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),如果您希望能够实现像 $(document).ready() 这样的 jQuery 方法你可以从任何地方调用(不用担心调用脚本的位置),你可以使用这样的东西:


For modern browsers (anything from IE9 and newer and any version of Chrome, Firefox or Safari), if you want to be able to implement a jQuery like $(document).ready() method that you can call from anywhere (without worrying about where the calling script is positioned), you can just use something like this:

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() 方法.根据浏览器的功能,它相当复杂.


If you need full cross browser compatibility (including old versions of IE) and you don't want to wait for window.onload, then you probably should go look at how a framework like jQuery implements its $(document).ready() method. It's fairly involved depending upon the capabilities of the browser.

让您了解 jQuery 的作用(它可以在放置 script 标签的任何地方工作).

To give you a little idea what jQuery does (which will work wherever the script tag is placed).

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

If supported, it tries the standard:

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

回退到:

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

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

or for older versions of IE, it uses:

document.attachEvent("onreadystatechange", fn);

回退到:

window.attachEvent("onload", fn);

而且,IE 代码路径中有一些我不太了解的变通方法,但看起来它与框架有关.

And, there are some work-arounds in the IE code path that I don't quite follow, but it looks like it has something to do with frames.

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

Here is a full substitute for jQuery's .ready() written in plain 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

The latest version of the code is shared publicly on GitHub at 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);

<小时>

这已经在:


This has been tested in:

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/

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

Here's a summary of how it works:

  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. 清除函数数组,以便释放它们可能使用的任何闭包.
  1. Create an IIFE (immediately invoked function expression) so we can have non-public state variables.
  2. Declare a public function docReady(fn, context)
  3. When docReady(fn, context) is called, check if the ready handler has already fired. If so, just schedule the newly added callback to fire right after this thread of JS finishes with setTimeout(fn, 1).
  4. If the ready handler has not already fired, then add this new callback to the list of callbacks to be called later.
  5. Check if the document is already ready. If so, execute all ready handlers.
  6. If we haven't installed event listeners yet to know when the document becomes ready, then install them now.
  7. If document.addEventListener exists, then install event handlers using .addEventListener() for both "DOMContentLoaded" and "load" events. The "load" is a backup event for safety and should not be needed.
  8. If document.addEventListener doesn't exist, then install event handlers using .attachEvent() for "onreadystatechange" and "onload" events.
  9. In the onreadystatechange event, check to see if the document.readyState === "complete" and if so, call a function to fire all the ready handlers.
  10. In all the other event handlers, call a function to fire all the ready handlers.
  11. In the function to call all the ready handlers, check a state variable to see if we've already fired. If we have, do nothing. If we haven't yet been called, then loop through the array of ready functions and call each one in the order they were added. Set a flag to indicate these have all been called so they are never executed more than once.
  12. Clear the function array so any closures they might be using can be freed.

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

Handlers registered with docReady() are guaranteed to be fired in the order they were registered.

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

If you call docReady(fn) after the document is already ready, the callback will be scheduled to execute as soon as the current thread of execution completes using setTimeout(fn, 1). This allows the calling code to always assume they are async callbacks that will be called later, even if later is as soon as the current thread of JS finishes and it preserves calling order.

这篇关于纯 JavaScript 等效于 jQuery 的 $.ready() - 如何在页面/DOM 准备好时调用函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆