$(document).ready没有jQuery
我有一个脚本,使用$(document).ready
,但它不使用任何其他的jQuery。 我想通过删除jQuery依赖来减轻它的负担。
如何在不使用jQuery的情况下实现自己的$(document).ready
function? 我知道使用window.onload
将不会是相同的,因为window.onload
所有的图像,帧等被加载后触发。
有一个基于标准的替代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 ); }
由于接受的答案还远远没有完成,所以我将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>
我不确定这个代码是如何工作的,但是对于我的表面testing来说,它工作得很好。 这花了一段时间,所以我希望你和其他人能从中受益。
PS:我build议编译它。
或者,您可以使用http://dustindiaz.com/smallest-domready-ever :
function r(f){/in/.test(document.readyState)?setTimeout(r,9,f):f()} r(function(){/*code to run*/});
或本地function,如果你只需要支持新的浏览器
document.addEventListener('DOMContentLoaded',function(){/*fun code to run*/})
三个选项:
- 如果
script
是主体的最后一个标记,那么在脚本标记执行之前,DOM将准备就绪 - 当DOM准备就绪时,“readyState”将变为“完成”
- 把所有东西放在'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
函数。 在这种情况下,你不是parsing+执行整个库,你只是做了很小的一部分。
将<script>/*JavaScript code*/</script>
放置在closures </body>
标记之前。
诚然,这可能不适合每个人的目的,因为它需要改变HTML文件,而不是只在JavaScript文件中做一些事情,但仍然…
穷人的解决scheme:
var checkLoad = function() { document.readyState !== "complete" ? setTimeout(checkLoad, 11) : alert("loaded!"); }; checkLoad();
查看小提琴
增加了这一个,我猜,好一点,自己的范围,而不是recursion
(function(){ var tId = setInterval(function() { if (document.readyState == "complete") onComplete() }, 11); function onComplete(){ clearInterval(tId); alert("loaded!"); }; })()
查看小提琴
我使用这个:
document.addEventListener("DOMContentLoaded", function(event) { //Do work });
注意:这可能只适用于较新的浏览器,尤其是这些: http : //caniuse.com/#feat=domcontentloaded
真的,如果你只关心Internet Explorer 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(); } } },
我最近使用这个手机网站。 这是John Resig从“Pro JavaScript技术”中的简化版本。 这取决于addEvent。
var ready = ( function () { function ready( f ) { if( ready.done ) return f(); if( ready.timer ) { ready.ready.push(f); } else { addEvent( window, "load", isDOMReady ); ready.ready = [ f ]; ready.timer = setInterval(isDOMReady, 13); } }; function isDOMReady() { if( ready.done ) return false; if( document && document.getElementsByTagName && document.getElementById && document.body ) { clearInterval( ready.timer ); ready.timer = null; for( var i = 0; i < ready.ready.length; i++ ) { ready.ready[i](); } ready.ready = null; ready.done = true; } } return ready; })();
这个问题在很久以前就被问到了。 对于任何人只是看到这个问题,现在有一个名为“你可能不需要jquery”的网站 ,通过所需的IE支持级别 – jQuery的所有function,并提供一些替代的,更小的库。
IE8文档就绪脚本根据你可能不需要jQuery
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(); }); }
jQuery的答案对我来说非常有用。 有一点耐心,它符合我的需求。 我希望它能帮助其他人。
function onReady ( callback ){ var addListener = document.addEventListener || document.attachEvent, removeListener = document.removeEventListener || document.detachEvent eventName = document.addEventListener ? "DOMContentLoaded" : "onreadystatechange" addListener.call(document, eventName, function(){ removeListener( eventName, arguments.callee, false ) callback() }, false ) }
这里是testingDOM ready的最小代码片段,它可以在所有浏览器(甚至IE 8)上运行:
r(function(){ alert('DOM Ready!'); }); function r(f){/in/.test(document.readyState)?setTimeout('r('+f+')',9):f()}
看到这个答案 。
跨浏览器(旧浏览器)和一个简单的解决scheme:
var docLoaded = setInterval(function () { if(document.readyState !== "complete") return; clearInterval(docLoaded); /* Your code goes here ie init() */ }, 30);
在jsfiddle中显示警报
只需将其添加到HTML页面的底部即可
<script> Your_Function(); </script>
因为,HTML文档是由上下分析的。
这个跨浏览器的代码将在DOM准备好后调用一个函数:
var domReady=function(func){ var scriptText='('+func+')();'; var scriptElement=document.createElement('script'); scriptElement.innerText=scriptText; document.body.appendChild(scriptElement); };
这是如何工作的:
-
domReady
的第一行调用函数的toString
方法来获取传入函数的string表示forms,并将其包装在立即调用该函数的expression式中。 -
domReady
的其余部分用domReady
创build一个脚本元素,并将其附加到文档的body
中。 - DOM准备就绪后,浏览器运行脚本标签。
例如,如果你这样做: domReady(function(){alert();});
,以下将附加到body
元素:
<script>(function (){alert();})();</script>
请注意,这仅适用于用户定义的函数。 以下将无法正常工作: domReady(alert);
这是值得看看坚实的addEvent()和http://www.braksator.com/how-to-make-your-own-jquery 。
这里是代码的情况下,网站出现故障
function addEvent(obj, type, fn) { if (obj.addEventListener) { obj.addEventListener(type, fn, false); EventCache.add(obj, type, fn); } else if (obj.attachEvent) { obj["e"+type+fn] = fn; obj[type+fn] = function() { obj["e"+type+fn]( window.event ); } obj.attachEvent( "on"+type, obj[type+fn] ); EventCache.add(obj, type, fn); } else { obj["on"+type] = obj["e"+type+fn]; } } var EventCache = function(){ var listEvents = []; return { listEvents : listEvents, add : function(node, sEventName, fHandler){ listEvents.push(arguments); }, flush : function(){ var i, item; for(i = listEvents.length - 1; i >= 0; i = i - 1){ item = listEvents[i]; if(item[0].removeEventListener){ item[0].removeEventListener(item[1], item[2], item[3]); }; if(item[1].substring(0, 2) != "on"){ item[1] = "on" + item[1]; }; if(item[0].detachEvent){ item[0].detachEvent(item[1], item[2]); }; item[0][item[1]] = null; }; } }; }(); // Usage addEvent(window, 'unload', EventCache.flush); addEvent(window, 'load', function(){alert("I'm ready");});
这个解决scheme如何?
// other onload attached earlier window.onload=function() { alert('test'); }; tmpPreviousFunction=window.onload ? window.onload : null; // our onload function window.onload=function() { alert('another message'); // execute previous one if (tmpPreviousFunction) tmpPreviousFunction(); };
我们发现了一个快速和肮脏的跨浏览器实现我们的可能做最简单的情况下伎俩与最小的实现:
window.onReady = function onReady(fn){ document.body ? fn() : setTimeout(function(){ onReady(fn);},50); };
与jQuery相比,使用JavaScript等价物总是很好的。 一个原因是less了一个依赖的库,它们比jQuery等效的要快得多。
jQuery等价物的一个奇妙的参考是http://youmightnotneedjquery.com/ 。
至于你的问题,我从上面的链接:)下面的代码只有警告是只适用于Internet Explorer 9和更高版本。
function ready(fn) { if (document.readyState != 'loading') { fn(); } else { document.addEventListener('DOMContentLoaded', fn); } }
这里介绍的setTimeout / setInterval解决scheme只适用于特定情况。
这个问题尤其在最老的8个Internet Explorer版本中出现。
影响这些setTimeout / setInterval解决scheme成功的variables是:
1) dynamic or static HTML 2) cached or non cached requests 3) size of the complete HTML document 4) chunked or non chunked transfer encoding
解决这个特定问题的原始(原生Javascript)代码在这里:
https://github.com/dperini/ContentLoaded http://javascript.nwbox.com/ContentLoaded (test)
这是jQuery团队构build其实现的代码。
这是我使用的,它很快,涵盖了我所想的所有基础。 适用于除IE <9之外的所有内容。
(() => { function fn() { // "On document ready" commands: console.log(document.readyState); }; if (document.readyState != 'loading') {fn()} else {document.addEventListener('DOMContentLoaded', fn)} })();
这似乎抓住了所有的情况:
- 如果DOM已经准备就绪(如果DOM不是“加载”,而是“交互式”或“完整”),则立即触发。
- 如果DOM仍在加载,那么它会为DOM何时可用(交互式)设置一个事件监听器。
DOMContentLoaded事件在IE9和其他一切中都可用,所以我个人认为可以使用它。 如果您没有将代码从ES2015转换为ES5,则将箭头函数声明重写为常规的匿名函数。
如果你想等所有的资源加载,所有的图像显示等,然后使用window.onload来代替。
对于IE9 +:
function ready(fn) { if (document.readyState != 'loading'){ fn(); } else { document.addEventListener('DOMContentLoaded', fn); } }
jQuery中的ready函数做了许多事情。 坦率地说,除非你的网站有非常小的输出,否则我不会看到取代它的意思。 jQuery是一个非常小的库,它处理你以后需要的各种各样的跨浏览器的东西。
无论如何,在这里发表一点点,只要打开jQuery,看看bindReady方法。
它根据事件模型调用document.addEventListener(“DOMContentLoaded”)或document.attachEvent('onreadystatechange'),然后从那里继续。
如果您在BODY底部附近加载jQuery,但是在写出jQuery(<func>)或jQuery(document).ready(<func>)的代码时遇到问题,请查看Github上的jqShim 。
而不是重新创build自己的文档准备function,它只是坚持的function,直到jQuery是可用的,然后继续与预期的jQuery。 将jQuery移动到主体底部的一点是加快页面加载速度,您仍然可以通过在模板头部内联jqShim.min.js来实现。
我最终编写了这个代码,将WordPress中的所有脚本移动到页脚,而这个填充代码现在直接位于页眉中。
这种方法是我能想到的最短路线。
基于DOMContentLoaded事件的解决scheme只有在脚本被加载到文档之前时才起作用,而在此build议的延迟检查可以确保代码始终被执行,即使在稍后dynamic加载的脚本中,也与JQuery的文档准备一致。
此代码与所有浏览器兼容(包括一些传统,IE6和Safari的Windows)。
(function ready() { if (!document.body) {setTimeout(ready, 50); return;} // Document is ready here })();
function onDocReady(fn){ (function c(){document.readyState!=="loading"?fn():setTimeout(c,9)})(); } function onDocLoad(fn){ (function c(){document.readyState==="complete"?fn():setTimeout(c,30)})(); }
onDocReady提供了一个callback,当HTML DOM准备好完全访问/parsing/操作。
onDocLoad提供了一个callback,当一切都已经加载(图像等)
- 这些function可以随时调用。
- 支持多个“听众”。
- 将在任何浏览器工作。
如果你不需要支持非常老的浏览器,即使你的外部脚本加载了async属性,也可以这样做:
HTMLDocument.prototype.ready = new Promise(function(resolve) { if(document.readyState != "loading") resolve(); else document.addEventListener("DOMContentLoaded", function() { resolve(); }); }); document.ready.then(function() { console.log("document.ready"); });
我只是使用:
setTimeout(function(){ //reference/manipulate DOM here });
而不像document.addEventListener("DOMContentLoaded" //etc
在顶部的答案,它的作品早在IE9 – http://caniuse.com/#search=DOMContentLoaded只表示最近IE11。;
例如,进入https://netrenderer.com/index.php ,从下拉菜单中selectInternet Explorer 9,进入https://dexygen.github.io/blog/oct-2017/jekyll/jekyll-categories/liquid- templates / 2017/10/22 / how-jekyll-builds-site-categories.html ,点击“渲染”,你会看到类似于这篇文章底部截图的东西。
See the following Javascript code I am using in the header to manipulate the style of the Jekyll "hacker" theme to my liking — in particular you can reference the if (location.pathname !== rootPath)
block to see how I am inserting the Home
and Blog Home
links, which are being displayed by IE9 per the NetRenderer site.
Interestingly I stumbled upon this setTimeout
solution in 2009: Is checking for the readiness of the DOM overkill? , which probably could have been worded slightly better, as I meant by using various frameworks' more complicated approaches.
setTimeout(function() {//delay execution until after dom is parsed var containerEls = document.getElementsByClassName('container'); var headingEl = containerEls[0].getElementsByTagName('h1')[0]; var headerEl = document.getElementsByTagName('header')[0]; var downloadsSectionEl = document.getElementById('downloads'); var rootPath = "/"; var blogRootPath = "/blog/"; containerEls[0].style.maxWidth = '800px'; containerEls[1].style.maxWidth = '800px'; headingEl.style.margin = '0'; headerEl.style.marginBottom = '7px'; downloadsSectionEl.style.margin = '0'; if (location.pathname !== rootPath) { downloadsSectionEl.appendChild(generateNavLink('Home', rootPath)); if (location.pathname !== blogRootPath) { downloadsSectionEl.appendChild(document.createTextNode(' | ')); downloadsSectionEl.appendChild(generateNavLink('Blog Home', blogRootPath)); } } function generateNavLink(linkText, hrefPath) { var navLink = document.createElement('a'); var linkTextNode = document.createTextNode(linkText); navLink.setAttribute('href', hrefPath); navLink.appendChild(linkTextNode); return navLink; } });
This was a good https://stackoverflow.com/a/11810957/185565 poor man's solution. One comment considered a counter to bail out in case of emergency. This is my modification.
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);
Edit of the edit of @duskwuff to support Internet Explorer 8 too. The difference is a new call to the function test of the regex and the setTimeout with an anonymous function.
Also, I set the timeout to 99.
function ready(f){/in/.test(document.readyState)?setTimeout(function(){ready(f);},99):f();}
In short, instead of the $(document).ready() used in jQuery, we can use a JavaScript method:
<script> document.addEventListener("DOMContentLoaded", function_name, false); function function_name(){ statements; } </script>
Thus, when the page is ready ie DOMContentLoaded only then the function function_name() will be invoked.