HTML JavaScript not working

Open the console before changing anything. In most files the script did run and threw on the first line, and the error names the cause.

When HTML JavaScript is not working, open the browser console before you change a line of code. In most files the script did run, threw on its first statement, and stopped.

The browser console with one red line at the top. Everything below it is a consequence.
The browser console with one red line at the top. Everything below it is a consequence.

Press F12, or right click and choose Inspect, then open the Console tab. Reload the page with the console open so you catch errors thrown during load.

Read the first red line only. One thrown error halts the rest of that script, so later messages usually describe symptoms of the first one.

The causes, in the order they actually occur

Symptom in the console Cause Fix
Cannot read properties of null Script ran before the element existed Move the script to the end of <body>, or add defer
X is not defined Files loaded in the wrong order, or a typo Load the dependency first, check the spelling
Cross origin requests are only supported for... Page opened from file:// Serve the page from an address
Refused to execute inline script A Content Security Policy is in force Move the code to a file the policy allows
404 on a .js file The file is not where the path says Fix the path, or inline the script
Nothing at all in the console The script never loaded Add a console.log on line one

The last row is the one people skip. An empty console does not mean the code is fine, it often means the browser never fetched the file.

Script order: the most common cause by a wide margin

A browser reads the document top to bottom. A script in the <head> runs while the body is still empty.

So document.getElementById('chart') returns null, and the next line that touches it throws. The code is correct and the timing is not.

Two fixes, both one word:

  1. Move the script tag to just before </body>. By then every element exists.
  2. Add defer to the tag. <script src="app.js" defer></script> keeps the tag in the head but delays execution until the document is parsed.

defer only applies to scripts with a src. For inline code in the head, wrap it in a DOMContentLoaded listener or move the tag.

<script>
  document.addEventListener('DOMContentLoaded', function () {
    document.getElementById('chart').textContent = 'ready';
  });
</script>

The script tag that silently ignores your code

This one produces no error at all:

<script src="chart.js">
  startChart();
</script>

When a <script> carries a src, the content between the tags is discarded by specification. startChart() never runs and nothing is logged.

Split it into two tags. One with the src, one with the inline call.

When the address bar starts with file://

Double clicking an HTML file opens it over the file protocol. Browsers treat each local file as its own origin with almost no privileges.

The address bar showing a file:/// path. Several JavaScript features are unavailable at this address.
The address bar showing a file:/// path. Several JavaScript features are unavailable at this address.

What stops working at file://:

  • fetch() and XMLHttpRequest against local files, blocked by CORS rules.
  • ES modules, meaning <script type="module"> and every import inside it.
  • canvas.getImageData() after drawing a local image, because the canvas is marked tainted.
  • Service workers and some storage APIs.

None of this is fixed by editing the code. The page needs an address. Dropping the file into the HTML file opener tells you quickly whether the protocol was the problem, because the same file behaves differently there.

Separate .js files that did not travel

If the page works on your machine and does nothing for the person you sent it to, the script was probably a neighbouring file.

<script src="app.js"></script> is a relative path. It resolves against wherever the HTML happens to sit.

Send only the HTML and the browser asks for an app.js that is not there. You see a 404 in the network tab and an otherwise dead page.

Fold the code into the HTML itself. A self-contained file has one moving part instead of three, and behaves the same wherever it lands.

Blocked rather than broken

Sometimes the code is fine, present, and refused. The console says Refused to execute inline script because it violates the following Content Security Policy directive.

That is a Content Security Policy header set by whatever is serving the page. It is a deliberate restriction, not a bug in your file.

The same refusal appears inside a locked down iframe. A frame carrying sandbox without allow-scripts will render your markup and run none of your code. The sandbox attribute lists what each token permits.

The network tab showing a 404 for a .js file the page asked for.
The network tab showing a 404 for a .js file the page asked for.

A test that separates the two questions

Two things can be wrong: the code, or the environment it is running in. Test them apart.

Make a file with nothing in it but this:

