有没有一种明确的JavaScript(不是jQuery)方法来检查网页是否已完全加载?

8
有没有一种明确的JavaScript方法来检查网页是否完全加载?完全加载指的是100%完成,包括HTML、脚本、CSS、图片、插件、AJAX等所有内容!
由于用户交互可能会影响AJAX,因此假设除了初始页面请求之外,页面上没有进一步的用户交互。

1
“completely” 的意思是什么?只是 DOM 还是也包括图片、CSS 样式表等? - Pekka
完全地,意味着100%完成。HTML、脚本、CSS、图像、插件,所有东西! - jnthnclrk
希望使用标准的JavaScript非jQuery解决方案。 - jnthnclrk
我喜欢一个适用于我无法控制的页面的解决方案。 - jnthnclrk
7个回答

8
你所要求的几乎是不可能的。没有办法确定所有内容是否已经完全加载。原因如下:
  • 在许多网页上,AJAX只有在onload(或DOMReady)事件触发后才开始运行,这使得使用onload事件来查看页面是否已加载成为不可能。
  • 你可以理论上通过覆盖window.XMLHttpRequest来告诉网页是否正在执行AJAX请求,但你仍然无法确定像Flash这样的插件是否正在加载或未加载。
  • 在一些网站上,例如Twitter.com,页面只会加载一次,并且每次用户单击链接时都会向服务器发出AJAX请求。在这种情况下,如何确定页面何时完成加载?
  • 实际上,浏览器本身也不能完全确定页面是否已经完全加载,或者它是否即将发出更多的AJAX请求。

唯一确定所有内容是否已加载的方法是,在页面中加载任何东西的每一段代码都告诉你的代码它已经完成了加载。


一个可行但不完整的解决方案:你可以尝试用一个函数覆盖XMLHttpRequest并返回它,这样就可以判断当前是否正在进行AJAX事件。但是,这种解决方案无法查看插件是否已加载,并且你将无法区分在页面加载时触发的AJAX事件和定期发生的AJAX请求,例如Stack Overflow上的请求,如果你有新通知,则更改左上角的Stack Exchange图标。

可以尝试以下代码:

(function(oldHttpRequest){
  // This isn't cross-browser, just a demonstration
  // of replacing XMLHttpRequest

  // Keep track of requests
  var requests_running = 0;

  // Override XMLHttpRequest's constructor
  window.XMLHttpRequest = function() {
    // Create an XMLHttpRequest
    var request = new oldHttpRequest();

    // Override the send method
    var old_send = request.send;
    request.send = function () {
      requests_running += 1;
      old_send.apply(request, arguments);
    };

    // Wait for it to load
    req.addEventListener("load", function() {
      requests_running -= 1;
    }, false);

    // Return our modified XMLHttpRequest
    return request;
  };

  window.addEventListener("load", function() {
    // Check every 50 ms to see if no requests are running
    setTimeout(function checkLoad() {
      if(requests_running === 0)
      {
        // Load is probably complete
      }
      else
        setTimeout(checkLoad, 50);
    }, 50);
  }, false);
})(window.XMLHttpRequest)

我们可以假设在初始页面请求之后没有进一步的用户交互吗?仍然不可能吗? - jnthnclrk
1
@trnsfrmr 无法完全确定页面是否已加载,但您可能会得到一个很好的猜测。请参见上面更新的答案。 - Na7coldwater
我想知道是否有一种方法可以检查事件队列中是否还有待执行的内容? - Davy8
1
大多数情况下,Web开发的可能性几乎是无限的,但有时会出现一些“不可能”的情况。感谢您的尝试,这是目前为止最好的答案。 - jnthnclrk
1
非常好的回答!虽然在开发过程中应用谨慎的纪律可以跟踪这一点,但实际上浏览器无法确定这一点。这实际上归结为停机问题,图灵已经出名地证明了它是不可判定的。 - Peter

4
window.onLoad = function(){
    //Stuff to do when page has loaded.
}

或者

<body onLoad="functionCall()">

这个能处理 AJAX、Flash 和 Silverlight 吗? - jnthnclrk

4

这是:

window.onload

事件将在此时触发。


是的,严格来说它是“load”事件,“onload”是事件处理程序在“window”对象上的属性名称 :-) - Pointy
这个能处理 AJAX、Flash 和 Silverlight 吗? - jnthnclrk
3
不确定我是否理解你的问题 - 它无法处理 AJAX,因为 AJAX 内容通常是在页面加载后才会加载的 - 你需要正式等待,因为浏览器无法知道是否会使用 AJAX 加载更多内容。它将等待 flash 等内容加载完成,但不会等待它们完成其操作(可能是加载更多内容!)。 - ADW
谢谢。我需要一个完整的解决方案,甚至包括 AJAX。 - jnthnclrk
2
我认为这样的解决方案不存在。你需要告诉代码你的AJAX可能会加载什么以及何时加载。例如,如果在“正常”页面加载时设置一个定时器,在10分钟后通过AJAX加载更多内容,你不能期望一个通用方法能够在没有深度集成到你的网站的情况下知道这将发生。不过,如果有人能向我展示如何以通用的方式完成这个任务,我会很高兴的。 - ADW

3
基本上ADW和Keith已经回答了这个问题,但我建议不要使用window.onload,而是:
if (window.addEventListener) {
    window.addEventListener("load", myfunction, false);
} else {
    window.attachEvent("onload", myfunction);
}

function myfunction() {
   ...
}

