在 Firefox 中,window.onload 在 jQuery document.ready 之前触发

sam*_*d0n 7 firefox jquery document-ready domcontentloaded jquery-events

我创建了一个显示问题的 Codepen:https ://codepen.io/samuelg0rd0n/pen/ExVGQEV

在 Chrome 中,它在窗口 onload 事件之前正确触发 DOMContentLoaded 事件和 jQuery 文档就绪事件。然而,在 Firefox 中,顺序是:

DOMContentLoaded
window.onload
$(window).on('load', function() { ... });
$(document).ready();
$(function() { ... });
Run Code Online (Sandbox Code Playgroud)

window.onload 和 jQuery 窗口 onLoad 都会在 jQuery 文档就绪事件之前触发。我很确定这一定是 jQuery 或 Firefox 中的某种错误。谁可以给我解释一下这个?谢谢。

Chrome版本:81.0.4044.138

火狐版本:75.0

在 macOS 和 Linux 上都尝试过,结果相同。

Pav*_*eva 4

这似乎是一个 jQuery 问题。我已经这样测试过:

  1. 当我使用普通 JS 函数时,我在 Firefox 中得到正确的顺序(首先触发 DOM Ready,然后触发窗口加载):

     document.addEventListener('DOMContentLoaded', function(event) {
       console.log('DOMContentLoaded');
     }); 
     window.addEventListener('load', function(event) {
       console.log('window load');
     });
    
    Run Code Online (Sandbox Code Playgroud)
  2. 当我使用 jQuery 时,我得到了错误的顺序(首先触发窗口加载,然后触发 DOM Ready):

     $( window ).on( 'load', function() {
       console.log('window load');
     }); 
     $(function () {
       console.log('DOM Ready');
     }); 
    
    Run Code Online (Sandbox Code Playgroud)

注意:这种错误的顺序仅在资源被缓存且Web控制台关闭时才会发生。

您可以通过运行以下代码以同样的方式验证触发事件的正确顺序:

document.addEventListener('readystatechange', function(event) {
  console.log( 'readystatechange', document.readyState );
});
Run Code Online (Sandbox Code Playgroud)

在 Firefox 中,它总是首先触发“交互”状态(例如 DOMContentLoaded),然后触发“完成”状态(例如窗口加载)。

因此,这不是 DOMContentLoaded 事件在窗口加载事件之前触发,而是 jQuery 的 DOM 就绪事件处理程序由于某种原因在窗口加载事件之前触发。

这是在 jQuery 版本 3.5.1(现在最新的)上进行测试的。在 Mac 上运行的 Firefox 版本 82.0.3(64 位)。

目前,我使用一个简单的解决方法,添加一个标志变量并在 jQuery 的 DomReady 事件中检查它。如果在触发 jQuery 的 DOM Ready 事件时窗口已经加载,我将运行必须在窗口加载时再次运行的函数。它并不理想或优雅,但完全满足简单的网页脚本。

var windowLoaded = false;
$( window ).on( 'load', function() {
  windowLoaded = true;
  runMyWindowLoadFunctions();
} );
$(function() {
  if ( windowLoaded ) {
    // Beware, runMyWindowLoadFunctions will run twice
    runMyWindowLoadFunctions();
  }
});
Run Code Online (Sandbox Code Playgroud)

更新

刚刚检查了就绪事件的 jQuery 源代码https://github.com/jquery/jquery/blob/master/src/core/ready.js

// The ready event handler and self cleanup method
function completed() {
  document.removeEventListener( "DOMContentLoaded", completed );
  window.removeEventListener( "load", completed );
  jQuery.ready();
}

// Catch cases where $(document).ready() is called
// after the browser event has already occurred.
if ( document.readyState !== "loading" ) {

  // Handle it asynchronously to allow scripts the opportunity to delay ready
  window.setTimeout( jQuery.ready );

} else {

  // Use the handy event callback
  document.addEventListener( "DOMContentLoaded", completed );

  // A fallback to window.onload, that will always work
  window.addEventListener( "load", completed );
}
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,在处理 jQuery 脚本之前可能会触发窗口加载事件,在这种情况下,jQuery 正在运行“就绪”事件处理程序。据我了解,jQuery 在运行窗口加载事件处理程序之前不会检查其“就绪”事件处理程序是否已被触发。