<!doctype html>
<html>
  <body>
    <p id="out">not yet</p>
    <script>
      document.getElementById('out').textContent = 'JavaScript runs here';
    </script>
  </body>
</html>

If that text changes, JavaScript is enabled and executing, and your problem is in your own code or its ordering. If it does not, the environment is blocking scripts and no amount of rewriting will help.

Paste your real page into an online HTML editor for the same comparison with the full file. It runs the page at a normal https address, so protocol restrictions drop away and only genuine code errors remain.

The same page running from an address. Script features that file:// blocks are available again.
The same page running from an address. Script features that file:// blocks are available again.

After it works, keep it working

Once the script runs, the thing that breaks it next is distribution. Attachments arrive without their sibling files, and mail gateways are hostile to .html.

Paste the finished page into a NOS document and it renders as written, scripts included, at an address of its own. Turning HTML into a link is that step, and the link survives your later edits.

Questions people ask

Why does my JavaScript work in CodePen but not in my HTML file?

CodePen inserts your script after the page has been built and wraps it for you. A plain file runs the script exactly where you put it. If the script sits in the head, it runs before the elements exist, so every getElementById returns null.

Why does the console say "Cross origin requests are only supported for protocol schemes http, https"?

You opened the file by double clicking it, so the address starts with file://. Browsers treat every file on disk as a separate origin, which blocks fetch, module imports and canvas image reads. Serving the page from an address removes the restriction.

My script tag has both src and inline code. Why is the inline code ignored?

That is the rule. When a script tag carries a src attribute, anything written between the tags is discarded. Use two separate script tags.

The page works for me but not for the person I sent it to. What changed?

Usually the script was a separate .js file next to the HTML, and only the HTML travelled. Inline the script, or paste the page into a document that serves it from one address.

Keep reading

document.getElementById: get one element and change itHow document.getElementById works: read and set .value, .textContent, .style and .classList,querySelector and querySelectorAll: find elements with CSS selectorsFind elements with CSS selectors in JavaScript: by id, class, attribute or state. Try selectThe file:// protocol: what works differently when a page is opened from diskfile:// is the address for a page opened from disk. It is not on the web: only this machine Content Security Policy: limiting what your own page may loadA Content Security Policy lists where a page may load scripts, styles, images and frames froSelf-contained HTML file: one file that works anywhereA self-contained HTML file carries its styles, scripts and small images inside itself and poCSS animation not workingKeyframes defined and nothing moves. Name mismatch, missing duration, a non animatable propeNot allowed to load local resourceThe browser refuses a file:// image or script inside a page. Why the rule exists, why disablHow to open HTML in a text editorRight click, Open with, pick a plain text editor. The per-platform route, which editors are How to open HTML on Safari iPhoneSafari on iPhone opens addresses, not folders. How to get local HTML into a Safari tab, and Run Svelte in one HTML fileSvelte is a compiler, so there is no one-line script tag. Run a Svelte 5 component in one HTTypeScript in HTML: test HTML, CSS, and typed code in one fileSee how HTML, CSS, and TypeScript work in one file, test the live result and its empty-inputMaterial UI in a single HTML fileRun Material UI in one HTML file with no build step: an import map, React from jsDelivr and Run WebAssembly in a single HTML filePut the .wasm bytes inside the page and WebAssembly runs from a single HTML file, with no feMathJax in an HTML file: LaTeX formulas with one script tagAdd MathJax to a single HTML file with one script tag. A working example, the delimiters it A JSX example in React, in one HTML fileA JSX example you can run from one HTML file: React and Babel from a CDN, a live counter, thesbuild and HTML: from source files to one working pageesbuild does not read HTML, it bundles JavaScript and CSS. See a working esbuild call insidelit-html in one HTML file: import, render, updateUse lit-html 3.2.1 in a single HTML file: a pinned CDN import, live examples with render() aUse Babel in a single HTML fileBabel is a JavaScript compiler. Load it from a CDN and a text/babel script tag turns JSX andBrython in HTML: run Python from one fileBrython runs Python inside an HTML page. Copy a one-file example, see the three parts it nee