如何在不使用jQuery的情况下编写类似于$(document).ready的JavaScript事件?

33

在jquery中,$(document).ready(function) 或 $(function) 能够实现什么功能?如何在没有使用jquery的情况下达到相同的效果,并兼容各种浏览器,允许附加多个函数。

注意:dom ready != window onload


我查看了jQuery源代码,其中有相当一部分与ready()处理程序相关的代码。如果我正确地阅读了代码,则该事件被称为DOMContentLoaded。IE不支持此功能,因此使用onreadystatechange代替。 - Šime Vidas
此外,它与 document.readyState 的值为 "complete" 有关。 - Šime Vidas
1
@Šime Vidas:这与许多事情有关(iframe加载有自己的怪癖?谁知道呢?),而且坦率地说,这是一个了不起的代码。我记得为什么我不再直接注册事件 - 它很难做到“正确”。 - Piskvor left the building
5
可能是 $(document).ready equivalent without jQuery 的重复问题。 - Roatin Marth
我相信使用jQuery比重新发明轮子更好。它有太多的怪癖(阅读浏览器兼容性问题)需要考虑。 - user
7个回答

24

以下是 jQuery 包装寻找的函数的方式 - 这段代码不需要 jQuery,且跨浏览器兼容。我已将所有对 jQuery.ready() 的调用替换为 yourcallback - 这是您需要定义的。

这里发生了什么:

  • 首先,定义了函数 DOMContentLoaded,当 DOMContentLoaded 事件触发时将使用它 - 它确保回调仅被调用一次。
  • 检查文档是否已加载 - 如果是,则立即触发回调。
  • 否则,嗅探功能(document.addEventListener / document.attachEvent),并将回调绑定到它(对于 IE 和普通浏览器不同,还有 onload 回调)。

从 jQuery 1.4.3 中提取的函数 bindReady() 和 DOMContentLoaded

/*
* Copyright 2010, John Resig
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*/
// Cleanup functions for the document ready method
// attached in the bindReady handler
if ( document.addEventListener ) {
DOMContentLoaded = function() {
    document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false );
    //jQuery.ready();
            yourcallback();
};

} else if ( document.attachEvent ) {
DOMContentLoaded = function() {
    // Make sure body exists, at least, in case IE gets a little overzealous 
            if ( document.readyState === "complete" ) {
        document.detachEvent( "onreadystatechange", DOMContentLoaded );
        //jQuery.ready();
                    yourcallback();
    }
    };
}

