没有使用jQuery的$(document).ready()等效方法是什么?

2412

我有一个使用了$(document).ready的脚本,但它没有使用jQuery的其他任何功能。 我想通过去除jQuery依赖来减轻它的负担。

我如何在不使用jQuery的情况下实现自己的$(document).ready功能? 我知道使用window.onload将不同,因为window.onload会在所有图像、框架等都加载完成后触发。


302
不是完全相同的功能。 - Joel Mueller
46
正如这个答案所述,如果你只需要jQuery中的 $(document).ready,那么你可以通过在页面底部而不是顶部运行代码来轻松解决这个问题。 HTML5Boilerplate正是采用了这种方法。 - Blazemonger
5
为什么不直接使用DOMContentLoaded事件?它可以在IE9及以上版本上使用。http://caniuse.com/domcontentloaded https://developer.mozilla.org/en-US/docs/Web/Events/DOMContentLoaded - Brock
值得一看:https://developer.mozilla.org/zh-CN/docs/Web/API/Document/readyState - Andrew
https://developer.mozilla.org/en-US/docs/Web/API/Document/DOMContentLoaded_event - Kithraya
我认为一个有趣的答案是解析jQuery是如何做到这一点的? - Chris Pink
39个回答

0

如果您将jQuery加载在BODY底部附近,但在编写jQuery(<func>)或jQuery(document)。ready(<func>)的代码时遇到问题,请查看Github上的jqShim

它不是重新创建自己的文档就绪函数,而是简单地保留函数,直到jQuery可用,然后按预期使用jQuery。将jQuery移动到body底部的目的是加快页面加载速度,您仍然可以通过在模板头中内联jqShim.min.js来实现它。

最终,我编写了此代码以将WordPress中的所有脚本移动到页脚,现在只需将此shim代码直接放在页眉中即可。


0

适用于所有已知的浏览器(通过BrowserStack测试)。IE6+,Safari 1+,Chrome 1+,Opera等。使用DOMContentLoaded,并回退到document.documentElement.doScroll()window.onload

/*! https://github.com/Kithraya/DOMContentLoaded v1.2.6 | MIT License */

DOMContentLoaded.version = "1.2.6";

