angular / angular.js

AngularJS - HTML enhanced for web apps!
https://angularjs.org
MIT License
58.85k stars 27.52k forks source link

jqLite "document ready" implementation causes lazy-loaded apps bootstrap to be delayed after window.onload #15690

Open laurentgoudet opened 7 years ago

laurentgoudet commented 7 years ago

Do you want to request a feature or report a bug?

Report a bug

What is the current behavior?

jqLite current "document ready" implementation uses DOMContentLoaded with window.onload as a fallback.

function jqLiteReady(fn) {
  function trigger() {
    window.document.removeEventListener('DOMContentLoaded', trigger);
    window.removeEventListener('load', trigger);
    fn();
  }

  // check if document is already loaded
  if (window.document.readyState === 'complete') {
    window.setTimeout(fn);
  } else {
    // We can not use jqLite since we are not done loading and jQuery could be loaded later.

    // Works for modern browsers and IE9
    window.document.addEventListener('DOMContentLoaded', trigger);

    // Fallback to window.onload for others
    window.addEventListener('load', trigger);
  }
}

If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem via https://plnkr.co or similar (template: http://plnkr.co/edit/tpl:yBpEi4).

angular.element(function() {
    angular.bootstrap(document.getElementById('myApp'), ['myApp']);
}

Unfortunately I can't create a plunker for it as plunker always execute code after the load event.

What is the expected behavior?

According to https://html.spec.whatwg.org/multipage/dom.html#current-document-readiness, document.readyState:

Returns "loading" while the Document is loading, "interactive" once it is finished parsing but still loading sub-resources, and "complete" once it has loaded.

What is the motivation / use case for changing the behavior?

As after DOMContentLoaded the document is interactive, checking for readyState === 'complete' is incorrect and forces asynchronously loaded Angular apps to wait for the - potentially far away - window.onload event.

The jQuery "document ready" implementation itself had a similar bug, which has been fixes in https://github.com/jquery/jquery/commit/dabd5ba96c05279b3ffb052db5b8d17f75996694. Apparently IE9/IE10 readyState implementation is buggy and one has to rely on document.readyState !== "loading" && !document.documentElement.doScroll instead of readyState === 'interactive'.

Which versions of AngularJS, and which browser / OS are affected by this issue? Did this work in previous versions of AngularJS? Please also test with the latest stable and snapshot (https://code.angularjs.org/snapshot/) versions.

All versions of AngularJS & all browsers

gkalpak commented 7 years ago

Sounds reasonable. @mgol: Any idea why we aren't following jQuery?

mgol commented 7 years ago

readyState is buggy in many browsers. jQuery has tried to use it in the past and had to revert. Now it's back in the source with some hacks to exclude IE 9-10 where it's broken but it seems we'll have to revert back in jQuery 3.2.0. See Timmy's comment at: https://github.com/jquery/jquery/issues/3271#issuecomment-249604578

All that to say, a perfect .ready is impossible. This is a choice between interactive or complete. It pains me to say it, but I think we have to drop interactive. It would be worse to document that jQuery can't be loaded with the defer attribute.

I can't recommend relying on readyState because of that. At the very least I'd at least wait for what the new solution will be in jQuery 3.2.0.

gkalpak commented 7 years ago

😄 Things are never as simple as they may seem (especially when there is more than one browser involved). Totally, agree we should wait and see what wy jQuery will go.