// 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( jQuery.ready, 1 );
    // ^^ you may want to call *your* function here, similarly for the other calls to jQuery.ready
    setTimeout( yourcallback, 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", jQuery.ready, false );
    window.addEventListener( "load", yourcallback, 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", yourcallback );

 }

这是51行纯JavaScript代码,仅用于可靠地注册事件。据我所知,没有更简单的方法。这表明像jQuery这样的封装器很有用:它们封装了功能嗅探和丑陋的兼容性问题,使您可以专注于其他事情。


2
我必须说,为了知道何时初始化使用DOM的脚本,需要大量复杂的代码...就像我在我的回答中发布的那样,我只是从关闭body标签后调用一个单独的初始化函数。 - Ruan Mendes
1
@Juan Mendes:是的,那个做法表面上看起来类似,但内联脚本标签也有它们的怪癖,比如在cough cough某些浏览器中阻止并行下载。而且不要忘记整个“让我们在它仍在加载时修改DOM”的乐趣。这就是为什么我更愿意使用$.ready()——一个在Google的CDN上缓存良好的库和一个良好工作的函数调用之后,它就可以正常工作了。但如果你想重新发明自1997年以来的所有JS黑客技巧,那就继续吧。 - Piskvor left the building
1
@Piskvor:“但是如果你想重新发明自1997年以来的所有JS黑客技巧,那就继续吧”?这怎么算是黑客技巧呢?我认为这是一个简单(优雅)的解决方案。如果在body标签后放置一个脚本标签,您可以确保DOM已准备就绪,而不是“在其仍在加载时修改DOM”。 - Ruan Mendes
1
@Juan Mendes:抱歉,我有点失态了。 - Piskvor left the building
1
你能给我指一些关于怪异模式和页面底部脚本的资料吗?http://developer.yahoo.com/performance/rules.html是一个链接,建议你总是把脚本放在页面底部。 - Ruan Mendes
显示剩余2条评论

9

有史以来最小的DOMReady代码。

<html>
  <head>
    <script>
      var ready = function (f) {
        (/complete|loaded|interactive/.test(document.readyState)) ?
            f() :
            setTimeout(ready, 9, f);
      };
    </script>
  </head>
  <body>
    <script>
      ready(function () {
        alert('DOM Ready!');
      });
    </script>
  </body>
</html>

快速问题:什么是“r”函数?我在FF中尝试时出现错误。 - Aerik
注意,这在幕后使用了 eval()。但是很酷的技巧! - Camilo Martin
1
如果你不在IE浏览器中,使用"setTimeout(ready, 9, f)";如果你在IE浏览器中,使用"setTimeout(function () { ready(f); }, 9)"。 - shawndumas
你的条件被反转了。 - the system

3

如果您支持IE9+和现代(2013年)版本的Chrome,FF,Safari等,那么这就是您所需的所有内容。

function ready(event) {
    // your code here
    console.log('The DOM is ready.', event);
    // clean up event binding
    window.removeEventListener('DOMContentLoaded', ready);
}

// bind to the load event
window.addEventListener('DOMContentLoaded', ready);

2
这是我使用的一种方法,似乎能够可靠地工作。
function ready(func) {
  var span = document.createElement('span');
  var ms = 0;
  setTimeout(function() {
    try {
      document.body.appendChild(span);

      document.body.removeChild(span);

      //Still here? Then document is ready
      func();
    } catch(e) {
      //Whoops, document is not ready yet, try again...

      setTimeout(arguments.callee, ms);
    }
  }, ms);
}

很简单,它只是不断尝试将一个空的<span>元素附加到document.body中。如果文档没有准备好,就会抛出异常,在这种情况下,它会使用新的setTimeout调用再次尝试。一旦没有抛出异常,它就会调用回调函数。
如果这种方法存在任何问题,我很乐意听取意见。它对我来说效果很好,但我没有进行任何流行JavaScript框架所必需的广泛测试。

两个词:内存泄漏。在旧的浏览器上,由于所有元素的添加和删除,你会像疯狂一样泄漏内存。另外,你正在用 JavaScript 重新实现忙等待循环(本身不是最高效的语言)。 - Piskvor left the building
我没有看到它。使用这种方法,当文档准备好时,一个元素只会被添加和删除一次。即使在此期间,也只有一个元素在超时函数之外的内存中被创建。此外,使用0毫秒延迟的setTimeout/setInterval与条件while循环不同。由于JS中计时器的工作方式以及此示例中超时函数内操作的简单性,性能不应受到显着影响。 - MooGoo

1

我看到了很多不同的尝试方法。最简单的方法(我认为是Yahoo最初建议的)就是在关闭body标签后调用您的初始化函数,这有点突兀,但只需要一行代码。


0

编辑

DomReady事件在JavaScript中并不存在。您可以通过遵循像Dean Edwards 这里这里等人所做的出色工作来实现自己的事件。有了这些,您就可以在文档上执行类似于窗口的事件附加过程。


查看用户83421对如何在Javascript中添加额外的window.onload事件的回答。

这里也进行一下总结。

if (window.addEventListener) // W3C standard
{
  window.addEventListener('load', myFunction, false); // NB **not** 'onload'
} 
else if (window.attachEvent) // Microsoft
{
  window.attachEvent('onload', myFunction);
}

你所拥有的代码片段仅适用于加载事件。 - Ruan Mendes

0

我把这段代码放在“关闭 body”标签之后和“关闭 html”标签之前,它运行得很好。load presets 函数为 CSS div 标签分配了宽度、高度和位置值,对于不同的屏幕尺寸非常有用。

document.onreadystatechange = function () {
if  (document.readyState == "interactive") {
loadPresets();
}

}


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