题 没有jQuery的$(document).ready等价


我有一个使用的脚本 $(document).ready,但它不使用jQuery中的任何其他内容。我想通过删除jQuery依赖项来减轻它。

我怎样才能实现自己的 $(document).ready 功能而不使用jQuery?我知道用的 window.onload 会不一样的 window.onload 在加载所有图像,帧等后触发。


1627
2018-04-28 21:51


起源


<body onload =“yourFunc()”>绝对是最轻的;) - cgp
......而且绝对不是同一个功能。 - Joel Mueller
如 这个答案 如果你想从jQuery那里得到的话,那就是 $(document).ready,您可以通过在页面的最底部而不是在顶部运行代码来轻松解决该问题。 HTML5Boilerplate 使用这种确切的方法。 - Blazemonger
为什么不使用DOMContentLoaded?这是IE9 + caniuse.com/domcontentloaded  developer.mozilla.org/en-US/docs/Web/Events/DOMContentLoaded - Brock
我把我的电话放在文件的最后,这解决了我的问题。调用该函数时,会加载所有内容。 - IgniteCoders


答案:


有一个基于标准的替代品,DOMContentLoaded 这得到了支持 98%的浏览器,虽然不是IE8:

document.addEventListener("DOMContentLoaded", function(event) { 
  //do work
});

jQuery的本机函数比window.onload复杂得多,如下所示。

function bindReady(){
    if ( readyBound ) return;
    readyBound = true;

    // Mozilla, Opera and webkit nightlies currently support this event
    if ( document.addEventListener ) {
        // Use the handy event callback
        document.addEventListener( "DOMContentLoaded", function(){
            document.removeEventListener( "DOMContentLoaded", arguments.callee, false );
            jQuery.ready();
        }, false );

    // If IE event model is used
    } else if ( document.attachEvent ) {
        // ensure firing before onload,
        // maybe late but safe also for iframes
        document.attachEvent("onreadystatechange", function(){
            if ( document.readyState === "complete" ) {
                document.detachEvent( "onreadystatechange", arguments.callee );
                jQuery.ready();
            }
        });

        // If IE and not an iframe
        // continually check to see if the document is ready
        if ( document.documentElement.doScroll && window == window.top ) (function(){
            if ( jQuery.isReady ) return;

            try {
                // If IE is used, use the trick by Diego Perini
                // http://javascript.nwbox.com/IEContentLoaded/
                document.documentElement.doScroll("left");
            } catch( error ) {
                setTimeout( arguments.callee, 0 );
                return;
            }

            // and execute any waiting functions
            jQuery.ready();
        })();
    }

    // A fallback to window.onload, that will always work
    jQuery.event.add( window, "load", jQuery.ready );
}

1053
2018-04-28 21:59



bindReady: github.com/jquery/jquery/blob/master/src/core.js - XP1
如果有人想要他们可以插入的代码,这里是一个实际工作的普通javascript实现: stackoverflow.com/questions/9899372/... - jfriend00
jQuery DOM ready代码似乎被简化了: github.com/jquery/jquery/blob/master/src/core/ready.js - Jose Nobile
我想我们已经准备好从IE8继续前进了......;)。感谢@JoseNobile的链接。 - Con Antonakos
如果之后加载脚本,DOMContentLoaded将无法工作。 JQuery文档随时可执行。 - Jared Insel


编辑:

这是jQuery ready的可行替代品

function ready(callback){
    // in case the document is already rendered
    if (document.readyState!='loading') callback();
    // modern browsers
    else if (document.addEventListener) document.addEventListener('DOMContentLoaded', callback);
    // IE <= 8
    else document.attachEvent('onreadystatechange', function(){
        if (document.readyState=='complete') callback();
    });
}

ready(function(){
    // do something
});

取自 https://plainjs.com/javascript/events/running-code-when-the-document-is-ready-15/


由于接受的答案远非完整,我将“准备好”的功能拼接在一起 jQuery.ready() 基于jQuery 1.6.2源码:

var ready = (function(){

    var readyList,
        DOMContentLoaded,
        class2type = {};
        class2type["[object Boolean]"] = "boolean";
        class2type["[object Number]"] = "number";
        class2type["[object String]"] = "string";
        class2type["[object Function]"] = "function";
        class2type["[object Array]"] = "array";
        class2type["[object Date]"] = "date";
        class2type["[object RegExp]"] = "regexp";
        class2type["[object Object]"] = "object";

    var ReadyObj = {
        // Is the DOM ready to be used? Set to true once it occurs.
        isReady: false,
        // A counter to track how many items to wait for before
        // the ready event fires. See #6781
        readyWait: 1,
        // Hold (or release) the ready event
        holdReady: function( hold ) {
            if ( hold ) {
                ReadyObj.readyWait++;
            } else {
                ReadyObj.ready( true );
            }
        },
        // Handle when the DOM is ready
        ready: function( wait ) {
            // Either a released hold or an DOMready/load event and not yet ready
            if ( (wait === true && !--ReadyObj.readyWait) || (wait !== true && !ReadyObj.isReady) ) {
                // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
                if ( !document.body ) {
                    return setTimeout( ReadyObj.ready, 1 );
                }

                // Remember that the DOM is ready
                ReadyObj.isReady = true;
                // If a normal DOM Ready event fired, decrement, and wait if need be
                if ( wait !== true && --ReadyObj.readyWait > 0 ) {
                    return;
                }
                // If there are functions bound, to execute
                readyList.resolveWith( document, [ ReadyObj ] );

                // Trigger any bound ready events
                //if ( ReadyObj.fn.trigger ) {
                //    ReadyObj( document ).trigger( "ready" ).unbind( "ready" );
                //}
            }
        },
        bindReady: function() {
            if ( readyList ) {
                return;
            }
            readyList = ReadyObj._Deferred();

            // Catch cases where $(document).ready() is called after the
            // browser event has already occurred.
            if ( document.readyState === "complete" ) {
                // Handle it asynchronously to allow scripts the opportunity to delay ready
                return setTimeout( ReadyObj.ready, 1 );
            }

            // Mozilla, Opera and webkit nightlies currently support this event
            if ( document.addEventListener ) {
                // Use the handy event callback
                document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
                // A fallback to window.onload, that will always work
                window.addEventListener( "load", ReadyObj.ready, false );

            // If IE event model is used
            } else if ( document.attachEvent ) {
                // ensure firing before onload,
                // maybe late but safe also for iframes
                document.attachEvent( "onreadystatechange", DOMContentLoaded );

                // A fallback to window.onload, that will always work
                window.attachEvent( "onload", ReadyObj.ready );

                // If IE and not a frame
                // continually check to see if the document is ready
                var toplevel = false;

                try {
                    toplevel = window.frameElement == null;
                } catch(e) {}

                if ( document.documentElement.doScroll && toplevel ) {
                    doScrollCheck();
                }
            }
        },
        _Deferred: function() {
            var // callbacks list
                callbacks = [],
                // stored [ context , args ]
                fired,
                // to avoid firing when already doing so
                firing,
                // flag to know if the deferred has been cancelled
                cancelled,
                // the deferred itself
                deferred  = {

                    // done( f1, f2, ...)
                    done: function() {
                        if ( !cancelled ) {
                            var args = arguments,
                                i,
                                length,
                                elem,
                                type,
                                _fired;
                            if ( fired ) {
                                _fired = fired;
                                fired = 0;
                            }
                            for ( i = 0, length = args.length; i < length; i++ ) {
                                elem = args[ i ];
                                type = ReadyObj.type( elem );
                                if ( type === "array" ) {
                                    deferred.done.apply( deferred, elem );
                                } else if ( type === "function" ) {
                                    callbacks.push( elem );
                                }
                            }
                            if ( _fired ) {
                                deferred.resolveWith( _fired[ 0 ], _fired[ 1 ] );
                            }
                        }
                        return this;
                    },

                    // resolve with given context and args
                    resolveWith: function( context, args ) {
                        if ( !cancelled && !fired && !firing ) {
                            // make sure args are available (#8421)
                            args = args || [];
                            firing = 1;
                            try {
                                while( callbacks[ 0 ] ) {
                                    callbacks.shift().apply( context, args );//shifts a callback, and applies it to document
                                }
                            }
                            finally {
                                fired = [ context, args ];
                                firing = 0;
                            }
                        }
                        return this;
                    },

                    // resolve with this as context and given arguments
                    resolve: function() {
                        deferred.resolveWith( this, arguments );
                        return this;
                    },

                    // Has this deferred been resolved?
                    isResolved: function() {
                        return !!( firing || fired );
                    },

                    // Cancel
                    cancel: function() {
                        cancelled = 1;
                        callbacks = [];
                        return this;
                    }
                };

            return deferred;
        },
        type: function( obj ) {
            return obj == null ?
                String( obj ) :
                class2type[ Object.prototype.toString.call(obj) ] || "object";
        }
    }
    // The DOM ready check for Internet Explorer
    function doScrollCheck() {
        if ( ReadyObj.isReady ) {
            return;
        }

        try {
            // If IE is used, use the trick by Diego Perini
            // http://javascript.nwbox.com/IEContentLoaded/
            document.documentElement.doScroll("left");
        } catch(e) {
            setTimeout( doScrollCheck, 1 );
            return;
        }

        // and execute any waiting functions
        ReadyObj.ready();
    }
    // Cleanup functions for the document ready method
    if ( document.addEventListener ) {
        DOMContentLoaded = function() {
            document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false );
            ReadyObj.ready();
        };

    } else if ( document.attachEvent ) {
        DOMContentLoaded = function() {
            // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
            if ( document.readyState === "complete" ) {
                document.detachEvent( "onreadystatechange", DOMContentLoaded );
                ReadyObj.ready();
            }
        };
    }
    function ready( fn ) {
        // Attach the listeners
        ReadyObj.bindReady();

        var type = ReadyObj.type( fn );

        // Add the callback
        readyList.done( fn );//readyList is result of _Deferred()
    }
    return ready;
})();

