0

I was going to ASAP script that checks if body tag is loaded, but it causes whole page to freeze.

Here it is:

while (!document.body)
    if (document.body) console.log('loaded');

This of course wouldn't work properly in all cases, but I'm puzzled why this becomes an infinite loop and freezes the page.

user229044
  • 232,980
  • 40
  • 330
  • 338
Jinx
  • 857
  • 2
  • 14
  • 28
  • 3
    "I'm puzzled why this becomes an infinite loop and freezes the page." - because it is an infinite loop? The DOM parser is halted while JS is executing synchronously. – Fabrício Matté Jun 07 '14 at 17:33
  • http://www.callmenick.com/2014/06/04/check-everything-loaded-javascript/ – mplungjan Jun 07 '14 at 17:35
  • You need to use events. You cannot do this with a loop. – user229044 Jun 07 '14 at 17:37
  • If you want a plain javascript function that will tell you when the DOM is ready, see here: [pure JavaScript equivalent to jQuery's $.ready() how to call a function when the page/dom is ready for it](http://stackoverflow.com/questions/9899372/pure-javascript-equivalent-to-jquerys-ready-how-to-call-a-function-when-the/9899701#9899701) – jfriend00 Jun 07 '14 at 18:44

2 Answers2

7

Javascript is single-threaded. While that loop is running, nothing else runs, so the body can't be loaded. There's nothing in the body of the loop that changes the value of document.body, so if it's not set when you start, it never will be.

Barmar
  • 741,623
  • 53
  • 500
  • 612
  • I don't think this issue is due to JS's single-threadness, rather the browser UI's single-threadness. – Fabrício Matté Jun 07 '14 at 17:35
  • So it then depends on where the script is included. If it runs before the body tag it will fail, after it should be fine then. – martin.code Jun 07 '14 at 17:36
  • 2
    I consider them one and the same. Everything in a page effectively runs in a single thread. – Barmar Jun 07 '14 at 17:36
  • @feat.martin Right -- it's either a zero-run loop or an infinite loop, but nothing in between. – Barmar Jun 07 '14 at 17:36
  • But I don't need it afterwards :D @feat.martin – Jinx Jun 07 '14 at 17:37
  • 1
    @feat.martin Putting it *after* is completely redundant. The body of the `while` loop will never be entered. – user229044 Jun 07 '14 at 17:37
  • Damn, I thought I was so clever. – Jinx Jun 07 '14 at 17:38
  • @Barmar Well, JS being single-threaded is an aspect of the ECMAScript language. The fact that it halts the DOM parser has more to do with the browser UI's thread being halted while the JS code runs. – Fabrício Matté Jun 07 '14 at 17:38
  • Why not use document.onload? – martin.code Jun 07 '14 at 17:39
  • 1
    @feat.martin he's apparently trying to do something BEFORE the document is loaded, maybe some kind of progress thing – Barmar Jun 07 '14 at 17:42
  • But why not just start an operation in a script included in the head then? Javascript encountered will just start to execute by the browser even if the page is not completely loaded, and then he can just stop this operation given an onload event? – martin.code Jun 07 '14 at 17:48
  • And yes, saying it runs all in the same thread may be an acceptable over-simplification, but the way you worded it looks like the DOM parser runs inside the JS thread. But obviously they are two completely different beasts which the browser abstracts away into the seemingly synchronicity of a single thread. – Fabrício Matté Jun 07 '14 at 17:49
  • In other words, the JS interpreter and its characteristics (single-threaded) have absolutely nothing to do with the DOM renderer, it is the UI thread which is responsible for running the DOM renderer and JS interpreter synchronously. – Fabrício Matté Jun 07 '14 at 18:02
  • They may be technically separate, but as far as a Javascript programmer is concerned, all of network loading, rendering, and script execution effectively runs in a single, synchronous thread. While a script is running, nothing can modify the DOM other than the script. If it's a simplification, so be it -- unless you're actually a browser implementor, it's a useful simplification. – Barmar Jun 07 '14 at 18:13
2

This code executes synchronously, so if document.body is undefined at first, it doesn't allow the browser to populate it between iterations. Use setTimeout or setImmediate to do that, or, better yet, listen for the DOMContentLoaded event or use jQuery's $(document).ready().

(function ready() {
  if (document.body) console.log('loaded');
  else setTimeout(ready, 500); // delay 1/2 second
})();
Klaus
  • 724
  • 6
  • 14
  • Yes, that was my second solution – Jinx Jun 07 '14 at 17:41
  • As i commented earlier, considering this: http://stackoverflow.com/questions/2920129/can-i-run-javascript-before-the-whole-page-is-loaded. Why dont just open a script tag first in the head that just starts doing whatever you want to do before the body loads and then falsify the condition for this to once the body has loaded? I struggle to see your motivation however, the usual thing is to listen to when to DOM is available, as @Klaus says. – martin.code Jun 07 '14 at 18:09
  • @feat.martin That's what he tried in the question. He started a `while` loop, and was expecting the condition to falsify when the body was loaded. The problem is that the body can't load while the script is running. That's why this `setTimeout` is necessary -- it allows the script to exit and restart periodically, to allow the body to load. – Barmar Jun 07 '14 at 18:15