Code inside DOMContentLoaded event not working
Asked Answered
H

9

78

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.

Hitt answered 12/10, 2016 at 8:14 Comment(6)
FWIW, there's no need at all for using DOMContentLoaded. Your script is already at the very end of the document, which is plenty good enough.Incurable
Why you add text/babel type to your script?Orthicon
This is just an example. In my other scripts, I am using arrow functions, so I need to use babel to convert it from ES6 to ES5Hitt
But when i remove it, code did worked.Orthicon
@Mohammad: The OP is using Babel Standalone to use Babel's transpiling in the browser. That's how you tell Babel Standalone which scripts it should transpile.Incurable
Replace text/babel to text/javascript and it worksGradatim
B
186

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() {}
Bakelite answered 12/10, 2016 at 8:17 Comment(12)
Nice one -- it took the OP asking me what to do about whether it had run to inspire me to add that, then I see you already had...Incurable
Ha, yes I guess that is pretty much the standard way to go in vanilla JS nowadays.Bakelite
Well, I think the standard way to go is just to ensure the script is at the end of body and not fuss about with DOMContentLoaded. It's only code that will be used where the author has no control over where the script tag will go that need this (like, amusingly, Babel Standalone).Incurable
Is that so T.J.? If your script block is at the end of the body it can never collide with "other stuff" DOMContentLoaded watches over?Bakelite
If it's at the end of body, all of the elements defined above it are present and accessible, yes.Incurable
You're right. On the other hand, it looks like static build processes like webstack getting very popular. In such environments, where js code pretty much regulates everything (css loading for instance) we want to load those js-bundles as early as possible. I guess in such cases we really need the DOMContentLoaded event again.Bakelite
Why? You don't want to wait for DOMContentLoaded to load your CSS.Incurable
No of course not, so we load a precompiled js-bundle as early as possible (head). But since javascript now regulates the deploying of stylesheet alongside program logic, you need to listen for DCL within that bundle to execute your js logic.Bakelite
Ah, I see. Not a fan of using script to load CSS, but I see how if you conflate things like that, you'd need the event.Incurable
Don't check for document.readyState === 'complete'! Check for document.readyState !== 'loading', because it is useless to listen for DOMContentLoaded if document.readyState is 'interactive'. Test your error and fix the code.Oralla
This code is working on codesandbox.io. Document.readState is already complete in the preview window without being handled within DOMContentLoaded event handler.Nuzzle
@Oralla could you tell why it is useless? Do you mean that it will also not fire if the state is equals to 'interactive'?Koheleth
I
20

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.

Incurable answered 12/10, 2016 at 8:18 Comment(3)
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?Hitt
@Jamgreen: I've added a response to that comment to the answer above.Incurable
@Ruslan: Thank you. I was misled by the spec saying "Each document has a current document readiness. When a Document object is created, it must have its current document readiness set to the string "loading" if the document is associated with an HTML parser, an XML parser, or an XSLT processor, and to the string "complete" otherwise. Various algorithms during page loading affect this value. When the value is set, the user agent must fire a simple event named readystatechange at the Document object." But just above that, it's clear that there's an intermediate "interactive" state! :-)Incurable
A
2

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');
}
Amphibolous answered 8/10, 2017 at 6:34 Comment(0)
N
2

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

Nefarious answered 9/1, 2020 at 12:44 Comment(1)
i just had a problem where the DOMContentLoaded not worked, so with your answer and the readystate = complete check it worked fine - Thanks a lot!Woven
A
2

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/

Archiepiscopate answered 17/10, 2021 at 16:43 Comment(0)
F
1

My clean aproach...

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

function init() {
    console.log("Do it !");
    ...
}
Fresnel answered 6/6, 2020 at 19:43 Comment(0)
S
0

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

Semiskilled answered 6/5, 2020 at 17:24 Comment(0)
A
0

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
});
Alleviator answered 7/5, 2020 at 11:32 Comment(1)
As of the time of this post - Run code snippet comes up emptyToneless
A
-3

DOMContentLoaded event is working in when we call it from script tag. But that event not working in js file

Androcles answered 16/8, 2022 at 10:42 Comment(1)
As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.Forbid

© 2022 - 2024 — McMap. All rights reserved.