在页面DOM加载完成(但在window.onload之前),如何接收通知?

7
我知道有一些方法可以在页面主体加载完成时(在所有图像和第三方资源加载之前,触发window.onload事件)得到通知,但对于每个浏览器而言,这些方法都不同。
是否有一种确定的方法可以在所有浏览器上实现?
到目前为止,我知道以下几种方法:
  • DOMContentLoaded:适用于Mozilla、Opera 9和最新的WebKits。这涉及添加一个监听器到事件中:

    document.addEventListener( "DOMContentLoaded", [初始化函数], false );

  • 延迟脚本:在IE上,您可以发出带有@defer属性的SCRIPT标签,该标签可在BODY标签关闭后可靠地加载。

  • 轮询:在其他浏览器上,您可以保持轮询,但是否存在标准的轮询对象,或者您需要在每个浏览器上执行不同的操作?

我想能够不使用document.write或外部文件来实现。
这可以通过jQuery简单地实现:
$(document).ready(function() { ... })

但是,我正在编写一个JS库,不能指望jQuery总是存在。


这方面有什么新消息吗?这个答案已经放在这里2.5年了...也许浏览器供应商和标准现在更加先进了? - Akku
不,虽然IE9现在也支持DOMContentLoaded。所以我们只能坐等<IE9浏览器的市场份额低于1%。 - Justus Romijn
9个回答

8

没有跨浏览器的方法可以检查DOM何时准备好--这就是为什么存在像jQuery这样的库,来抽象掉不兼容的小细节。

Mozilla、Opera和现代WebKit支持DOMContentLoaded事件。IE和Safari需要一些奇怪的技巧,比如滚动窗口或检查样式表。详细信息包含在jQuery的bindReady()函数中。


3

1
看起来这是从这段代码派生出来的:http://javascript.nwbox.com/ContentLoaded/。我在各种浏览器中尝试了这个原始代码,效果非常好。 - arlomedia

2
使用像jQuery这样的库将为您节省无数小时的浏览器不一致性问题。 在这种情况下,使用jQuery只需
$(document).ready ( function () {
    //your code here
});

如果你好奇,可以查看源代码以了解它是如何完成的。但在当今时代,我认为任何人都不应该重新发明轮子,因为库的编写者已经为你完成了所有痛苦的工作。


2
YUI使用三种测试来实现这个功能:对于Firefox和最近的WebKit,会触发DOMContentLoaded事件。对于旧版本的Safari,会监视document.readyState直到它变成"loaded"或"complete"。对于IE,会创建一个HTML

标签并调用"doScroll()"方法,如果DOM没有准备好就会出错。YAHOO.util.Event的源码显示了YUI特定的代码。在Event.js中搜索"doScroll"即可找到。


1

只需从jQuery中提取相关的代码,John Resig已经在jQuery中涵盖了这个问题的大部分基础知识。


0
为什么不是这样:
<body>  
  <!-- various content -->  
  <script type="text/javascript">  
  <!--  
    myInit();  
  -->
  </script>  
</body>  

如果我理解正确的话,myInit函数会在浏览器在页面中加载到它时立即执行,而它是body标签中的最后一个元素。

不幸的是,如果我无法控制页面(例如创建一个将通过<script>标签包含的JS库),这个简单的解决方案就行不通。 - levik

0

你正在寻找的花哨的跨浏览器解决方案......不存在......(想象一下人群发出“啊啊啊”的声音)。

DomContentLoaded 是最好的选择。对于IE老版本,仍需要使用轮询技术。

  1. 尝试使用addEventListener;
  2. 如果不可用(显然是IE),检查框架;
  3. 如果不是框架,请滚动直到没有错误抛出(轮询);
  4. 如果是框架,请使用IE事件document.onreadystatechange;
  5. 对于其他不支持的浏览器,请使用旧的document.onload事件。

我在javascript.info上找到了以下代码示例,您可以使用它来覆盖所有浏览器:

function bindReady(handler){

    var called = false

    function ready() { 
        if (called) return
        called = true
        handler()
    }

    if ( document.addEventListener ) { // native event
        document.addEventListener( "DOMContentLoaded", ready, false )
    } else if ( document.attachEvent ) {  // IE

        try {
            var isFrame = window.frameElement != null
        } catch(e) {}

        // IE, the document is not inside a frame
        if ( document.documentElement.doScroll && !isFrame ) {
            function tryScroll(){
                if (called) return
                try {
                    document.documentElement.doScroll("left")
                    ready()
                } catch(e) {
                    setTimeout(tryScroll, 10)
                }
            }
            tryScroll()
        }

        // IE, the document is inside a frame
        document.attachEvent("onreadystatechange", function(){
            if ( document.readyState === "complete" ) {
                ready()
            }
        })
    }

    // Old browsers
    if (window.addEventListener)
        window.addEventListener('load', ready, false)
    else if (window.attachEvent)
        window.attachEvent('onload', ready)
    else {
        var fn = window.onload // very old browser, copy old onload
        window.onload = function() { // replace by new onload and call the old one
            fn && fn()
            ready()
        }
    }
}

-2

这个很好用:

setTimeout(MyInitFunction, 0);

不,它并不可靠。很抱歉。:-/ - Már Örlygsson

-2
使用setTimeout可以工作得很好,尽管执行时间由浏览器决定。如果将超时时间设置为零,浏览器将在事情“安定”时执行。
好处在于您可以拥有许多setTimeout,而不必担心链接onLoad事件。
setTimeout(myFunction, 0);
setTimeout(anotherFunction, 0);
setTimeout(function(){ doSomething ...}, 0);

等到文档加载完成后,它们都会运行;或者如果你在文档加载完成后设置了一个函数,它们将在你的脚本运行完后运行。

它们运行的顺序是不确定的,并且在不同的浏览器中可能会改变。因此,你不能指望例如myFunctionanotherFunction之前运行。


整个重点是尽早执行JS,但不要在页面的DOM完全就位之前执行-这将允许您检查DOM而无需等待潜在缓慢的图像加载。 - levik
1
不能保证正常工作。本文解释了原因:http://snook.ca/archives/javascript/settimeout_solve_domcontentloaded/ - Morgan Cheng

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