How to detect if DOMContentLoaded was fired - javascript

I'm trying to help developing a library and for it I'm trying to work with page loading.
In the process I want to make the library completely compatible with the use of defer and async.
What I want is simple:
How can I know that DOMContentLoaded was fired by the time the file is executed?
Why is this so difficult?
In IE, document.readyState show interactive before DOMContentLoaded.
I won't use browser detection in any way, it's against the policy of me and the rest of the participants.
What's the correct alternative?
Edit:
Seems like I wasn't clear enough. I'm not interested to know if the load event has already occurred!!! I already knew how to solve that problem! I want to know how to solve with DOMContentLoaded!!!

For seeing if all resources in the page have been loaded:
if (document.readyState === "complete" || document.readyState === "loaded") {
// document is already ready to go
}
This has been supported in IE and webkit for a long time. It was added to Firefox in 3.6. Here's the spec. "loaded" is for older Safari browsers.
If you want to know when the page has been loaded and parsed, but all subresources have not yet been loaded (which is more akin to DOMContentLoaded), you can add the "interactive" value:
if (document.readyState === "complete"
|| document.readyState === "loaded"
|| document.readyState === "interactive") {
// document has at least been parsed
}
Beyond this, if you really just want to know when DOMContentLoaded has fired, then you'll have to install an event handler for that (before it fires) and set a flag when it fires.
This MDN documentation is also a really good read about understanding more about the DOM states.

You can check the document's readyState value and this way tell if the event was fired or not. Here's the code to run a function named start() when the document has finished parsing:
if (/complete|interactive|loaded/.test(document.readyState)) {
// In case the document has finished parsing, document's readyState will
// be one of "complete", "interactive" or (non-standard) "loaded".
start();
} else {
// The document is not ready yet, so wait for the DOMContentLoaded event
document.addEventListener('DOMContentLoaded', start, false);
}
Notice that the code above detects when the document has finished parsing. Beware that's not the same as detecting if DOMContentLoaded was fired (which happens immediately after interactive), but it serves the same practical purpose, i.e., it tells you that the document has finished loading and has been parsed, but sub-resources such as images, stylesheets and frames are still loading (source).

How to correctly run something on DOMContentLoaded (or ASAP)
If you need to wait for the HTML document to be fully loaded and parsed to run something, you need to wait for DOMContentLoaded, no doubt about it. But if you can't control when your script is going to execute, it's possible that DOMContentLoaded was already fired by the time you get a chance to listen for the event.
To take that into consideration, your code needs to check if DOMContentLoaded was already fired and, if so, proceed to executing right away whatever it is that needed to wait for DOMContentLoaded:
function runWhenPageIsFullyParsed() {
// your logic goes here
}
if (document.readyState === "complete") {
// already fired, so run logic right away
runWhenPageIsFullyParsed();
} else {
// not fired yet, so let's listen for the event
window.addEventListener("DOMContentLoaded", runWhenPageIsFullyParsed);
}
The correct order of events during page loading is:
document.readyState changes to interactive
window's DOMContentLoaded event gets fired
document.readyState changes to complete
window's load event gets fired load
You can check the complete order of events during page loading in this fiddle.

Try this or look at this link
<script>
function addListener(obj, eventName, listener) { //function to add event
if (obj.addEventListener) {
obj.addEventListener(eventName, listener, false);
} else {
obj.attachEvent("on" + eventName, listener);
}
}
addListener(document, "DOMContentLoaded", finishedDCL); //add event DOMContentLoaded
function finishedDCL() {
alert("Now DOMContentLoaded is complete!");
}
</script>
Note
If you have a <script> after a <link rel="stylesheet" ...>
the page will not finish parsing - and DOMContentLoaded will not fire - until the stylesheet is loaded

