HTML JavaScript
html javascript script elements orchestrate when javascript runs against dom construction. produ javascript in html — via <script> — controls
Introduction
JavaScript in HTML — via <script> — controls when code runs relative to DOM construction. Stripe Dashboard loads a minimal inline bootstrap then ES modules; Google Search uses strict CSP with nonce scripts. Staff engineers treat script placement as a performance and security contract — parser-blocking legacy patterns still ship in third-party snippets unless gated.
Business problem
Script placement mistakes block first paint, break checkout in webviews, and violate CSP on deploy. Amazon product pages carry dozens of vendor tags — each sync script in head competes for main thread with image decode.
- Conversion: Shopify measured 300ms parser delay from misplaced analytics — measurable cart abandonment on mobile.
- Security: Inline script without nonce is XSS amplifier; compromised CMS injects script via template.
- Compliance: PCI scope expands when payment pages load unreviewed third-party script in same document.
Why this feature exists
Hypertext needed executable logic before modules and bundlers. Script element let documents trigger computation during parse — powerful but dangerous; defer/async/module attributes evolved to fix ordering.
- History: Netscape script tag 1995; document.write for sync injection; HTML5 added defer, async, type=module.
- Rejected: javascript: URLs in href for app logic — accessibility and CSP failures.
- Modern role: Vite/Webpack emit script tags; SSR frameworks inject hydration bundles with known order.
Browser internals
Script processing pauses HTML parser for classic scripts without defer/async until download and execute complete. Module scripts defer by default; nomodule provides legacy fallback. Dynamic import() inside modules is async.
- Parser blocking: Sync script in body mid-content freezes DOM construction below insertion point.
- Execution: Microtasks from script run before next parser chunk — ordering bugs in init code.
- CSP: script-src nonce/hash required for inline; eval blocked unless unsafe-eval.
Classic <script src="a.js"> → fetch → execute → resume parse<script defer> → fetch parallel, execute after parse complete<script type="module"> → defer + strict mode + CORS for cross-origin
Rendering workflow
Scripts in head without defer delay body parse start if sync. Body-end scripts still block render of content below. Airbnb moved critical path scripts to module + defer — LCP improved when hero HTML parsed first.
- FCP: First paint waits for CSS unless script blocks earlier — profile with Performance panel.
- INP: Long sync execute during hydration spikes interaction delay.
- Preload: link rel=preload as=script prioritizes fetch without executing early.
Feature deep dive
Loading strategy matrix: defer for app bundles needing full DOM; async for independent analytics; type=module for modern apps; avoid inline except tiny bootstrap or nonce CSP. Never document.write in 2025 production.
- Head: Only defer/module/preload hints — not sync vendor tags.
- Body end: Legacy sync fallback when CSP allows — prefer defer in head.
- Import maps: script type=importmap for bare specifier resolution in browsers.
<script type="importmap">{ "imports": { "stripe": "https://js.stripe.com/v3/" } }</script><script type="module" src="/checkout.js" defer></script><script nomodule src="/legacy-bundle.js" defer></script>
Accessibility analysis
Script-driven UI must not replace semantic HTML without ARIA parity. BBC avoids client-only navigation labels — SSR provides real links. Focus management after script route change is mandatory for SPA shells.
- Progressive enhancement: Core content readable without script — GOV.UK baseline.
- Live regions: Script updates need aria-live for async status — not alert().
- Motion: Script-triggered animation respects prefers-reduced-motion.
SEO impact
Googlebot executes JavaScript but first-wave crawl favors HTML in initial response. Airbnb SSR listing content; client script enhances filters. Pure client render of h1/title delays indexing.
- Rendering: Search Console URL Inspection shows rendered vs raw HTML diff.
- Lazy content: Infinite scroll via script may miss deep links without paginated fallbacks.
- Structured data: JSON-LD in script type=application/ld+json — valid pattern, separate from app JS.
Security considerations
Every script tag is trust expansion. SRI integrity on CDN scripts; CSP script-src with nonces; Subresource Integrity on Stripe.js. Third-party script gets same-origin cookie access unless SameSite partitioned.
- XSS: Inline onclick and javascript: URLs — ban in style guide.
- Supply chain: Compromised npm CDN — pin integrity hashes.
- Magecart: Monitor script src changes in CI on template deploy.
Performance impact
Amazon PDP caps synchronous third-party scripts in critical path — tags load after load event or via tag manager with consent gate. Module splitting reduces parse+compile cost vs monolith IIFE.
- LCP: Don't inject hero image swap via sync head script.
- INP: Code-split route bundles; defer non-critical widgets.
- Memory: Multiple framework versions from tag sprawl — audit duplicate React loads.
Real production example
Stripe Checkout host page — minimal HTML shell, Stripe.js loaded with SRI, application logic in module defer. No inline secrets; publishable key in data attribute encoded.
- Pattern: One module entry, dynamic import for heavy admin panels.
- Telemetry: RUM tracks long tasks correlated with script URL.
- Feature flags: Server decides which script bundles to include — not client document.write.
<script src="https://js.stripe.com/v3/" crossorigin integrity="sha384-…" defer></script><script type="module" src="/assets/checkout.mjs"></script>
Enterprise usage
Google internal sites enforce CSP strict-dynamic with nonces generated per request. Shopify theme-check warns on script without defer in theme.liquid. Script inventory spreadsheet reviewed quarterly.
- CI: eslint ban on eval, new Function, inline script in JSX except nonce wrapper.
- Tag manager: Single injection point — not per-team head snippets.
- Consent: GDPR blocks marketing scripts until opt-in — HTML placeholder only.
Common production failures
Black Friday deploy moved analytics script above CSS without defer — global LCP regression 1.8s; rollback removed sync tag. Postmortem mandated script lint in CI.
- CSP outage: New inline bootstrap without nonce — checkout blank 90 minutes.
- Duplicate jQuery: Three versions from plugins — INP p95 doubled on category pages.
- SEO: Client-only title update — Google indexed default title for two weeks.
Architecture review questions
- Are all non-critical scripts defer or async — none parser-blocking in head?
- Does CSP script-src allow every script src on this page?
- Is core content visible in HTML without executing JavaScript?
- Do CDN scripts use integrity attributes where supported?
- What is total script bytes and long-task count on 4G throttling?
- Are third-party scripts inventoried with owner and rollback plan?
Hands-on project
Refactor a landing page script load order — move to defer modules, add CSP nonce pipeline, measure LCP/INP before and after, document third-party script registry.
- Deliverable: Performance trace screenshot + CSP header diff.
- Verify: Checkout works with JS enabled; core text visible with JS off.
- Stretch: Import maps for one bare specifier dependency.
Interview questions
Compare defer, async, and type=module script loading semantics.(Advanced)
Sync classic blocks parse until execute. defer: fetch parallel, execute after document parsed, order preserved. async: fetch parallel, execute when ready, order not guaranteed. module: defer by default, strict mode, defer until DOM ready, supports static/dynamic import.
Follow-up: When is async appropriate for analytics?
How would you roll out strict CSP on a Shopify merchant theme with legacy inline scripts?(Advanced)
Inventory inline and eval usage; extract to external files; add nonces via server Liquid; use strict-dynamic for trusted entry script; hash small inline config blocks; staged rollout with Report-Only CSP collecting violations; theme-check gate on new inline.
Follow-up: What breaks Stripe embed patterns?
SEO implications of client-rendered main content via script after empty body?(Advanced)
Google may render JS but delayed content risks soft indexing, unstable snippets, and crawl budget waste. Staff SSR or prerender critical text, links, and JSON-LD. Compare raw vs rendered HTML in Search Console; paginate for infinite lists.
Follow-up: Does Google always execute JS?
Try it yourself
Edit the HTML, CSS, or JS panels — the preview updates as you type.
Try it yourself
Summary
Script elements orchestrate when JavaScript runs against DOM construction. Production teams at Stripe and Google enforce defer/module patterns, strict CSP, and SSR-visible content — parser-blocking inline script is technical debt with measurable CWV and security cost.