Well, that’s embarrassing, but today I stumbled upon that old caveat and took me a while to debug it. Technically, it’s not in the TIL category, because I already knew that, but still decided to blog it here, to make sure it won’t happen once again! :)

In short, you should not try to access document.body from scripts in <head>. For example, the following will always log nay :(:

<!DOCTYPE html>
<html>
  <head>
    <script>
      if (document.body) {
        console.log("yay!")
      } else {
        console.log("nay :(")
      }
    </script>
  </head>
  <body>
  </body>
</html>

That’s expected, because you’re writing a script before the actual <body> element, but it’s easy to forget in the modern era of web frameworks where you rarely have to deal with the actual DOM lifecycle.

The correct way to do that is to wait for the element to appear. For example, you can bind to DOMContentLoaded:

<!DOCTYPE html>
<html>
  <head>
    <script>
      document.addEventListener("DOMContentLoaded", function() {
        if (document.body) {
          console.log("yay!")
        } else {
          console.log("nay :(")
        }});
    </script>
  </head>
  <body>
  </body>
</html>

That will always log yay!.

Hope I won’t step on that rake again. :)