If you want to know "exactly" if DOMContentLoaded was fired, you could use a boolean flag like this:
var loaded=false;
document.addEventListener('DOMContentLoaded',function(){
loaded=true;
...
}
then check with:
if(loaded) ...

Here is the thing, if some other library (such as jQuery) already used DOMContentLoaded, you can't use it again. That can be bad news, because you end up without being able to use it. You are gonna say, why not just use $(document).ready(), because, NO!, not everything needs to use jQuery, specially if it is a different library.

Related

When to call document.appendChild()?

In my page I dynamically add a script with a function like this:
function addScript( src ) {
var s = document.createElement( 'script' );
s.setAttribute( 'src', src );
document.body.appendChild( s );
}
I'm wondering though if I should wait for the document to be loaded before doing this. Is it possible that the body in the document may be not ready yet?
I'm using this at the moment:
document.onreadystatechange = function () {
if (typeof document.body !== 'undefined') {
addScript('http://my.url);
}
};
Not sure if it is unnecessary though and maybe delay the load of the script in the page.
It is possible that the body will not yet be available, but the head should be, so you could append it there.
document.head.appendChild(s);
Otherwise, if you do want to wait for the document to load, use the DOMContentLoaded event.
document.addEventListener("DOMContentLoaded", function() {
// inject the script
});
onreadystatechange is used in reference to a http request. ( or used only by the older versions of IE )
The best thing you could do it call the method just below the closing of the body tag. At this point you can be sure that the document has indeed loaded.
<script>
addScript('http://my.url');
</script>
</body>
But you can alway attach the callback to the onload event of the document.
Instead of doing document.onreadystatechange I would use document.onload, which fires after the document has finished loading all elements. onreadystatechange can fire multiple times in one session (which I have utilized previously), notably after ajax loading. onload only fires once the page finishes loading, and never again.
The readystatechange event is fired when the readyState attribute of a document has changed.
The load event fires at the end of the document loading process. At this point, all of the objects in the document are in the DOM, and all the images, scripts, links and sub-frames have finished loading.
Sources:
readystatechange
onload
I have had the same issue tryin to add a button to the youtube page using an extension, but since the page has a load of ajax calls my button always gets overwritten.
I would suggest you use
window.addEventListener("DOMNodeInserted", function(){
}, false);
With this each time the dom get changed your code will run, and you should put some logic checks if your element is still in the document if not add it again, and so on. Hackie but it works.

in chrome extensions how to trigger an event on deactivation?

I have a chrome extension which injects some DOM event listeners through the content scripts. I want to remove those event listeners from the DOM in the event that the user deactivates the plugins, is there a method to do so?
It's an interesting question. It has to do with a concept of "orphaned" scripts. I talk at length about those in an addendum here.
Problem is, as soon as the script becomes detached from the parent extension, Chrome APIs will fail. As such, detecting this is not straightforward.
There are many possible approaches:
Maintain an open port to the background page. The port will fire an onDisconnected event in case the background page ceases to exist.
This is an event-based approach - you will be able to react immediately.
But this has an important downside: maintaining an open port will prevent an Event page from unloading. So if you use a non-persistent background page, this is not optimal.
Periodically, or better yet - in the beginning of your handlers, try to do something with Chrome API. This will fail, and you can catch the exception and assume that the extension is orphaned.
Please note that this is pretty much undefined behavior. How Chrome API reacts can change over time.
function heartbeat(success, failure) {
try {
if(chrome.runtime.getManifest()) {
success();
} else { // will return undefined in an orphaned script
failure();
}
} catch(e) { // currently doesn't happen, but may happen
failure();
}
}
function handler() {
heartbeat(
function(){ // hearbeat success
/* Do stuff */
},
function(){ // hearbeat failure
someEvent.removeListener(handler);
console.log("Goodbye, cruel world!");
}
);
}
someEvent.addListener(handler);
Finally, there is a proposal to make a special event for this situation, but it's not implemented yet.
Specifically for updates when the extension is reloaded, you can make it inject scripts into existing pages and let old scripts know they should deactivate; however, since your question is about extension being removed, it doesn't help.
With the hard part done, actual removal of event listeners depends on how you added them, but should be straightforward.
onDisabled fired when app or extension has been disabled.
chrome.management.onDisabled.addListener(function callback)
EventTarget.removeEventListener() removes the event listener previously registered.
var div = document.getElementById('div');
var listener = function (event) {
/* do something here */
};
div.addEventListener('click', listener, false);
div.removeEventListener('click', listener, false);
I think you don't need to this, since once your extension is disabled, your event listener will be removed and won't be injected.

difference of load/ready events between window and document

Is there a difference between $(window).ready(function(){}) and $(document).ready(function(){})?
If so, what is it?
In that same vein, what is the difference between $(window).load(function(){}); and $(document).load(function(){});?
In researching this and other "ready" issues, I think I've found the difference care of this question.
Here is the ready function handler in jQuery.
ready: function( fn ) {
// Attach the listeners
jQuery.bindReady();
// Add the callback
readyList.add( fn );
return this;
},
It seems as though you can add any element (even complete jibberish) into this function and its callback will be added to the readyList. It also seems that when the document is ready, it will trigger all of the callbacks in readyList, regardless of whether they are part of the document or not.
See this fiddle for an example: http://jsfiddle.net/w5k5t/2/
I have not fully tested an order to these ready-calls, but a brief inspection of the code leads me to believe they will be executed synchronously in the order the callbacks are added.
Therefore, $(document).ready and $(window).ready are synonymous, just as is $('aoeuaoeuaoeu').ready is synonymous, and each will likely fire in the order they are declared.
document ready event executes already when the HTML-Document is loaded and the DOM is ready, even if all the graphics haven’t loaded yet.
The window load event executes a bit later when the complete page is fully loaded, including all frames, objects and images. Therefore functions which concern images or other page contents should be placed in the load event for the window or the content tag itself.
refer link1 link2

window.onload vs $(document).ready()

What are the differences between JavaScript's window.onload and jQuery's $(document).ready() method?
The ready event occurs after the HTML document has been loaded, while the onload event occurs later, when all content (e.g. images) also has been loaded.
The onload event is a standard event in the DOM, while the ready event is specific to jQuery. The purpose of the ready event is that it should occur as early as possible after the document has loaded, so that code that adds functionality to the elements in the page doesn't have to wait for all content to load.
window.onload is the built-in JavaScript event, but as its implementation had subtle quirks across browsers (Firefox, Internet Explorer 6, Internet Explorer 8, and Opera), jQuery provides document.ready, which abstracts those away, and fires as soon as the page's DOM is ready (doesn't wait for images, etc.).
$(document).ready (note that it's not document.ready, which is undefined) is a jQuery function, wrapping and providing consistency to the following events:
DOMContentLoaded - a newish event which fires when the document's DOM is loaded (which may be some time before the images, etc. are loaded); again, slightly different in Internet Explorer and in rest of the world
and window.onload (which is implemented even in old browsers), which fires when the entire page loads (images, styles, etc.)
$(document).ready() is a jQuery event. JQuery’s $(document).ready() method gets called as soon as the DOM is ready (which means that the browser has parsed the HTML and built the DOM tree). This allows you to run code as soon as the document is ready to be manipulated.
For example, if a browser supports the DOMContentLoaded event (as many non-IE browsers do), then it will fire on that event. (Note that the DOMContentLoaded event was only added to IE in IE9+.)
Two syntaxes can be used for this:
$( document ).ready(function() {
console.log( "ready!" );
});
Or the shorthand version:
$(function() {
console.log( "ready!" );
});
Main points for $(document).ready():
It will not wait for the images to be loaded.
Used to execute JavaScript when the DOM is completely loaded. Put event handlers here.
Can be used multiple times.
Replace $ with jQuery when you receive "$ is not defined."
Not used if you want to manipulate images. Use $(window).load() instead.
window.onload() is a native JavaScript function. The window.onload() event fires when all the content on your page has loaded, including the DOM (document object model), banner ads and images. Another difference between the two is that, while we can have more than one $(document).ready() function, we can only have one onload function.
A little tip:
Always use the window.addEventListener to add an event to window. Because that way you can execute the code in different event handlers .
Correct code:
window.addEventListener('load', function () {
alert('Hello!')
})
window.addEventListener('load', function () {
alert('Bye!')
})
Invalid code:
window.onload = function () {
alert('Hello!') // it will not work!!!
}
window.onload = function () {
alert('Bye!')
}
This is because onload is just property of the object, which is overwritten.
By analogy with addEventListener, it is better to use $(document).ready() rather than onload.
A Windows load event fires when all the content on your page is fully loaded including the DOM (document object model) content and asynchronous JavaScript, frames and images. You can also use body onload=. Both are the same; window.onload = function(){} and <body onload="func();"> are different ways of using the same event.
jQuery $document.ready function event executes a bit earlier than window.onload and is called once the DOM(Document object model) is loaded on your page. It will not wait for the images, frames to get fully load.
Taken from the following article:
how $document.ready() is different from window.onload()
$(document).ready(function() {
// Executes when the HTML document is loaded and the DOM is ready
alert("Document is ready");
});
// .load() method deprecated from jQuery 1.8 onward
$(window).on("load", function() {
// Executes when complete page is fully loaded, including
// all frames, objects and images
alert("Window is loaded");
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
The $(document).ready() is a jQuery event which occurs when the HTML document has been fully loaded, while the window.onload event occurs later, when everything including images on the page loaded.
Also window.onload is a pure javascript event in the DOM, while the $(document).ready() event is a method in jQuery.
$(document).ready() is usually the wrapper for jQuery to make sure the elements all loaded in to be used in jQuery...
Look at to jQuery source code to understand how it's working:
jQuery.ready.promise = function( obj ) {
if ( !readyList ) {
readyList = jQuery.Deferred();
// Catch cases where $(document).ready() is called after the browser event has already occurred.
// we once tried to use readyState "interactive" here, but it caused issues like the one
// discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15
if ( document.readyState === "complete" ) {
// Handle it asynchronously to allow scripts the opportunity to delay ready
setTimeout( jQuery.ready );
// Standards-based browsers support DOMContentLoaded
} else if ( document.addEventListener ) {
// Use the handy event callback
document.addEventListener( "DOMContentLoaded", completed, false );
// A fallback to window.onload, that will always work
window.addEventListener( "load", completed, false );
// If IE event model is used
} else {
// Ensure firing before onload, maybe late but safe also for iframes
document.attachEvent( "onreadystatechange", completed );
// A fallback to window.onload, that will always work
window.attachEvent( "onload", completed );
// If IE and not a frame
// continually check to see if the document is ready
var top = false;
try {
top = window.frameElement == null && document.documentElement;
} catch(e) {}
if ( top && top.doScroll ) {
(function doScrollCheck() {
if ( !jQuery.isReady ) {
try {
// Use the trick by Diego Perini
// http://javascript.nwbox.com/IEContentLoaded/
top.doScroll("left");
} catch(e) {
return setTimeout( doScrollCheck, 50 );
}
// detach all dom ready events
detach();
// and execute any waiting functions
jQuery.ready();
}
})();
}
}
}
return readyList.promise( obj );
};
jQuery.fn.ready = function( fn ) {
// Add the callback
jQuery.ready.promise().done( fn );
return this;
};
Also I have created the image below as a quick references for both:
A word of caution on using $(document).ready() with Internet Explorer. If an HTTP request is interrupted before the entire document is loaded (for example, while a page is streaming to the browser, another link is clicked) IE will trigger the $(document).ready event.
If any code within the $(document).ready() event references DOM objects, the potential exists for those objects to be not found, and Javascript errors can occur. Either guard your references to those objects, or defer code which references those objects to the window.load event.
I have not been able to reproduce this problem in other browsers (specifically, Chrome and Firefox)
Events
$(document).on('ready', handler) binds to the ready event from jQuery. The handler is called when the DOM is loaded. Assets like images maybe still are missing. It will never be called if the document is ready at the time of binding. jQuery uses the DOMContentLoaded-Event for that, emulating it if not available.
$(document).on('load', handler) is an event that will be fired once all resources are loaded from the server. Images are loaded now. While onload is a raw HTML event, ready is built by jQuery.
Functions
$(document).ready(handler) actually is a promise. The handler will be called immediately if document is ready at the time of calling. Otherwise it binds to the ready-Event.
Before jQuery 1.8, $(document).load(handler) existed as an alias to $(document).on('load',handler).
Further Reading
The timing
On the function ready
An example
Promises
The removed event alias
window.onload: A normal JavaScript event.
document.ready: A specific jQuery event when the entire HTML has been loaded.
One thing to remember (or should I say recall) is that you cannot stack onloads like you can with ready. In other words, jQuery magic allows multiple readys on the same page, but you can't do that with onload.
The last onload will overrule any previous onloads.
A nice way to deal with that is with a function apparently written by one Simon Willison and described in Using Multiple JavaScript Onload Functions.
function addLoadEvent(func) {
var oldonload = window.onload;
if (typeof window.onload != 'function') {
window.onload = func;
}
else {
window.onload = function() {
if (oldonload) {
oldonload();
}
func();
}
}
}
// Example use:
addLoadEvent(nameOfSomeFunctionToRunOnPageLoad);
addLoadEvent(function() {
/* More code to run on page load */
});
Document.ready (a jQuery event) will fire when all the elements are in place, and they can be referenced in the JavaScript code, but the content is not necessarily loaded. Document.ready executes when the HTML document is loaded.
$(document).ready(function() {
// Code to be executed
alert("Document is ready");
});
The window.load however will wait for the page to be fully loaded. This includes inner frames, images, etc.
$(window).load(function() {
//Fires when the page is loaded completely
alert("window is loaded");
});
The document.ready event occurs when the HTML document has been loaded, and the window.onload event occurs always later, when all content (images, etc) has been loaded.
You can use the document.ready event if you want to intervene "early" in the rendering process, without waiting for the images to load.
If you need the images (or any other "content") ready before your script "does something", you need to wait until window.onload.
For instance, if you are implementing a "Slide Show" pattern, and you need to perform calculations based on image sizes, you may want to wait until window.onload. Otherwise, you might experience some random problems, depending on how fast the images will get loaded. Your script would be running concurrently with the thread that loads images. If your script is long enough, or the server is fast enough, you may not notice a problem, if images happen to arrive in time. But the safest practice would be allowing for images to get loaded.
document.ready could be a nice event for you to show some "loading..." sign to users, and upon window.onload, you can complete any scripting that needed resources loaded, and then finally remove the "Loading..." sign.
Examples :-
// document ready events
$(document).ready(function(){
alert("document is ready..");
})
// using JQuery
$(function(){
alert("document is ready..");
})
// window on load event
function myFunction(){
alert("window is loaded..");
}
window.onload = myFunction;
Time flies, it's ECMAScript 2021 now and IE11 is used by people less and less. The most two events in contrast are load and DOMContentLoaded.
DOMContentLoaded fires after the initial HTML document has been completely loaded and parsed.
load fires after DOMContentLoaded and the whole page has loaded,
waiting for all dependent resources to finish loading. Example of resources: scripts, stylesheets, images and iframes etc.
IMPORTANT: Synchronous scripts will pause parsing of the DOM.
Both two events can be used to determine the DOM is able to use or not. For examples:
<script>
// DOM hasn't been completely parsed
document.body; // null
window.addEventListener('DOMContentLoaded', () => {
// Now safely manipulate DOM
document.querySelector('#id');
document.body.appendChild();
});
/**
* Should be used only to detect a fully-loaded page.
*
* If you just want to manipulate DOM safely, `DOMContentLoaded` is better.
*/
window.addEventListener('load', () => {
// Safely manipulate DOM too
document.links;
});
</script>
window.onload is a JavaScript inbuilt function. window.onload trigger when the HTML page loaded. window.onload can be written only once.
document.ready is a function of the jQuery library. document.ready triggers when HTML and all JavaScript code, CSS, and images which are included in the HTML file are completely loaded.
document.ready can be written multiple times according to requirements.
When you say $(document).ready(f), you tell script engine to do the following:
get the object document and push it, since it's not in local scope, it must do a hash table lookup to find where document is, fortunately document is globally bound so it is a single lookup.
find the object $ and select it, since it's not in local scope, it must do a hash table lookup, which may or may not have collisions.
find the object f in global scope, which is another hash table lookup, or push function object and initialize it.
call ready of selected object, which involves another hash table lookup into the selected object to find the method and invoke it.
done.
In the best case, this is 2 hash table lookups, but that's ignoring the heavy work done by jQuery, where $ is the kitchen sink of all possible inputs to jQuery, so another map is likely there to dispatch the query to correct handler.
Alternatively, you could do this:
window.onload = function() {...}
which will
find the object window in global scope, if the JavaScript is optimized, it will know that since window isn't changed, it has already the selected object, so nothing needs to be done.
function object is pushed on the operand stack.
check if onload is a property or not by doing a hash table lookup, since it is, it is called like a function.
In the best case, this costs a single hash table lookup, which is necessary because onload must be fetched.
Ideally, jQuery would compile their queries to strings that can be pasted to do what you wanted jQuery to do but without the runtime dispatching of jQuery. This way you have an option of either
do dynamic dispatch of jquery like we do today.
have jQuery compile your query to pure JavaScript string that can be passed to eval to do what you want.
copy the result of 2 directly into your code, and skip the cost of eval.
window.onload is provided by DOM api and it says " the load event fires when a given resource has loaded".
"The load event fires at the end of the document loading process. At this point, all of the objects in the document are in the DOM, and all the images, scripts, links and sub-frames have finished loading."
DOM onload
But in jQuery $(document).ready() will only run once the page Document Object Model (DOM) is ready for JavaScript code to execute. This does not include images, scripts, iframes etc. jquery ready event
So the jquery ready method will run earlier than the dom onload event.

Firefox DOMContentLoaded event problem

i've created a script that works with the Dom so it has to wait until the Dom is ready before execute every operation. I want that this script can be included in two ways:
In the head tag so that it is loaded before the Dom is ready. I've used document.addEventListener("DOMContentLoaded",function(){...},false) for this and it works well
Loaded dinamically when the Dom is already loaded. In this case the script doesn't work because for some reasons if the Dom is already loaded the DOMContentLoaded event isn't fired
So i want to know, is there a way to check if the Dom is already loaded so that the script can execute the code without using the DOMContentLoaded event?
PS: this must be an external script so i have no control on the page where it will be included
Ok, this is cheesy but it should work:
Wrap your external script in an init() method:
e.g. (and I'm just guessing here)
var myModule = {
init: function {
//all the code goes here
/..
}
}
Then put your script import back into the head tag and at the very bottom of your DOM markup add a script tag
<script>
myModule.init();
</script>
Again I wouldn't usually recommend it but if you need to avoid checking the DOM loaded event that's what I'd do
One of the easiest ways to do it would be to check if you can getElementById for something at the end of the page. Something like:
if (document.getElementById('footer')) { /* Dom is probably loaded */ }
else { /* Dom is probably NOT loaded */ }
But really, the best practice is probably to make sure that the code is always being called the same way. you could use an 'onload' event instead, as that will work in all browsers and is usually only a fraction of a second after the DOMContentLoaded event. But, I suppose that's really up to you.
Define your function:
function domReadyHandler() { /* ... */ }
Add the event listener:
document.addEventListener('DOMContentLoaded', domReadyHandler, false);
Execute the handler after dynamically loading your content, too:
function myXhrCallback() { /* ... */; domReadyHandler(); }
You could set a global var saying that the script is dynamically being included, Depending on this you can fire the load listener when the script is being eval'd.
window.SCRIPT_IS_DYNAMICALLY_LOADED = true;
< include script>
In script:
if (window.SCRIPT_IS_DYNAMICALLY_LOADED) {
domContentLoadedListener();
} else {
document.addEventListener("DOMContentLoaded",domContentLoadedListener, false);
}
Check the value for document.readyState. If the value is 'complete', you can execute the script, if it's something else, add the listener.

Categories

Resources