Turning Learners Into Developers
Codekilla
CODEKILLA
back to course
Lesson 04 / 1153%· free preview
Basics4/13

JavaScript Where To

External vs internal <script>, where to place them, and why <script defer> wins.

Definition

JavaScript runs in HTML pages via the <script> tag. There are exactly three ways to attach JS to a page:

  1. External file<script src="app.js"></script> (production-grade).
  2. Internal block<script>...</script> (small demos, inline tutorials).
  3. Inline event handler<button onclick="..."> (legacy; avoid in new code).

Production rule: ship JS as external files with the defer attribute. Browsers cache external files; defer keeps the page interactive while the script downloads.

Choose a Placement
WhereWhen it runsProsCons
<head> (no attributes)Blocks HTML parsing until script downloads + executesSimpleSlow page load
<head> + deferDownloads in parallel, executes after HTML parsesBest for app codeDOM-ready guaranteed
<head> + asyncDownloads in parallel, executes ASAP (out-of-order)Best for analyticsCan run before DOM exists
Bottom of <body>Executes after the HTML above itDOM is already parsedSlower to start downloading
External Script with `defer` — Walkthrough

Step 1. Create app.js:

javascript
document.getElementById("demo").innerHTML = "Loaded from app.js!";
console.log("app.js executed");

Step 2. Create index.html and link the external file with defer:

html
<!DOCTYPE html>
<html>
<head>
  <title>Where To Demo</title>
  <script src="app.js" defer></script>
</head>
<body>
  <p id="demo">Loading…</p>
</body>
</html>

Step 3. Open index.html in a browser.

Loaded from app.js!

And in DevTools console:

app.js executed
What Just Happened?
  • The browser hit <script src="app.js" defer> in <head>.
  • Because of defer, it began downloading app.js in parallel with HTML parsing.
  • It finished parsing the rest of the page (including <p id="demo">).
  • Once the DOM was ready and app.js had downloaded, it executed — and getElementById found the <p> because the DOM was complete.

Without defer, the browser would have paused parsing at the <script> tag, blocking everything below it.

defer vs async — When to Use Which
AttributeOrder preserved?Waits for DOM?Use for
defer✅ Yes✅ Yes (executes before DOMContentLoaded)App code, libraries
async❌ No (whichever finishes first)❌ NoIndependent scripts (analytics, ads)
neither✅ Yes❌ No (blocks parsing)Avoid in modern code
html
<!-- App code: defer keeps execution order -->
<script src="vendor.js" defer></script>
<script src="app.js"   defer></script>

<!-- Analytics: async ships fastest -->
<script src="https://www.googletagmanager.com/gtag/js" async></script>
Multiple Scripts — Order Rules

With defer, scripts execute in the order they appear in HTML. With async, order is whichever downloads first. If your app.js depends on vendor.js, you must use defer (or ESM import).

ES Modules — The Modern Way

Modern apps use <script type="module">:

html
<script type="module" src="main.js"></script>

Modules are:

  • Always deferred (no need for the defer attribute).
  • CORS-restricted when loaded from file:// — open via a local dev server.
  • Allow import / export syntax across files.
Online Compiler — Skip Setup

Don't want to wire up an HTML file? Use Codekilla's online JavaScript compiler — paste your code, click Run, see output.

Common Mistakes
  • <script> in <head> without defer — blocks page rendering until the script downloads. Slow first paint.
  • Targeting getElementById from a head-script without defer — DOM doesn't exist yet, returns null.
  • Mixing async and defer on dependent scriptsasync runs out of order, breaking dependencies.
  • Inline onclick in production — clutters HTML, breaks Content-Security-Policy. Use addEventListener instead.
  • Forgetting the .js extension in <script src="..."> — most servers serve it correctly anyway, but tooling complains.
Interview Questions

Practice Exercises
  1. External move — Take a page with an inline <script>...</script> block and move the code into app.js. Add defer and confirm it still works. Hint: link with <script src="app.js" defer></script>.
  2. Order test — Create three scripts that each console.log their name. Reload with defer then async — observe console order. Hint: defer keeps insertion order; async is a race.
  3. DOM-ready demo — Place a <script> in <head> (no defer) that tries to read <p id="demo">. Observe the null. Add defer and watch it work. Hint: console.log the result of getElementById("demo").

💡 Think Like a Programmer: Default to <script src="app.js" defer></script> in <head>. It's the modern, fastest, most predictable placement. Save async for independent third-party tags (analytics, ads).

AI-powered recap

Quick recap quiz?

We'll generate 5 MCQs from this lesson and check your understanding instantly. Takes ~30 seconds.

Ready to move on?
// example library
Want more hands-on snippets in JavaScript?
Browse 2 runnable examples · across 1 chapter · short, copy-paste-friendly · grouped by topic
Explore examples
// side-by-side reference
See this in other languages
Compare the same concept across C, C++, Java, and Python — one table, zero tab-switching.
Compare Languages
// feedback.matters()
Did this lesson help you?