这个能处理 AJAX、Flash 和 Silverlight 吗? - jnthnclrk
什么是 AJAX?这与 window.onload 事件完全相同。由于 AJAX 可以在页面加载之后很长时间调用(并且它是异步的),所以 AJAX 请求的“加载”不是 onload 事件的一部分。如果您在页面加载时已经执行了 AJAX 请求,则必须添加一个侦听器到 window.load 事件和一个到 AJAX 完成事件… 在两个侦听器中设置全局标志,并检查是否已经触发了另一个侦听器。如果没有,除了设置标志外,什么都不做。 - roberkules

2
使用 window.onloaddocument.readyState 和 AJAX 请求的回调函数的组合,您应该能够实现想要的功能。只需确保窗口已加载,DOM 已准备好进行操作,并跟踪 AJAX 请求。
特别是对于 AJAX,根据您发出的请求数量:每次发出请求时增加一个变量,并在该变量 === 总请求量时触发函数。如果您不知道 AJAX 请求的数量,但知道哪个请求会最后完成,只需在其完成时触发回调函数即可。
当所有条件都满足时,触发最终函数以执行您想要的操作,因为此时应该加载了所有内容。
关于 Flash 和 Silverlight 应用程序(不确定 window.onloaddocument.ready 是否跟踪这些应用程序),您还可以记录应用程序中加载的数据量,并在已加载的数据量 === 总数据量时,让应用程序触发一个函数或将变量递增到页面上。
window.onload = function() {
    var time = window.setInterval(function() {
     if(document.readyState == "interactive") { 
         increment(); 
         window.clearInterval(time);
     }
    }, 250);
}

var total = 10, current = 0;
var increment = function() {
    current += 1;
    if(current === total) { weAreDone(); }    
}

function weAreDone() {
    // Everything should be done!
}

我无法控制页面,因此不确定是否可以向AJAX和其他插件添加回调函数。我喜欢记录给定页面的数据量并进行测量的想法。有JavaScript函数可以实现吗? - jnthnclrk

1
这是我编写的非侵入式js函数,使用加载事件。在这种情况下,我在js脚本加载时触发事件,因为这是我的js自动加载器函数,但您可以使用相同的原则在其他项目上添加事件。只要提供此脚本来管理专用
标记中加载的js脚本即可。
function scriptLoaded(e) {
    var oLoadedScript = e.target || e.srcElement;
    alert ('loaded : ' + oLoadedScript.src);
    return false;
}

/**
 * Import js lib and fire function ControlData on events
 * @param js_librairies
 * @returns {Boolean}
 */
function init(){

    // lib import
    // Locate js in the div
    var myscript_location = document.getElementById('js_script_goes_here');


    // DEBUG
    if (undefined == myscript_location)
            alert('div not found');
        else
            alert('found div : ' + myscript_location);

 // to prevent js script from catching in dev mode
    var force_js_reload = "?version=1" ;

    for (var i=0; i < js_librairies.length ; ++i) {
        var my_script = document.createElement('script');
        my_script.defer = false;
        my_script.src = relative_path + js_librairies[i] + force_js_reload ;
        my_script.type = 'text/javascript';
        // DEBUG
        my_script.onload = scriptLoaded;

        myscript_location.appendChild(my_script);

    }
    return false;
}

/**
 * Start non intrusive js
 * @param func
 */
function addLoadEvent(func) {
      var oldonload = window.onload;
      if (typeof window.onload != 'function') {
        window.onload = func;
      } else {
        window.onload = function() {
          if (oldonload) {
            oldonload();
          }
          func();
        };
      }
}


//ONLOAD
addLoadEvent(init);

那么这其中哪一部分是非侵入式的呢?我看到了全局命名空间中的函数,alert(),它甚至覆盖了其他代码可能设置的任何window.onload处理程序... - Jani Hartikainen
@Jani:警告只是为了调试和解释目的。您可以将其移动,添加一个div并附加一个textnode msg,或者只需注释回调行(这就是为什么它在上面被注释为“DEBUG”)。 - hornetbzz

-1
function r(f){/in/(document.readyState)?setTimeout(r,9,f):f()}

提供者:史上最小的DOMReady代码 - Dustin Diaz

更新:适用于IE

function r(f){/in/.test(document.readyState)?setTimeout('r('+f+')',9):f()}

附言: window.onload 是一件非常不同的事情


“regexp is a function” 的技巧在 IE 中不起作用,遗憾。 - Pointy
这个能处理 AJAX、Flash 和 Silverlight 吗?IE 对我来说不太重要。 - jnthnclrk
1
这是一个onDomReady实现。当页面的HTML标记soup被浏览器完全下载和解析时,onDomReady就会触发。它不关心嵌入在页面上的图像、CSS文件或JavaScript文件,所以这根本不是发布者所问的。而且,它也无法处理AJAX、Flash和Silverlight。根本没有办法处理这些。即使是Onload事件也无法正确处理它们,除非您将自定义事件添加到您的flash/silverlight代码中。 - Ilya Volodin
@Ilya Volodin:我不是一个有远见的人。在17小时前之前,我对Flash、Ajax或Silverlight一无所知。问题已经被编辑过了。我希望你也会将onload投下反对票。 - naveen
@naveen onload更接近于他在编辑问题之前所要求的内容。onDomReady将在页面HTML被下载和解析后立即触发,这并不构成页面的完全加载。然而,onload将等待页面上的所有资源被下载,如图像、CSS文件和JavaScripts。它甚至会等到Flash和Silverlight文件被下载,但不会等到它们被执行,这是没有自定义代码在Flash或Silverlight内部不可能实现的。Ajax也是一样的。 - Ilya Volodin

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