Vanilla JavaScript equivalent of jQuery's $.ready() - how to call a function when the page/DOM is ready for it

Vanilla JavaScript equivalent of jQuery’s $.ready() - how to call a function when the page/DOM is ready for it

技术背景

在前端开发中,我们常常需要在页面的DOM加载完成后执行特定的代码。jQuery提供了$(document).ready()方法来实现这一功能,但在某些情况下,我们可能不希望引入jQuery库,而是使用原生JavaScript来实现相同的功能。不同浏览器对DOM加载完成事件的支持有所不同,因此需要考虑跨浏览器兼容性。

实现步骤

1. 简单的方法:将代码放在body末尾

将代码放在<body>标签的末尾,这样可以确保在DOM加载完成后执行代码。这种方法在所有浏览器中都有效,并且比onload事件执行得更快,因为它只等待DOM准备好,而不是等待所有图像加载完成。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!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>
HTML

2. 现代浏览器的实现

对于IE9及以上版本以及Chrome、Firefox、Safari的任何版本,可以使用以下方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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);
}
}

// Usage
docReady(function() {
// DOM is loaded and ready for manipulation here
});
JAVASCRIPT

3. 全浏览器兼容的实现

如果需要支持旧版本的IE,并且不想等待window.onload事件,可以参考jQuery的实现方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
(function(funcName, baseObj) {
funcName = funcName || "docReady";
baseObj = baseObj || window;
var readyList = [];
var readyFired = false;
var readyEventHandlersInstalled = false;

function ready() {
if (!readyFired) {
readyFired = true;
for (var i = 0; i < readyList.length; i++) {
readyList[i].fn.call(window, readyList[i].ctx);
}
readyList = [];
}
}

function readyStateChange() {
if ( document.readyState === "complete" ) {
ready();
}
}

baseObj[funcName] = function(callback, context) {
if (typeof callback !== "function") {
throw new TypeError("callback for docReady(fn) must be a function");
}
if (readyFired) {
setTimeout(function() {callback(context);}, 1);
return;
} else {
readyList.push({fn: callback, ctx: context});
}
if (document.readyState === "complete") {
setTimeout(ready, 1);
} else if (!readyEventHandlersInstalled) {
if (document.addEventListener) {
document.addEventListener("DOMContentLoaded", ready, false);
window.addEventListener("load", ready, false);
} else {
document.attachEvent("onreadystatechange", readyStateChange);
window.attachEvent("onload", ready);
}
readyEventHandlersInstalled = true;
}
}
})("docReady", window);

// Usage
// 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);
JAVASCRIPT

4. 其他实现方式

  • 使用document.addEventListener(IE9及以上):
1
2
3
document.addEventListener("DOMContentLoaded", function(event) {
// Your code to run since DOM is loaded and ready
});
JAVASCRIPT
  • 跨浏览器兼容的技巧
1
2
3
4
5
function r(f){/in/.test(document.readyState)?setTimeout('r('+f+')',9):f()}
// use like
r(function(){
alert('DOM Ready!');
});
JAVASCRIPT
  • 检查document.readyState
1
2
3
4
5
6
7
8
document.onreadystatechange = function () {
var state = document.readyState;
if (state == 'interactive') {
init();
} else if (state == 'complete') {
initOnCompleteLoad();
}
};
JAVASCRIPT
  • 可复用版本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
window.readyHandlers = [];
window.ready = function ready(handler) {
window.readyHandlers.push(handler);
handleState();
};

window.handleState = function handleState () {
if (['interactive', 'complete'].indexOf(document.readyState) > -1) {
while(window.readyHandlers.length > 0) {
(window.readyHandlers.shift())();
}
}
};

document.onreadystatechange = window.handleState;

// Usage
ready(function () {
// your code here
});
JAVASCRIPT

核心代码

以下是一个简洁的实现:

1
2
3
4
5
6
7
8
9
function ready(fn){
var d = document;
(d.readyState == 'loading')? d.addEventListener('DOMContentLoaded', fn) : fn();
}

// Use like
ready(function(){
//some code
});
JAVASCRIPT

最佳实践

  • 对于现代浏览器,优先使用document.addEventListener("DOMContentLoaded", fn)方法。
  • 如果需要支持旧版本的IE,使用全浏览器兼容的实现方式。
  • 将代码放在<body>标签的末尾是一种简单且可靠的方法。

常见问题

  • IIFE不会等待DOM加载
    不要使用自执行的IIFE来等待DOM加载,因为它不会等待DOM加载完成。例如:
    1
    2
    3
    4
    (function() {
    // Your page initialization code here - WRONG
    // The DOM will be available here - WRONG
    })();
    JAVASCRIPT
  • document.ondomcontentready兼容性问题
    document.ondomcontentready方法没有完全的浏览器兼容性,不建议使用。

Vanilla JavaScript equivalent of jQuery's $.ready() - how to call a function when the page/DOM is ready for it
https://119291.xyz/posts/vanilla-javascript-equivalent-of-jquerys-ready/
作者
ww
发布于
2025年5月26日
许可协议