← Back to Chapters

HTML Script Tag

? HTML <script> Tag

⚡ Quick Overview

The <script> tag is how HTML pages load or embed JavaScript. Scripts can live inline in the page, inside a <script> block, or in separate .js files loaded with the src attribute. Modern best practice is to keep JavaScript in external files and attach behavior using addEventListener instead of inline attributes like onclick.

Script loading strategy matters for performance and correctness. Attributes like defer, async, and type="module" control when and how your JavaScript runs relative to HTML parsing.

? Key Concepts

  • Inline JavaScript: Code directly inside HTML tags (e.g., <button onclick="...">). Simple but not recommended.
  • Internal scripts: JavaScript inside a <script> block in the HTML page.
  • External scripts: Separate .js files loaded with src="/path/app.js".
  • defer: Downloads script in parallel, executes it after HTML parsing, preserves order. Great default.
  • async: Downloads and runs as soon as possible; execution order is not guaranteed. Best for independent scripts (analytics/ads).
  • Modules: type="module" enables ES modules, scoped variables, import, and behaves like defer.
  • Fallbacks: nomodule lets you serve legacy bundles to older browsers that don’t support modules.

? Syntax & Loading Strategies

Common patterns for including JavaScript on a page:

? View Script Loading Examples
<!-- 1. External script with defer (preferred in <head>) -->
<head>
  <script src="/assets/app.js" defer></script>
</head>

<!-- 2. External script with async (fire when loaded; order not guaranteed) -->
<script src="/ads.js" async></script>

<!-- 3. Internal script at end of body -->
<body>
  ...
  <script>
    document.addEventListener('DOMContentLoaded', () => {
      const btn = document.getElementById('greet');
      btn.addEventListener('click', () => alert('Hello!'));
    });
  </script>
</body>
? View Module & CDN Examples
<!-- 4. Module with legacy fallback -->
<script type="module">
  // Module code is scoped (no globals). Imports allowed if you have separate files.
  const sum = (a, b) => a + b;
  window.__sum = sum; // expose only if you explicitly assign to window
</script>
<script nomodule src="/legacy-bundle.js"></script>

<!-- 5. CDN with integrity -->
<script src="https://cdn.example.com/lib.min.js"
        integrity="sha384-..." crossorigin="anonymous" defer></script>

? Live Output / Explanation

?️ Interactive Demo

These buttons are wired up using addEventListener from regular scripts and modules. There are no inline onclick attributes in the HTML.

Ready.

? Tips & Best Practices

  • Prefer defer in <head> for scripts that depend on the DOM. It preserves execution order and doesn’t block HTML parsing.
  • Use async only for independent scripts that don’t rely on others (analytics, ads).
  • Use modules: type="module" uses strict mode, scopes variables, and supports import. Globals only appear if you attach them to window.
  • Avoid inline handlers: instead of onclick in HTML, attach listeners via JavaScript for cleaner markup and CSP compatibility.
  • Load from CDNs safely: add integrity + crossorigin for Subresource Integrity checks.
  • Keep scripts light in <head>: large blocking scripts without defer can slow down first paint.
  • Be careful with async chains: don’t make async scripts depend on each other because execution order isn’t guaranteed.
  • Handle errors: when dynamically loading modules or remote scripts, consider load/error events and try...catch around imports.

? Try It Yourself

  • Move a script from the bottom of <body> into <head> with defer and confirm the behavior is identical.
  • Replace an inline onclick with a separate script that uses document.getElementById(...).addEventListener('click', ...).
  • Add a small type="module" block that uses a dynamic import() when a button is clicked, and log something only after the module loads.
  • Load a third-party script from a CDN with integrity and crossorigin, and wire up script.addEventListener('load'/'error') to log success or failure.

? Summary

  • The <script> tag loads or embeds JavaScript, either inline, internal, or external.
  • defer is usually the best choice for page scripts that depend on the DOM and each other.
  • async suits independent scripts where load order doesn’t matter.
  • type="module" enables ES module syntax, scoped code, and modern loading behavior.
  • Avoid inline event handlers; attach behavior using addEventListener instead.
  • Good loading strategy improves performance, maintainability, and security of your web applications.