Code inside DOMContentLoaded event not working

JavascriptBabeljsAddeventlistenerDomcontentloaded

Javascript Problem Overview


I have used

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
</head>
<body>
  <button type="button" id="button">Click</button>
  <pre id="output">Not Loading...</pre>

  <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.17.0/babel.min.js"></script>
  <script type="text/babel">
    document.addEventListener('DOMContentLoaded', function () {
      const button = document.getElementById('button');
      const output = document.getElementById('output');

      output.textContent = 'Loading...';

      addEventListener('click', function () {
        output.textContent = 'Done';
      });
     });
  </script>
</body>
</html>

but it seems the code inside document.addEventListener('DOMContentLoaded', function () {}); is not loading.

If I remove this from my code, it suddenly works.

I have made a JS Bin here.

Javascript Solutions


Solution 1 - Javascript

It's most likely because the DOMContentLoaded event was already fired at this point. The best practice in general is to check for document.readyState to determine whether or not you need to listen for that event at all.

if (document.readyState !== 'loading') {
    console.log('document is already ready, just execute code here');
    myInitCode();
} else {
    document.addEventListener('DOMContentLoaded', function () {
        console.log('document was not ready, place code here');
        myInitCode();
    });
}

function myInitCode() {}

Solution 2 - Javascript

The event has already fired by the time that code hooks it. The way Babel standalone works is by responding to DOMContentLoaded by finding and executing all of the type="text/babel" scripts on the page. You can see this in the index.js file:

// Listen for load event if we're in a browser and then kick off finding and
// running of scripts with "text/babel" type.
const transformScriptTags = () => runScripts(transform);
if (typeof window !== 'undefined' && window && window.addEventListener) {
  window.addEventListener('DOMContentLoaded', transformScriptTags, false);
}

Just run the code directly, without waiting for the event, since you know Babel standalone will wait for it for you.

Also note that if you put you script at the end of the body, just before the closing </body> tag, there's no need to wait for DOMContentLoaded even if you don't use Babel. All of the elements defined above the script will exist and be available to your script.


In a comment you've asked:

> But I am using Babel standalone in development, but I will pre-compile it when I go into production. Should I add it back on when I go into production?

Just ensure that your script tag is at the end of body as described above, and there's no need to use the event.

If it's important to you to use it anyway, you can check to see whether the event has already run by checking document.readyState (after following the link, scroll up a bit):

function onReady() {
    // ...your code here...
}
if (document.readyState !== "loading") {
    onReady(); // Or setTimeout(onReady, 0); if you want it consistently async
} else {
    document.addEventListener("DOMContentLoaded", onReady);
}

document.readyState goes through these stages (scroll up slightly from the link above):

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

Solution 3 - Javascript

Thanks to Ruslan & here is the full code snippet with the convenient detach of the DOMContentLoaded handler after it is used.

'use strict';
var dclhandler = false;
if (document.readyState !== 'loading') {
    start();
} else {
    dclhandler = true;
    document.addEventListener('DOMContentLoaded', start);
}
function start() {
    if (dclhandler) { document.removeEventListener('DOMContentLoaded', start); }
    console.log('Start the site`s JS activities');
}

Solution 4 - Javascript

Another option would be to use the readystatechange event. The readystatechange event fires when the readyState attribute of the document has changed. The readyState attribute can be one of the following three values: 'loading', 'interactive', or 'complete'. An alternative to using the DOMContentLoaded event is to look for the readyState to equal 'interactive' inside of the document's readystatechange event, as in the following snippet.

document.onreadystatechange = function () {
  if (document.readyState === 'interactive') {
    // Execute code here
  }
}

Although, in your case, the document's readyState seems to have already reached 'complete'. In that case, you can simply swap 'interactive' for 'complete' in the snippet above. This is technically equal to the load event instead of the DOMContentLoaded event.

Read more on MDN, Document.readyState Document: readystatechange event

Solution 5 - Javascript

I also encountered the same problem when I enable both HTML Auto Minify and Rocket Loader in Cloudflare. the conclusion is that DOMContentLoaded event is missing when handled by these features simultaneously.

You can add the following code before all DOMContentLoaded event listeners in the HTML file script block to fix this.

var inCloudFlare = true;
window.addEventListener("DOMContentLoaded", function () {
    inCloudFlare = false;
});
if (document.readyState === "loading") {
    window.addEventListener("load", function () {
        if (inCloudFlare) window.dispatchEvent(new Event("DOMContentLoaded"));
    });
}

For explanation please go to my blog.

https://hollowmansblog.wordpress.com/2021/10/18/solution-to-missing-domcontentloaded-event-when-enabling-both-html-auto-minify-and-rocket-loader-in-cloudflare/

Solution 6 - Javascript

I would use document.addEventListener("DOMContentLoaded", () => {/*yourcode*/});

Solution 7 - Javascript

https://learnwithparam.com/blog/vanilla-js-equivalent-of-jquery-ready/

function ready(callbackFunc) {
  if (document.readyState !== 'loading') {
    // Document is already ready, call the callback directly
    callbackFunc();
  } else if (document.addEventListener) {
    // All modern browsers to register DOMContentLoaded
    document.addEventListener('DOMContentLoaded', callbackFunc);
  } else {
    // Old IE browsers
    document.attachEvent('onreadystatechange', function() {
      if (document.readyState === 'complete') {
        callbackFunc();
      }
    });
  }
}

ready(function() {
  // your code here
});

Solution 8 - Javascript

My clean aproach...

if (document.readyState !== 'loading') init()
else document.addEventListener('DOMContentLoaded', init);

function init() {
    console.log("Do it !");
    ...
}

Solution 9 - Javascript

I've come into this problem with my live server. My live server is running on cloudflare with some cache and rocket loading js. I assume this is what is causing the unexpected behaviour. The code was working perfectly on local server when using DOMContentLoaded, but not on live.

If you can use jQuery, switching to

jQuery(document).ready(function() {
//code...
})

work as expected.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJamgreenView Question on Stackoverflow
Solution 1 - JavascriptjAndyView Answer on Stackoverflow
Solution 2 - JavascriptT.J. CrowderView Answer on Stackoverflow
Solution 3 - JavascriptValentine ShiView Answer on Stackoverflow
Solution 4 - JavascriptJazzBrothaView Answer on Stackoverflow
Solution 5 - JavascriptHollow ManView Answer on Stackoverflow
Solution 6 - JavascriptRobin GrgiView Answer on Stackoverflow
Solution 7 - JavascriptАнастасияView Answer on Stackoverflow
Solution 8 - JavascriptDaniel De LeónView Answer on Stackoverflow
Solution 9 - JavascriptGabriel GView Answer on Stackoverflow