如何使用:

<script>
    ready(function(){
        alert('It works!');
    });
    ready(function(){
        alert('Also works!');
    });
</script>

我不确定这段代码的功能如何,但它在我的肤浅测试中运行良好。这花了很长时间,所以我希望你和其他人可以从中受益。

PS:我建议 编译 它。

或者你可以使用 http://dustindiaz.com/smallest-domready-ever

function r(f){/in/.test(document.readyState)?setTimeout(r,9,f):f()}
r(function(){/*code to run*/});

或者本机函数,如果你只需要支持新的浏览器(与jQuery ready不同,如果在页面加载后添加它,则不会运行)

document.addEventListener('DOMContentLoaded',function(){/*fun code to run*/})

290
2017-08-13 20:52



天啊。下次在删除jquery依赖时我会多次思考。 - Johnny_D
@Johnny_D首先不要添加jQuery依赖=痛苦消失了! - Frederik Krautwald
@FrederikKrautwald无论人们怎么说,概念上jQuery是一件好事,因为DOM API是一个非常臃肿,冗长和不一致的,我只是希望有一个精简版本可用 - Timo Huovinen
@TimoHuovinen替代方案:Zepto.js(9.1 kb),Snack.js(8.1 kb),$ dom(2.3 kb)和140 Medley(0.5 kb)。 编辑: 你也可以看看恩德。 - Frederik Krautwald
@FrederikKrautwald $ dom听起来像我想要的,但不确定它是否符合要求。 Zepto看起来也很有前途,谢谢分享! - Timo Huovinen


三种选择:

  1. 如果 script 是正文的最后一个标记,DOM将在脚本标记执行之前就绪
  2. 当DOM准备就绪时,“readyState”将变为“complete”
  3. 将所有内容放在“DOMContentLoaded”事件侦听器下

的onreadystatechange

  document.onreadystatechange = function () {
     if (document.readyState == "complete") {
     // document is ready. Do your stuff here
   }
 }

资源: MDN

DOMContentLoaded

document.addEventListener('DOMContentLoaded', function() {
   console.log('document is ready. I can sleep now');
});

关注石器时代的浏览器: 转到jQuery源代码并使用 ready 功能。在这种情况下,你没有解析+执行整个库,你只做了很小的一部分。


184
2017-09-12 22:33



第二个例子比标记的答案更加优雅和简洁。为什么这个没有被标记为正确的? - 0112
对于DOMContentLoaded来说仍然是+1,它完全符合我的要求。 - tripleee
onreadystatechange为我做了诀窍......需要在异步jquery加载后运行一些脚本。 - Abram
就像一个FYI,#1并不完全正确。页面末尾的脚本很可能在DOM完成之前加载。这就是听众优越的原因。他们在浏览器完成时监听。把它放在最后是指责脚本加载比浏览器渲染慢。 - Machavity
当文档已经完成加载时,此变体也会起作用,如果可以,请更新您的(imo best)答案:if(document.readyState =='complete'){init(); } else {document.onreadystatechange = function(){if(document.readyState =='complete'){init(); }}} - ZPiDER


