我有一个使用 $(document).ready
的脚本,但它不使用 jQuery 中的任何其他内容。我想通过删除 jQuery 依赖项来减轻它。
如何在不使用 jQuery 的情况下实现自己的 $(document).ready
功能?我知道使用 window.onload
会有所不同,因为 window.onload
在所有图像、帧等加载后触发。
$(document).ready
,您可以通过在页面的最底部而不是顶部运行代码来轻松解决该问题。 HTML5Boilerplate 使用这种精确的方法。
有超过 99% of browsers 支持的基于标准的替换DOMContentLoaded
,但不支持 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 就绪的可行替代品
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/
Another good domReady function here 取自 https://stackoverflow.com/a/9899701/175071
由于接受的答案远未完成,我将基于 jQuery 1.6.2 源代码的“就绪”函数拼接在一起,例如 jQuery.ready()
:
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.:我建议compiling它。
或者您可以使用 http://dustindiaz.com/smallest-domready-ever:
function r(f){/in/.test(document.readyState)?setTimeout(r,9,f):f()}
r(function(){/*code to run*/});
或者原生函数,如果你只需要支持新的浏览器(与 jQuery 不同,如果你在页面加载后添加它,这将不会运行)
document.addEventListener('DOMContentLoaded',function(){/*fun code to run*/})
三个选项:
如果 script 是 body 的最后一个标签,则 DOM 将在 script 标签执行之前准备好当 DOM 准备好时,“readyState”将变为“complete” 将所有内容放在 '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
函数。在这种情况下,您不会解析+执行整个库,而您正在做的只是其中的一小部分。
将您的 <script>/*JavaScript code*/</script>
放在 结束 </body>
标记之前。
诚然,这可能不适合每个人的目的,因为它需要更改 HTML 文件,而不仅仅是在 JavaScript 文件中做一些事情,例如 document.ready
,但仍然......
穷人的解决方法:
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!");
};
})()
我用这个:
document.addEventListener("DOMContentLoaded", function(event) {
//Do work
});
注意:这可能只适用于较新的浏览器,尤其是这些:http://caniuse.com/#feat=domcontentloaded
现在是 2020 年,<script>
标记具有 defer
属性。
例如:
<script src="demo_defer.js" defer></script>
它指定当页面完成解析时执行脚本。
https://www.w3schools.com/tags/att_script_defer.asp
这个问题很久以前就被问过了。对于刚刚看到这个问题的任何人,现在有一个名为 "you might not need jquery" 的站点,它按所需的 IE 支持级别分解了 jquery 的所有功能,并提供了一些替代的、更小的库。
根据 you might not need jquery 的 IE8 文档准备脚本
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();
});
}
'onreadystatechange'
而不是 document.attachEvent('onload', fn);
确实,如果您只关心 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();
}
}
},
跨浏览器(旧浏览器也是)和一个简单的解决方案:
var docLoaded = setInterval(function () {
if(document.readyState !== "complete") return;
clearInterval(docLoaded);
/*
Your code goes here i.e. init()
*/
}, 30);
我最近将其用于移动网站。这是 John Resig 的“Pro JavaScript Techniques”的简化版本。这取决于 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;
})();
2022版
在 2022 年,你需要做的就是把 defer 属性放在你的脚本上,然后加载到 head 中!
参考:https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-defer
<!doctype html>
<html>
<head>
<script src="/script.js" defer></script>
</head>
<body>
<p>In 2022, all you need to do is put the defer attribute on your script, and load it in the head!</p>
</body>
</html>
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 )
}
removeListener
,即。 removeListener.call(document, ...
这是测试 DOM 就绪的最小代码片段,它适用于所有浏览器(甚至 IE 8):
r(function(){
alert('DOM Ready!');
});
function r(f){/in/.test(document.readyState)?setTimeout('r('+f+')',9):f()}
请参阅此answer。
只需将其添加到 HTML 页面的底部...
<script>
Your_Function();
</script>
因为,HTML 文档是由 top-bottom 解析的。
最小和 100% 工作
我从 PlainJS 中选择了答案,它对我来说很好用。它扩展了 DOMContentLoaded
以便它可以被所有浏览器接受。
这个函数相当于 jQuery 的 $(document).ready()
方法:
document.addEventListener('DOMContentLoaded', function(){
// do something
});
然而,与 jQuery 相比,此代码只能在现代浏览器 (IE > 8) 中正常运行,并且在插入此脚本时文档已经呈现的情况下(例如通过 Ajax)将无法正常运行。因此,我们需要稍微扩展一下:
function run() {
// do something
}
// in case the document is already rendered
if (document.readyState!='loading') run();
// modern browsers
else if (document.addEventListener)
document.addEventListener('DOMContentLoaded', run);
// IE <= 8
else document.attachEvent('onreadystatechange', function(){
if (document.readyState=='complete') run();
});
这基本上涵盖了所有可能性,并且是 jQuery 助手的可行替代品。
Rock Solid 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");});
与 jQuery 相比,使用 JavaScript 等价物总是好的。一个原因是依赖的库少了一个,而且它们比 jQuery 等价物快得多。
jQuery 等效项的一个极好的参考是 http://youmightnotneedjquery.com/。
就您的问题而言,我从上面的链接中获取了以下代码:) 唯一需要注意的是它仅适用于 Internet Explorer 9 及更高版本。
function ready(fn) {
if (document.readyState != 'loading') {
fn();
}
else {
document.addEventListener('DOMContentLoaded', fn);
}
}
一旦 DOM 准备好,此跨浏览器代码将调用一个函数:
var domReady=function(func){
var scriptText='('+func+')();';
var scriptElement=document.createElement('script');
scriptElement.innerText=scriptText;
document.body.appendChild(scriptElement);
};
以下是它的工作原理:
domReady 的第一行调用函数的 toString 方法来获取您传入的函数的字符串表示形式,并将其包装在立即调用该函数的表达式中。 domReady 的其余部分使用表达式创建一个脚本元素并将其附加到文档的正文中。 DOM 准备好后,浏览器运行附加到正文的脚本标签。
例如,如果您这样做:domReady(function(){alert();});
,则以下内容将附加到 body
元素:
<script>(function (){alert();})();</script>
请注意,这仅适用于用户定义的函数。以下将不起作用:domReady(alert);
这个解决方案怎么样?
// 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);
};
doc.body
是什么!?
我只是使用:
setTimeout(function(){
//reference/manipulate DOM here
});
与最上面的答案中的 document.addEventListener("DOMContentLoaded" //etc
不同,它可以追溯到 IE9 - http://caniuse.com/#search=DOMContentLoaded 仅表示最近到 IE11。
有趣的是,我在 2009 年偶然发现了这个 setTimeout
解决方案:Is checking for the readiness of the DOM overkill?,它的措辞可能会稍微好一些,因为我的意思是“使用各种框架的更复杂的方法来检查 DOM 的准备情况是否过度”。
对于这种技术为什么有效,我最好的解释是,当到达具有这种 setTimeout 的脚本时,DOM 正在被解析,因此 setTimeout 内的代码的执行被推迟到该操作完成。
比较
这里(在下面的片段中)是选择的可用浏览器“内置”方法及其执行顺序的比较。评论
任何现代浏览器都不支持 document.onload (X)(从不触发事件)
如果您使用
(F) 并同时使用 window.onload (E) 则只会执行第一个(因为它会覆盖第二个)(F) 中给出的事件处理程序由附加的 onload 函数包装
document.onreadystatechange (D) 不覆盖 document.addEventListener('readystatechange'...) (C) 可能会导致类似 onXYZevent 的方法独立于 addEventListener 队列(允许添加多个侦听器)。执行这两个处理程序之间可能没有任何反应。
所有脚本都在控制台中写入它们的时间戳 - 但也可以访问 div 的脚本也在正文中写入它们的时间戳(在脚本执行后单击“整页”链接以查看它)。
解决方案 readystatechange (C,D) 由浏览器多次执行,但针对不同的文档状态:
loading - 文档正在加载(没有在代码段中触发)
交互式 - 文档被解析,在 DOMContentLoaded 之前触发
完成 - 加载文档和资源,在正文/窗口加载之前触发
使用纯 JavaScript 的最简单方法。没有 jQuery:
document.addEventListener("DOMContentLoaded", function(event) {
// Your code to run since DOM is loaded and ready
});
如果您不必支持非常旧的浏览器,即使您的外部脚本加载了异步属性,也可以使用以下方法:
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");
});
对于 IE9+:
function ready(fn) {
if (document.readyState != 'loading'){
fn();
} else {
document.addEventListener('DOMContentLoaded', fn);
}
}
这是我使用的,它速度快,涵盖了我认为的所有基础;适用于除 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 代替。
此处介绍的 setTimeout/setInterval 解决方案仅适用于特定情况。
该问题尤其出现在不超过 8 的旧 Internet Explorer 版本中。
影响这些 setTimeout/setInterval 解决方案成功的变量是:
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 团队构建实现的代码。
现在你应该使用模块。将您的代码放入模块的默认函数中,然后将该函数导入脚本元素中。
client.js
:
export default function ()
{
alert ("test");
}
index.html
:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>test</title>
</head>
<body>
<script type="module">
import main from './client.js';
main ();
</script>
</body>
</html>