script When the browser loads HTML and comes across a <script>...</script> tag, it can’t continue building the DOM <script> in HTML executes before continuing building the DOM <script async> does not block DOMContentLoaded event <script> generated dynamically with document.createElement("script") and then added to the webpage does not block DOMContentLoaded event styles external style sheets don’t affect DOM, DOMContentLoaded does not wait for them but <script> waits for external CSS, DOMContentLoaded waits for CSS in case of a <script> <link type="text/css" rel="stylesheet" href="style.css"> <script> // the script doesn't not execute until the stylesheet is loaded alert(getComputedStyle(document.body).marginTop); </script> DOMContentLoaded event happens on the document object fully loaded HTML + DOM tree is built, but external resources <img> + css may not yet have loaded handler runs when the document is loaded, so it can see all the elements, including <img> but it doesn’t wait images to load we can apply JavaScript to elements at this stage waits for <script> <script async> does not block DOMContentLoaded event external style sheets don’t affect DOM loading, DOMContentLoaded does not wait for them autofill forms are triggered on DOMContentLoaded, if DOMContentLoaded is postponed by long-loading scripts, then autofill also awaits document.addEventListener("DOMContentLoaded", func) // not "document.onDOMContentLoaded = func" function func() { alert('DOM is ready'); // image is not yet loaded (unless it was cached), so the size is 0x0 alert(`Image size: ${img.offsetWidth}x${img.offsetHeight}`); } onload event on the window object triggers when the whole page and resources are loaded including styles, images and other resources Message will show correct sizes... window.addEventListener("load", () => { alert('Page is fully loaded'); alert(`Image size: ${img.offsetWidth}x${img.offsetHeight}`); }) beforeunload triggers before the page leave If we cancel the event, browser asks whether the user really wants to leave (e.g we have unsaved changes) get msg Changes you made may not be saved we can not customize it coz webmasters abused this event handler by showing annoying message in the past window.onbeforeunload = function() { return "There are unsaved changes. Leave now?" } // browser shows 'Changes you made may not be saved.' instead of our text Same result window.onbeforeunload = function() { return false } onunload When a visitor finally leaving the page, the 'unload' event triggers on window Naturally, 'unload' event is when the user leaves us, and we’d like to save the data on our server in the handler we can only do simple things that do not involve delays or asking a user There exists a special navigator.sendBeacon(url, data) method for such needs It sends the data in background. The transition to another page is not delayed: the browser leaves the page, but still performs sendBeacon The request is sent as POST request We can send not only a string, but also forms and other formats The data is limited by 64kb const analyticsData = { /* object with gathered data */ } window.addEventListener("unload", function() { navigator.sendBeacon("/analytics", JSON.stringify(analyticsData)) }) readyState document.readyState property tells us about the current loading state "loading" the document is loading "interactive" the document was fully read "complete" the document was fully read and all resources (like images) are loaded too function work() { /*...*/ } if (document.readyState == 'loading') { document.addEventListener('DOMContentLoaded', work) // still loading, wait for the event } else { work() // DOM is ready! } readystatechange event rarely used triggers when the state changes Print state changes... document.addEventListener('readystatechange', () => console.log(document.readyState)) Events flow readyState:loading readyState:interactive DOMContentLoaded iframe onload img onload readyState:complete window onload document.readyState becomes interactive right before DOMContentLoaded , they are same things document.readyState becomes complete when all resources (iframe and img) are loaded it happens in about the same time as img.onload and window.onload switching to complete state means the same as window.onload difference is that window.onload always works after all other load handlers scripts: async, defer, dynamic When the browser hits <script> tag, it can’t continue building the DOM It must execute the script right now same happens for external scripts <script src="..."> Scripts can’t see DOM elements below them bulky script at the top “blocks the page” till script downloads and executes :( for long HTML documents, that may be a noticeable delay we can put a script at the bottom and it doesn’t block the page content from showing "defer", "async" script attributes that solve the problem, they do not block page rendering defer The defer attribute tells the browser not to wait for the script The script loads “in the background”, and then runs when the DOM is fully built Scripts with defer never block the page Scripts with defer execute when the DOM is ready (but before DOMContentLoaded event) DOMContentLoaded event handler waits for the deferred script Deferred scripts execution keep their relative order, even they are downloaded in parallel That's important if we need to load a JavaScript library and some script depends on it The defer attribute is only for external scripts defer attribute is ignored if <script> tag has no src attribute async async attribute is somewhat like defer The async attribute means that a script is completely independent async scripts load in the background and run when ready browser doesn’t block on async scripts (like defer) page content shows up immediately Other scripts don’t wait for async scripts, and async scripts don’t wait for them DOMContentLoaded and async scripts don’t wait for each other DOMContentLoaded may happen both before & after an async script Async scripts are great when we integrate an independent third-party script into the page like counters, ads and so on, where our scripts shouldn’t wait for them dynamic we can create a script and append it to the document dynamically using JavaScript dynamic scripts behave as async by default They don’t wait for anything, nothing waits for them The script that loads first – runs first script.async = false scripts will be executed in the document order, just like defer const script = document.createElement('script') script.src = "/article/script-async-defer/long.js" document.body.append(script) In practice defer is used for scripts that need the whole DOM and/or their relative execution order is important async is used for independent scripts, like counters or ads By using defer or async you let a user see the the page before the script loads Don’t forget to put “loading” indication and disable buttons that aren’t functional yet. load & error may track the loading of external resources – scripts, iframes, pictures and so on. triggers after the script was loaded and executed For our own scripts we could use JavaScript 'modules' here but for external Errors that occur during the loading of the script can be tracked in an error event Events onload/onerror track only the loading itself. To track script errors, window.onerror global handler can be used Most resources start loading when they are added to the document. But <img> is an exception. It starts loading when it gets a src For <iframe>, the iframe.onload event triggers when the iframe loading finished both for successful load and in case of an error, that’s for historical reasons let img = document.createElement('img') img.src = "https://js.cx/clipart/train.gif" // img.onload = function() { alert(`Image loaded, size ${img.width}x${img.height}`) } img.onerror = function() { alert("Error occurred while loading image") } crossorigin scripts from one site can’t access contents of the other site actually, one origin (domain/port/protocol triplet) can’t access the content from another one. if we have a subdomain, or just another port, these are different origins with no access to each other we can’t get error details for a script from another domain To allow cross-origin access, the <script> needs to have the "crossorigin" attribute & remote server must provide special headers There are three levels of cross-origin access: No crossorigin attribute – access prohibited crossorigin="anonymous" – access allowed if the server responds with the header 'Access-Control-Allow-Origin' with * or our origin crossorigin="use-credentials" – access allowed if the server sends back the header 'Access-Control-Allow-Origin' with our origin and Access-Control-Allow-Credentials: true . Browser sends authorization information and cookies to remote server.