Javascript domready?

52

我知道可以使用不同的框架,如prototype或jQuery来附加函数到window.onload,但这不是我想要的。

我需要像.readyState这样的东西,这样我就可以做出这样的事情:

if(document.isReady){
  var id = document.getElem ...
}

除了使用框架提供的方法,还有其他方法吗?


10
跨浏览器可靠地检测DOM是否已准备好并不是一件简单的事情。为什么您不想使用已经被证明可靠的框架之一呢? - Mauricio Scheffer
2
我建议看一下DomAssistant - 它是一个非常轻量级的库。或者,可以查看D-Lite获取一组额外轻量级的函数。 - dusoft
基本上,我被要求不这样做。 - kristian nissen
1
请看我在一个类似问题上的回答,其中包含了一个跨浏览器的DOMReady对象链接。 - Victor
你可能需要重新考虑正确的答案。 - Patrick W. McMahon
同意@PatrickW.McMahon的观点,截至2017年,建议使用标准的document.addEventListener('DOMContentLoaded' ...,而不是使用库中的方法。 - Pierre H.
11个回答

44

编辑: 随着2018年的到来,我认为安全起见应该监听DOMContentLoaded事件。

function fireOnReady() { /* ... */ }
if (document.readyState === 'complete') {
    fireOnReady();
} else {
    document.addEventListener("DOMContentLoaded", fireOnReady);
}

请注意,事件仅会在页面加载时触发一次!如果你需要支持非常老的浏览器,请查看下面我编写的超级轻量级脚本。



仅供历史参考:



jQuery有一个未记录的属性叫做isReady,它在内部用于确定DOM就绪事件是否已经触发:

if($.isReady) {
    // DOM is ready
} else {
    // DOM is not yet ready
}
我从1.5.2版本开始测试,一直回溯到1.3.2版本,该属性一直存在。虽然没有文档说明,但我认为您可以依赖此属性在未来版本的jQuery中使用。
编辑:一年后,即v1.7.2版本,他们仍在使用$.isReady,仍未经过记录,请自行承担风险。升级时要小心。
编辑:v1.9版本仍在使用$.isReady,仍未经过记录。
编辑:即使是拥有所有“重大”变化的v2.0版本,仍在使用$.isReady,仍未经过记录。
编辑:由于有几个人指出上述内容并不真正回答问题,所以我创建了一个mini DOM ready snippet,灵感来源于Dustin Diaz的最小的DOM就绪片段。 Dustin创建了一种巧妙的方式来检查文档的readyState,类似于这样的东西:
if( !/in/.test(document.readyState) ) {
    // document is ready
} else {
    // document is NOT ready
}
这个方法之所以有效,是因为浏览器有3种加载状态:“loading”,“interactive”和“complete”(旧的 WebKit 还使用了“loaded”,但现在您不必担心它)。你会注意到,“loading”和“interactive”都包含文本“in”...所以如果在document.readyState中找到字符串“in”,那么我们就知道还没有准备好。

4
不确定为什么会被踩。我解释了这是未记录的内容。在未来版本的jQuery中调试此问题将非常简单,并且它是其中一个自我描述变量,永远不会通过“这有更好的名称”的测试,因此可能永远不会被重命名。无论如何...我希望人们能够解释踩的原因。 - Ryan Wheale
7
这并没有回答问题(原帖作者不想使用框架)。但是,它确实对一个被标记为“完全重复”的问题提供了一个很好的答案(我给它点了+1)。https://dev59.com/Wmsy5IYBdhLWcg3wtwMd - Brent Bradburn
1
被点赞并不回答这个问题,但回答了许多最终来到这里的人的问题。 - bjedrzejewski
要求是在不使用框架的情况下获取dom准备就绪。您的示例解决了问题,但使用了框架。这应该是一个不同的问题的一部分,询问如何使用JQuery获取dom准备就绪,但不应该是当前问题的解决方案。不要因为解决另一个问题而点赞。只有当它解决当前问题时才点赞。 - Patrick W. McMahon
1
@et al - 这是我在 S.O. 上最早的回答之一,当时我还不够明智。感谢那些指出我的短视的人,我已经更新了我的答案,包括一个无框架的解决方案。 - Ryan Wheale
显示剩余2条评论

32

自从这个问题被提出5年以来,DOMContentLoaded事件已成为一个普遍支持的事件,您可以直接使用它。

document.addEventListener('DOMContentLoaded', function() {
    //.. do stuff ..
}, false);

我正准备发表一下关于DOMContentLoaded的评论。既然这里没有任何不同意见,我就认为这是正确的做法了? - Nocturno
这在每个现代浏览器中都得到了支持 http://caniuse.com/#search=DOMContentLoaded - str
你为什么在人们已经评论了addEventListener()一年之后才发布这篇文章?你并没有添加任何其他答案已经提供的内容。 - Patrick W. McMahon
抱歉,Patrick。我知道这看起来像是我故意以与你相同的方式重新回答这个问题,以窃取你的网络积分,但我真的没有看到你的答案。 当时我浏览了一下,认为这很有趣,没有人指出这不再是需要第三方代码解决的问题。我认为,人们正在点赞的部分仍然是我的答案的一部分 - 这现在是普遍支持的。 - Damon Smith

32

虽然我通常主张避免使用框架,除非必要,但在这种情况下使用一个是完全可以的。这里是jQuery:

$(function () {
    // do stuff after DOM has loaded
});

请注意,这与window.onload事件不同,因为onload在其他资源(如图像等)加载完毕后首先执行。我在示例中使用的代码将在DOM完成加载时执行,即在完整的HTML结构可用时(不一定是在图像、CSS等可用时)。如果您想要一个可检查的变量,可以在ready函数中设置一个。
var documentIsReady = false;
$(function () { documentIsReady = true; });

当然,如果你只想检查DOM是否准备好,可以找到比jQuery更轻量级的库。但在不同浏览器行为非常不同的情况下(这是其中之一),请使用库。
使用DOMAssistant库的一些代码,制作自己的“DOM已准备就绪”函数并不难:
var domLoaded = function (callback) {
    /* Internet Explorer */
    /*@cc_on
    @if (@_win32 || @_win64)
        document.write('<script id="ieScriptLoad" defer src="//:"><\/script>');
        document.getElementById('ieScriptLoad').onreadystatechange = function() {
            if (this.readyState == 'complete') {
                callback();
            }
        };
    @end @*/
    /* Mozilla, Chrome, Opera */
    if (document.addEventListener) {
        document.addEventListener('DOMContentLoaded', callback, false);
    }
    /* Safari, iCab, Konqueror */
    if (/KHTML|WebKit|iCab/i.test(navigator.userAgent)) {
        var DOMLoadTimer = setInterval(function () {
            if (/loaded|complete/i.test(document.readyState)) {
                callback();
                clearInterval(DOMLoadTimer);
            }
        }, 10);
    }
    /* Other web browsers */
    window.onload = callback;
};

没有经过测试,但应该可以工作。我从DOMAssistant中简化了它,因为DOMAssistant允许多个回调,并检查以确保您不能添加相同的函数等。


4
这是我对框架的唯一需求,其他所有方面都必须为其服务,我不需要其他的东西。如果可能的话,在这种情况下我更愿意不使用框架,否则我会选择使用jQuery。 - kristian nissen
1
我的代码将调用 callback 最多两次(因为 window.onload 应该在所有浏览器中执行)。处理事件时,请检查是否已经调用了 callback,如果已经调用,则返回(这就是 DOMAssistant 所做的,但我删除了那部分内容)。 - Blixt
1
改变 window.onload 的值而不考虑实际值可能会在其他脚本代码中产生意外行为。此外,我们不能调用两次 domLoaded,因为只有最后一个会被使用。 - fguillen
1
这段代码对我有用!除非我使用else if,否则Firefox会触发两次。 - jisaacstone
+1,不要说“只需使用jQuery或X库”,我讨厌人们这样做。 - Vitim.us
显示剩余4条评论

11
我已经更新了DOMAssistant库的代码,它能够很好地运行。
var domReady = (function (){
  var arrDomReadyCallBacks = [] ;
  function excuteDomReadyCallBacks(){
       for (var i=0; i < arrDomReadyCallBacks.length; i++) {
         arrDomReadyCallBacks[i]();
       }
       arrDomReadyCallBacks = [] ;
  }

  return function (callback){
    arrDomReadyCallBacks.push(callback);
     /* Mozilla, Chrome, Opera */
      if (document.addEventListener ) {
          document.addEventListener('DOMContentLoaded', excuteDomReadyCallBacks, false);
      }
    /* Safari, iCab, Konqueror */
    if (/KHTML|WebKit|iCab/i.test(navigator.userAgent)) {
        browserTypeSet = true ;
        var DOMLoadTimer = setInterval(function () {
            if (/loaded|complete/i.test(document.readyState)) {
                //callback();
                excuteDomReadyCallBacks();
                clearInterval(DOMLoadTimer);
            }
        }, 10);
    }
    /* Other web browsers */

    window.onload = excuteDomReadyCallBacks;
}
})()

为什么要踩票呢? 该代码已在IE 8、Firefox和Chrome中进行了测试。 - Yehia
你的例子就像一个鲁伯·戈尔德堡机器。在编程中要记住KISS方法。有简单的一行代码解决问题的方法,不需要额外的处理。请添加更多文档说明为什么你的解决方案比简单的一行代码更好。没有文档说明的代码片段不会得到很多赞。 - Patrick W. McMahon

10

看看这个来自微软的演示。-> http://ie.microsoft.com/testdrive/HTML5/DOMContentLoaded/Default.html 以下是允许您在 DOM 准备就绪时运行 JS 代码的要点...

(function () {
    function load2(){
        // put whatever you need to load on DOM ready in here
        document.getElementById("but3").addEventListener("click", doMore, false);
    }
    if (window.addEventListener) {
        window.addEventListener('DOMContentLoaded', load2, false);
    } else {
        window.attachEvent('onload', load2);
    }
} ());

这是演示的一些JavaScript源代码... http://ie.microsoft.com/testdrive/Includes/Script/ReturnAndShareControls.js

在谷歌Chrome和IE 9上,它运行良好。非常好!


1
我认为应该是 document.addEventListener(... 而不是 window - banderson623
@banderson623 抱歉,因为很久没有使用我帖子中的代码了,所以我无法回忆起它的细节。也许是浏览器更新导致了你所提到的改变。 - Randy Skretka
你不需要在addEventListener('DOMContentLoaded', load2, false)的末尾加上false。DOMContentLoaded没有要遍历的父项。 - Patrick W. McMahon

10
document.addEventListener("DOMContentLoaded", function(e){
    console.log("dom ready");//output to web browser console
});

不需要使用JQuery。在DOMContentLoaded事件中不需要传递第三个参数,因为该事件没有父元素来遍历事件。也不需要过于花哨,只需使用下面的代码即可检测DOM何时完全加载并准备好使用。当我注意到DOM助手库时,我笑了。那个库绝对是无用的。

DOMContentLoaded事件在文档完全加载和解析完成时触发,而无需等待样式表、图像和子框架完成加载。

addEventListener()是一个很好的函数,可以检查任何事件,包括DOM就绪状态。当使用"DOMContentLoaded"时,在addEventListener()中不需要第三个参数,因为这个触发器没有父项来传递事件。在上面的示例中,您会注意到第二个参数是一个匿名函数。您也可以将函数的名称传递给第二个参数。

document.addEventListener([(string)event trigger], [function],[(boolean)traverse DOM tree]);
使用此方法的另一个好处是在更新JQuery时不会出现问题。

6
jQuery不使用window.onload。 $(document).ready()会等待DOM加载完毕并可以遍历(此时可能还未加载完所有内容)。
如果你查看jQuery源代码并仔细分析,你会发现bindReady()方法负责处理这个过程,该方法有多个不同的实现适用于不同的浏览器,只有在所有这些实现都失败时才会回退到监听窗口的load事件。

@PatrickW.McMahon - 我在我的回答中没有建议使用jQuery。如果你仔细阅读,我告诉OP阅读jQuery的源代码,以了解他们如何进行准备检测,以便他可以模仿它。 - Justin Niessner
为什么要让别人深入研究jQuery代码,当你可以链接到w3schools文档以获取DOM状态呢?这样做需要更少的时间,而且不需要深入挖掘成千上万行的jQuery代码,并跟随jQuery标志和触发器,只是为了找出他们将w3school演示包装到一个jQuery函数中。简单的解决方案通常是最好的解决方案。程序员的时间很宝贵。选择最有效的解决方案。等他深入研究jQuery框架时,他可能会直接使用jQuery。 - Patrick W. McMahon
@PatrickW.McMahon - 不需要查看成千上万行代码。我在我的答案中告诉了OP要查看哪个方法。W3Schools不会向OP展示在每个浏览器中实现相同功能的最佳方法。如果你想成为一个更好的开发者,你应该从好的代码中学习。W3Schools肯定没有好的代码。 - Justin Niessner
@Justin Niessner - W3School是Web的管理机构。如果您认为他们没有按照自己制定的标准提供高质量的代码,那么我们手头的问题就更大了。运营W3School的成员正是正在开发新HTML5标准的人。 - Patrick W. McMahon
@PatrickW.McMahon - W3School绝对不是Web的管理机构。那应该是W3C(万维网联盟)http://www.w3.org/。W3Schools已经比以前好了,但仍然存在问题。请查看http://www.w3fools.com获取更多信息。 - Justin Niessner
显示剩余2条评论

3

这实际上是大多数人使用像jQuery这样的框架的最大原因之一,因为解决此问题在不同浏览器中并不一致。


2
我知道,并且我自己是一个jquery的重度用户,但在这种情况下,我不想依赖于框架,我被要求避免使用它。 - kristian nissen
2
我建议您可能查看jQuery的源代码,并将您想要的代码移植到您的项目中。 - Scott Fox
1
获取DOM准备好只需要一行代码 document.addEventListener("DOMContentLoaded", function(e){/*dom ready do something*/});,为什么需要使用框架来实现DOM准备好的功能呢? - Patrick W. McMahon

1
试试这个:
链接:在GitHub上的onDomReady 或者下面的源代码:
if(document.ondomready == undefined) {
    document.ondomready = {};
    document.ondomready = null;
} else {
    document.ondomready=document.ondomready;
}
var oldonload=document.onload;
var isLaunched = 0;
document.onload = function() {  
    if(oldonload !== null) {
        oldonload.call();
    }
};
document.addEventListener("DOMContentLoaded", function onDom(evt) {
    var olddomready = document.ondomready;
    if(olddomready !== null) {
        if(isLaunched == 0) {
            olddomready.call(evt);
            isLaunched == 1;
            //We now launched the mapped DOMContentLoaded Event
        } else {
            //We already launched DOMContentLoaded Event
        }
    }
}, false);

我已经在Opera 11.x/12.x上测试过了。其他浏览器还没有测试过。如果有测试过的,请告诉我。

注意:我还没有上传存储库,但我会在闲暇时间尽快上传。


"DOMContentLoaded" 在 DOM 完全加载和解析时只会触发一次。你添加的所有额外内容都是无用的。 - Patrick W. McMahon

0
如果您想要更小的库,只做特定的事情,您可以使用microjs中的库。对于手头的问题,可以使用DOMStatic库的就绪方法。

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