什么是 '$(document).ready()' 的非jQuery等效方法?

629

$(document).ready() 的非 jQuery 等效方法是什么?


4
еҰӮжһңдҪ жғіеңЁдёҚдҪҝз”Ёд»»дҪ•еә“зҡ„жғ…еҶөдёӢеӨҚеҲ¶jQueryзҡ„$(document).ready()дәӢ件пјҢиҜ·жҹҘзңӢиҝҷдёӘй“ҫжҺҘпјҡhttps://dev59.com/questions/jHI-5IYBdhLWcg3wj5FG#1795167 - Christian C. Salvadó
@OP:请查看《Pro JavaScript Techniques》第89页,了解$(document).ready()的原生JavaScript实现- http://books.google.com/books?id=GgJN2CC_2s4C&lpg=PP1&dq=pro%20javascript%20techniques&pg=PA89#v=onepage&q=&f=false。它还使用了Dean Edwards编写的addEvent事件绑定抽象,该代码也在书中 :) - Russ Cam
6
可能是重复的问题:没有使用jQuery的$(document).ready等效方法 - Qantas 94 Heavy
9个回答

898

这来自ECMA并且完美运作。这段代码片段已经足够了,但如果你想深入了解并探索其他选项,请查看详细说明

document.addEventListener("DOMContentLoaded", function() {
  // code...
});

window.onload 和 JQuery $(document).ready 并不相同,因为 $(document).ready 只等待 DOM 树加载完毕,而 window.onload 则会检查所有包括外部资源和图片在内的元素。

编辑:根据Jan Derk的观察,增加了IE8及更早版本的等效代码。您可以在MDN上查看此代码的来源

// alternative to DOMContentLoaded
document.onreadystatechange = function () {
    if (document.readyState == "interactive") {
        // Initialize your application or run some code.
    }
}

除了"interactive",还有其他选项。详情请见MDN文档


9
@Deerloper 不是的,我刚在Chrome控制台上试了一下 - 没有任何反应: document.addEventListener("DOMContentLoaded",function(){console.log(123)}) 现在你可以试试。 - oriadam
3
浏览器中DOMContentLoaded的支持: http://caniuse.com/domcontentloaded - Guillaume Husta
1
我发现了一个奇怪的IE11场景,当我使用调整大小函数时,它不会正确执行,因为它使用jquery进行一堆计算...但是如果我像这样包装它(在window.addevent内的doc ready中),似乎就没问题了。window.addEventListener("DOMContentLoaded", function (event) { $(document).ready(function () { alignBody(); }); });。这正常吗?因为感觉像个邪恶的黑客技巧。 - petrosmm
9
对于那些测试@oriadam评论的人,如果你在控制台尝试监听它,很可能是因为DOMContentLoaded事件早就被触发了。请尝试在本地测试中将其包含在页面中。 - Sebastien

65

现在已经是2018年了,以下是一种简单快捷的方法。

这将添加一个事件监听器,但如果它已经被触发,我们将检查 dom 是否处于就绪状态或者是否已完成。这可能会在子资源完成加载之前或之后触发(如图片、样式表、框架等)。

function domReady(fn) {
  // If we're early to the party
  document.addEventListener("DOMContentLoaded", fn);
  // If late; I mean on time.
  if (document.readyState === "interactive" || document.readyState === "complete" ) {
    fn();
  }
}

domReady(() => console.log("DOM is ready, come and get it!"));

相关阅读


更新

这里提供了一些使用标准的ES6导入和导出编写的实用工具辅助函数,同时包含TypeScript。也许我可以将它们制作成一个可以作为项目依赖安装的快速库。

JavaScript

export const domReady = (callBack) => {
  if (document.readyState === "loading") {
    document.addEventListener('DOMContentLoaded', callBack);
  }
  else {
    callBack();
  }
}