放置你的 <script>/*JavaScript code*/</script> 对 在结束之前  </body> 标签。

不可否认,这可能不适合每个人的目的,因为它需要更改HTML文件,而不是仅仅在JavaScript文件中执行某些操作。 document.ready,但还是......


81
2017-12-07 16:46



在我看来,存在兼容性问题,因为页面还没有准备好,你不能在这些和那些浏览器中做到这一点或那样。不幸的是我记得不清楚了。尽管如此,对于99%的所有案例(以及Yahoo!建议)足够接近的方式+1。 - Boldewyn
实际上,将脚本元素放在页面底部是一个几乎完美的解决方案。它可以跨浏览器工作并模拟document.ready完美。唯一的缺点是,它比使用某些智能代码(有点)更突出,您将不得不向用户询问您正在创建的脚本以添加额外的脚本片段来调​​用ready或init函数。 - Stijn de Witt
@StijndeWitt - 你必须调用init函数是什么意思?使用document.ready的脚本不需要其他客户端代码来调用它,它是自包含的,并且等同于代码包含在主体末尾的脚本也可以是自包含的而不是需要其他代码来调用它。 - nnnnnn
为什么不把脚本 后 关闭身体标签,并在结束前 </html> 标签? - Charles Holbrow


穷人的解决方案:

var checkLoad = function() {   
    document.readyState !== "complete" ? setTimeout(checkLoad, 11) : alert("loaded!");   
};  

checkLoad();  

查看小提琴

添加了这个,我想,自己的范围和非递归更好一点

(function(){
    var tId = setInterval(function() {
        if (document.readyState == "complete") onComplete()
    }, 11);
    function onComplete(){
        clearInterval(tId);    
        alert("loaded!");    
    };
})()

查看小提琴


67
2017-08-04 18:13



@PhilipLangford或者只是把它放在一个 setInterval 并完全删除递归。 - Alex W
@Raveren,嗯,你是对的,我很确定我在发布时测试了它。无论如何,它只是变得更简单,现在功能只是被调用,没有包装。 - Jakob Sternberg
这不性感。不,抱歉。使用定时器/间隔来检测东西可能“有效”,但是如果你继续这样编程,那么任何更大的项目都值得一试。不要像这样一起破解东西。做对了。请。这种代码会损害开发生态系统,因为有一个更好的解决方案并且您知道它。 - dudewad
我认为这个答案更接近于 dustindiaz.com/smallest-domready-ever 所以我改进了脚本: jsfiddle.net/iegik/PT7x9 - iegik
@ReidBlomquist是的,这是一个“错误”的方式,这就是我所指出的(虽然有点坚定,我知道)。你可以说,做错了它在某种程度上“帮助”了生态系统,但问题在于,由于他们没有经验知道更好的代码,人们会采用“好”代码的大量不良代码对生态系统没有帮助,因为他们将采用那些糟糕的代码并将其实现为实际的生产架构解决方案。所以,我想,我们只需要对这种“谬误”持不同意见。 - dudewad


我用这个:

document.addEventListener("DOMContentLoaded", function(event) { 
    //Do work
});

注意:这可能仅适用于较新的浏览器,尤其是: http://caniuse.com/#feat=domcontentloaded


31
2017-12-23 19:14



实际上是IE9及以上版本 - Pascalius


真的,如果你在乎 互联网浏览器9+ 只有,这段代码足以取代 jQuery.ready

    document.addEventListener("DOMContentLoaded", callback);

如果你担心 Internet Explorer 6中 和一些非常奇怪和罕见的浏览器,这将工作:

domReady: function (callback) {
    // Mozilla, Opera and WebKit
    if (document.addEventListener) {
        document.addEventListener("DOMContentLoaded", callback, false);
        // If Internet Explorer, the event model is used
    } else if (document.attachEvent) {
        document.attachEvent("onreadystatechange", function() {
            if (document.readyState === "complete" ) {
                callback();
            }
        });
        // A fallback to window.onload, that will always work
    } else {
        var oldOnload = window.onload;
        window.onload = function () {
            oldOnload && oldOnload();
            callback();
        }
    }
},

19
2017-11-07 07:45