function DOMContentLoaded() { "use strict";
    
    var ael = 'addEventListener', rel = 'removeEventListener', aev = 'attachEvent', dev = 'detachEvent';
    var alreadyRun = false, // for use in the idempotent function ready()
        funcs = arguments;
    
    // old versions of JS return '[object Object]' for null.
    function type(obj) { return (obj === null) ? 'null' : Object.prototype.toString.call(obj).slice(8,-1).toLowerCase() }
    function microtime() { return + new Date() } 
    
     /* document.readyState === 'complete' reports correctly in every browser I have tested, including IE.
        But IE6 to 10 don't return the correct readyState values as per the spec:
        readyState is sometimes 'interactive', even when the DOM isn't accessible in IE6/7 so checking for the onreadystatechange event like jQuery does is not optimal
        readyState is complete at basically the same time as 'window.onload' (they're functionally equivalent, within a few tenths of a second)
        Accessing undefined properties of a defined object (document) will not throw an error (in case readyState is undefined).
     */
    
    // Check for IE < 11 via conditional compilation
    /// values: 5?: IE5, 5.5?: IE5.5, 5.6/5.7: IE6/7, 5.8: IE8, 9: IE9, 10: IE10, 11*: (IE11 older doc mode), undefined: IE11 / NOT IE
    var jscript_version = Number( new Function("/*@cc_on return @_jscript_version; @*\/")() ) || NaN;
    
    // check if the DOM has already loaded
    if (document.readyState === 'complete') { ready(null); return; }  // here we send null as the readyTime, since we don't know when the DOM became ready.
    
    if (jscript_version < 9) { doIEScrollCheck(); return; } // For IE<9 poll document.documentElement.doScroll(), no further actions are needed.
    
     /* 
        Chrome, Edge, Firefox, IE9+, Opera 9+, Safari 3.1+, Android Webview, Chrome for Android, Edge Mobile, 
        Firefox for Android 4+, Opera for Android, iOS Safari, Samsung Internet, etc, support addEventListener
        And IE9+ supports 'DOMContentLoaded' 
     */
        
    if (document[ael]) {
        document[ael]("DOMContentLoaded", ready, false); 
        window[ael]("load", ready, false); // fallback to the load event in case addEventListener is supported, but not DOMContentLoaded
    } else 
    if (aev in window) { window[aev]('onload', ready);
        /* Old Opera has a default of window.attachEvent being falsy, so we use the in operator instead
           https://dev.opera.com/blog/window-event-attachevent-detachevent-script-onreadystatechange/

           Honestly if somebody is using a browser so outdated AND obscure (like Opera 7 where neither addEventListener 
           nor "DOMContLoaded" is supported, they deserve to wait for the full page).
           I CBA testing whether readyState === 'interactive' is truly interactive in browsers designed in 2003. I just assume it isn't (like in IE6-8). 
        */
    } else { // fallback to queue window.onload that will always work
       addOnload(ready);
    }
    
    
    // This function allows us to preserve any original window.onload handlers (in super old browsers where this is even necessary), 
    // while keeping the option to chain onloads, and dequeue them.
    
    function addOnload(fn) { var prev = window.onload; // old window.onload, which could be set by this function, or elsewhere
        
        // we add a function queue list to allow for dequeueing 
        // addOnload.queue is the queue of functions that we will run when when the DOM is ready
        if ( type( addOnload.queue ) !== 'array') { addOnload.queue = [];
            if ( type(prev) === 'function') { addOnload.queue.push( prev ); } // add the previously defined event handler
        }
        
        if (typeof fn === 'function') { addOnload.queue.push(fn) }

        window.onload = function() { // iterate through the queued functions
            for (var i = 0; i < addOnload.queue.length; i++) { addOnload.queue[i]() } 
        };
    }   

    // remove a queued window.onload function from the chain (simplified); 
    
    function dequeueOnload(fn) { var q = addOnload.queue, i = 0;
    
        // sort through the queued functions in addOnload.queue until we find `fn`
        if (type( q ) === 'array') {        // if found, remove from the queue
            for (; i < q.length; i++) { ;;(fn === q[i]) ? q.splice(i, 1) : 0; } // void( (fn === q[i]) ? q.splice(i, 1) : 0 ) 
        }
    }
    
    function ready(ev) { // idempotent event handler function
        if (alreadyRun) {return} alreadyRun = true; 
        
        // this time is when the DOM has loaded (or if all else fails, when it was actually possible to inference the DOM has loaded via a 'load' event)
        // perhaps this should be `null` if we have to inference readyTime via a 'load' event, but this functionality is better.
        var readyTime = microtime(); 
        
        detach(); // detach any event handlers
                        
        // run the functions
        for (var i=0; i < funcs.length; i++) {  var func = funcs[i];
            
            if (type(func) === 'function') {
                func.call(document, { 'readyTime': (ev === null ? null : readyTime), 'funcExecuteTime': microtime() }, func); 
                // jquery calls 'ready' with `this` being set to document, so we'll do the same. 
            }       
        }
    }

    function detach() {
        if (document[rel]) { 
            document[rel]("DOMContentLoaded", ready); window[rel]("load", ready);
        } else
        if (dev in window) { window[dev]("onload", ready); } 
        else {
            dequeueOnload(ready);
        }                                                               
    }
    
    function doIEScrollCheck() { // for use in IE < 9 only.
        if ( window.frameElement ) { 
            // we're in an <iframe> or similar
            // the document.documentElemeent.doScroll technique does not work if we're not at the top-level (parent document)

            try { window.attachEvent("onload", ready); } catch (e) { } // attach to onload if were in an <iframe> in IE as there's no way to tell otherwise
            
            return;
        } 
        try {
            document.documentElement.doScroll('left');  // when this statement no longer throws, the DOM is accessible in old IE
        } catch(error) {
            setTimeout(function() {
                (document.readyState === 'complete') ? ready() : doIEScrollCheck();
            }, 50);
            return;
        }
        ready();
    }
}