export const windowReady = (callBack) => {
  if (document.readyState === 'complete') {
    callBack();
  }
  else {
    window.addEventListener('load', callBack);
  }
}

TypeScript

export const domReady = (callBack: () => void) => {
  if (document.readyState === "loading") {
    document.addEventListener('DOMContentLoaded', callBack);
  }
  else {
    callBack();
  }
}

export const windowReady = (callBack: () => void) => {
  if (document.readyState === 'complete') {
    callBack();
  }
  else {
    window.addEventListener('load', callBack);
  }
}

承诺对象

export const domReady = new Promise(resolve => {
  if (document.readyState === "loading") {
    document.addEventListener('DOMContentLoaded', resolve);
  }
  else {
    resolve();
  }
});

export const windowReady = new Promise(resolve => {
  if (document.readyState === 'complete') {
    resolve();
  }
  else {
    window.addEventListener('load', resolve);
  }
});

49

我整理了一点东西

domready.js

(function(exports, d) {
  function domReady(fn, context) {

    function onReady(event) {
      d.removeEventListener("DOMContentLoaded", onReady);
      fn.call(context || exports, event);
    }

    function onReadyIe(event) {
      if (d.readyState === "complete") {
        d.detachEvent("onreadystatechange", onReadyIe);
        fn.call(context || exports, event);
      }
    }

    d.addEventListener && d.addEventListener("DOMContentLoaded", onReady) ||
    d.attachEvent      && d.attachEvent("onreadystatechange", onReadyIe);
  }

  exports.domReady = domReady;
})(window, document);

如何使用它

<script src="domready.js"></script>
<script>
  domReady(function(event) {
    alert("dom is ready!");
  });
</script>

通过传递第二个参数,您还可以更改回调函数运行的上下文

function init(event) {
  alert("check the console");
  this.log(event);
}

domReady(init, console);

38

有一个基于标准的替代方案

DOMContentLoaded被90%以上的浏览器支持,但不包括IE8(因此下面的代码使用JQuery来获得浏览器支持)

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 );
}

2
新的jQuery不再支持旧版浏览器,现在只有使用addEventListener来绑定DOMContentLoadedload事件。而且第一次触发时会同时移除两个监听器,这样就不会触发两次了。 - jcubic

33
根据http://youmightnotneedjquery.com/#ready,一个很好的替代品且仍然可以在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();
    });
  }
}

// test
window.ready(function() {
    alert('it works');
});

改进: 我个人也会检查fn的类型是否为函数。 而且正如@elliottregan建议的那样,在使用后删除事件侦听器。

function ready(fn) {
  if (typeof fn !== 'function') {
    throw new Error('Argument passed to ready should be a function');
  }

  if (document.readyState != 'loading') {
    fn();
  } else if (document.addEventListener) {
    document.addEventListener('DOMContentLoaded', fn, {
      once: true // A boolean value indicating that the listener should be invoked at most once after being added. If true, the listener would be automatically removed when invoked.
    });
  } else {
    document.attachEvent('onreadystatechange', function() {
      if (document.readyState != 'loading')
        fn();
    });
  }
}

// tests
try {
  window.ready(5);
} catch (ex) {
  console.log(ex.message);
}


window.ready(function() {
  alert('it works');
});

我回答这个问题晚的原因是因为我在寻找答案,但在这里找不到。我认为这是最好的解决方案。

1
是的,我认为这是最好的答案。易于阅读,即使 DOM 已加载,它也可以执行代码。唯一需要添加的是在事件触发后删除事件监听器。 - elliottregan

30

这不是回答问题,也没有展示任何非jQuery的代码。请参见下面@sospedra的回答。

$(document).ready() 的好处在于它会在 window.onload 之前触发。load 函数会等待所有内容加载完成,包括外部资源和图像。然而,$(document).ready 会在 DOM 树构建完成后立即触发,可以对其进行操作。如果您想要实现不使用 jQuery 的 DOM 就绪状态,可以尝试该库。有人从 jQuery 中提取了 `ready` 部分。它很小巧,你可能会发现它有用:

domready at Google Code


4
DomReady代表代码网络!通过@CMS在github上:https://github.com/cms/domready/network - Kzqai
134
这并没有回答问题,也没有展示任何非jQuery的代码。它是如何获得这么多赞的? - Daniel W.
3
因为它简单又实用。我们大多数人来这里是为了找一种方法,确保DOM已经准备好被JavaScript代码使用。 - abarazal
20
没错,但我们中的一些人来这里是为了得到实际答案。 - Slbox
7
回答的不太好-忽略了提问者的问题。我们中的一些人因为各种原因不想使用jQuery。 - Sean Haddy

18
在最近的浏览器中,最简单的方法是使用适当的GlobalEventHandlersonDOMContentLoadedonloadonloadeddata(...)

onDOMContentLoaded = (function(){ console.log("DOM ready!") })()

onload = (function(){ console.log("Page fully loaded!") })()

onloadeddata = (function(){ console.log("Data loaded!") })()

DOMContentLoaded事件在初始HTML文档完全加载和解析完成时触发,而无需等待样式表、图像和子框架的加载完成。非常不同的load事件只应用于检测完全加载的页面。在这里使用load而不是DOMContentLoaded是一个非常常见的错误,而DOMContentLoaded更加适合,所以要谨慎。

https://developer.mozilla.org/en-US/docs/Web/Events/DOMContentLoaded

所使用的函数是IIFE,在这种情况下非常有用,因为它在准备好时会自动触发自身:

https://en.wikipedia.org/wiki/Immediately-invoked_function_expression

显然,将其放置在任何脚本的末尾更为合适。

在ES6中,我们还可以将其写成箭头函数:

onload = (() => { console.log("ES6 page fully loaded") })()

最好使用DOM元素,我们可以等待任何变量准备就绪,然后触发一个箭头函数立即执行表达式。
行为将保持不变,但内存占用更少。

footer = (() => { console.log("Footer loaded!") })()
<div id="footer">


一个IIFE能在DOM完成加载元素之前触发吗? - CTS_AE
当然,它只是一个闭包中的匿名函数。 - NVRM
新手问题:除了语法更简洁、上下文意图不太明确之外,这种方法与使用事件监听器有什么区别? - cregox
事件监听器更加灵活,一个主要的区别是我们可以在同一个事件上声明多个监听器,但在这种情况下并不重要,因为事件是onload,它只会触发一次。 - NVRM

8

在没有任何库的情况下,使用纯基础JavaScript?这是一个错误。$只是一个标识符,如果你不定义它,它将未定义。

jQuery定义$作为其自己的“everything object”(也称为jQuery),因此您可以在不与其他库发生冲突的情况下使用它。如果您不使用jQuery(或某些其他定义它的库),则$将未定义。

还是你想问在普通JavaScript中的等效物是什么?在这种情况下,你可能想要window.onload,虽然不完全等效,但在纯基础JavaScript中实现相同效果的最快最简单的方法。


46
对于此回答的许多反对者(以及下面的其他回答者):当这个问题被提出时,它只是简单地说:“$(document).ready()在JavaScript中是什么?不是jQuery。是什么意思?”这听起来像他正在询问在没有加载jQuery的普通JavaScript中这意味着什么。在我的回答中,我试图回答这个问题,并给出最接近的简单答案,以适用于在没有加载jQuery或其他库的情况下使用纯JavaScript。请注意,所有额外的上下文都是由其他人猜测问题的含义而添加的,而不是原始发布者。 - Brian Campbell

0

body 的 onLoad 也可以是一种替代方案:

<html>
<head><title>Body onLoad Exmaple</title>

<script type="text/javascript">
    function window_onload() {
        //do something
    }
</script>

</head>
<body onLoad="window_onload()">

</body>
</html>

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