使用方法:

<script>
DOMContentLoaded(function(e) { console.log(e) });
</script>

0
(function(f){
  if(document.readyState != "loading") f();
  else document.addEventListener("DOMContentLoaded", f);
})(function(){
  console.log("The Document is ready");
});

这个回答有什么其他答案没有的优点? - dwjohnston
它使用自包含闭包(不会填充全局“window”作用域),适用于所有浏览器且非常紧凑。我没有看到其他类似的答案。 - Dustin Poissant
它甚至在DOM已经加载后仍然起作用(就像jQuery.ready一样),这是大多数答案未能做到的。 - Dustin Poissant

-1

这是一个不错的https://dev59.com/A3RA5IYBdhLWcg3w2x6W#11810957穷人解决方案。有一条评论提到了在紧急情况下退出的对策。这是我的修改。

function doTheMagic(counter) {
  alert("It worked on " + counter);
}

// wait for document ready then call handler function
var checkLoad = function(counter) {
  counter++;
  if (document.readyState != "complete" && counter<1000) {
    var fn = function() { checkLoad(counter); };
    setTimeout(fn,10);
  } else doTheMagic(counter);
};
checkLoad(0);

-1

编辑@duskwuff的编辑,以支持Internet Explorer 8。区别在于对正则表达式测试函数test的新调用和使用匿名函数的setTimeout。

此外,我将超时设置为99。

function ready(f){/in/.test(document.readyState)?setTimeout(function(){ready(f);},99):f();}

-2

试试这个:

function ready(callback){
    if(typeof callback === "function"){
        document.addEventListener("DOMContentLoaded", callback);
        window.addEventListener("load", callback);
    }else{
        throw new Error("Sorry, I can not run this!");
    }
}
ready(function(){
    console.log("It worked!");
});

2
你会运行两次回调函数。 - Andrew

-2

jQuery 中的 ready 函数有很多功能。坦白地说,除非您的网站输出非常小,否则我不认为替换它有什么意义。 jQuery 是一个相当小的库,它处理所有你以后需要的各种跨浏览器的事情。

无论如何,在这里发布它没有太大的意义,只需打开 jQuery 并查看 bindReady 方法即可。

它首先调用 document.addEventListener("DOMContentLoaded")document.attachEvent('onreadystatechange'),具体取决于事件模型,然后从那里开始执行。


-4
简而言之,我们可以使用JavaScript方法来代替jQuery中使用的$(document).ready()。
<script>
    document.addEventListener("DOMContentLoaded", function_name, false);
    function function_name(){
        statements;
    }
</script>

因此,当页面准备就绪即DOMContentLoaded时,函数function_name()才会被调用。

1
每当一个高票问题出现在首页时,检查日期确保你不是在回复一个非常古老的帖子通常是值得的。(我可能会回复的一种情况是,如果多年来有一个新的解决方案,而原来没有。虽然DOMContentLoaded肯定已经被提到了。) - Katana314

-7
如果您想支持Internet Explorer 7+(无怪癖、兼容性和其他烦恼)、最新版Chrome、最新版Safari、最新版Firefox和无iframes - 只需这样做即可:

is_loaded = false
callbacks = []

loaded = ->
  is_loaded = true
  for i in [0...callbacks.length]
    callbacks[i].call document
  callbacks = []

content_loaded = ->
  document.removeEventListener "DOMContentLoaded", content_loaded, true
  loaded()

state_changed = ->
  if document.readyState is "complete"
    document.detachEvent "onreadystatechange", state_changed
    loaded()

if !!document.addEventListener
  document.addEventListener "DOMContentLoaded", content_loaded, true
else
  document.attachEvent "onreadystatechange", state_changed

dom_ready = (callback) ->
  if is_loaded
    callback.call document
  else
    callbacks.push callback

11
这绝对不是 JavaScript。 - Richard J. Ross III
9
看起来有人在写 CoffeeScript。 - Adrian

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接